mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-25 22:52:01 +00:00
Merge branch 'v2.11' into stable
This commit is contained in:
commit
0dcf6f289d
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@ -235,8 +235,8 @@ directory inside your pybind11 git clone. Files will be modified in place,
|
|||||||
so you can use git to monitor the changes.
|
so you can use git to monitor the changes.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run --rm -v $PWD:/mounted_pybind11 -it silkeh/clang:13
|
docker run --rm -v $PWD:/mounted_pybind11 -it silkeh/clang:15-bullseye
|
||||||
apt-get update && apt-get install -y python3-dev python3-pytest
|
apt-get update && apt-get install -y git python3-dev python3-pytest
|
||||||
cmake -S /mounted_pybind11/ -B build -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--use-color" -DDOWNLOAD_EIGEN=ON -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_STANDARD=17
|
cmake -S /mounted_pybind11/ -B build -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--use-color" -DDOWNLOAD_EIGEN=ON -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_STANDARD=17
|
||||||
cmake --build build -j 2
|
cmake --build build -j 2
|
||||||
```
|
```
|
||||||
|
88
.github/workflows/ci.yml
vendored
88
.github/workflows/ci.yml
vendored
@ -9,11 +9,14 @@ on:
|
|||||||
- stable
|
- stable
|
||||||
- v*
|
- v*
|
||||||
|
|
||||||
|
permissions: read-all
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: test-${{ github.ref }}
|
group: test-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
PIP_BREAK_SYSTEM_PACKAGES: 1
|
||||||
PIP_ONLY_BINARY: numpy
|
PIP_ONLY_BINARY: numpy
|
||||||
FORCE_COLOR: 3
|
FORCE_COLOR: 3
|
||||||
PYTEST_TIMEOUT: 300
|
PYTEST_TIMEOUT: 300
|
||||||
@ -33,9 +36,10 @@ jobs:
|
|||||||
- '3.9'
|
- '3.9'
|
||||||
- '3.10'
|
- '3.10'
|
||||||
- '3.11'
|
- '3.11'
|
||||||
- 'pypy-3.7'
|
- '3.12'
|
||||||
- 'pypy-3.8'
|
- 'pypy-3.8'
|
||||||
- 'pypy-3.9'
|
- 'pypy-3.9'
|
||||||
|
- 'pypy-3.10'
|
||||||
|
|
||||||
# Items in here will either be added to the build matrix (if not
|
# Items in here will either be added to the build matrix (if not
|
||||||
# present), or add new keys to an existing matrix element if all the
|
# present), or add new keys to an existing matrix element if all the
|
||||||
@ -71,6 +75,7 @@ jobs:
|
|||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python }}
|
python-version: ${{ matrix.python }}
|
||||||
|
allow-prereleases: true
|
||||||
|
|
||||||
- name: Setup Boost (Linux)
|
- name: Setup Boost (Linux)
|
||||||
# Can't use boost + define _
|
# Can't use boost + define _
|
||||||
@ -82,7 +87,7 @@ jobs:
|
|||||||
run: brew install boost
|
run: brew install boost
|
||||||
|
|
||||||
- name: Update CMake
|
- name: Update CMake
|
||||||
uses: jwlawson/actions-setup-cmake@v1.13
|
uses: jwlawson/actions-setup-cmake@v1.14
|
||||||
|
|
||||||
- name: Cache wheels
|
- name: Cache wheels
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
@ -164,7 +169,6 @@ jobs:
|
|||||||
-DDOWNLOAD_EIGEN=ON
|
-DDOWNLOAD_EIGEN=ON
|
||||||
-DCMAKE_CXX_STANDARD=17
|
-DCMAKE_CXX_STANDARD=17
|
||||||
-DPYBIND11_INTERNALS_VERSION=10000000
|
-DPYBIND11_INTERNALS_VERSION=10000000
|
||||||
"-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp"
|
|
||||||
${{ matrix.args }}
|
${{ matrix.args }}
|
||||||
|
|
||||||
- name: Build (unstable ABI)
|
- name: Build (unstable ABI)
|
||||||
@ -179,7 +183,9 @@ jobs:
|
|||||||
# This makes sure the setup_helpers module can build packages using
|
# This makes sure the setup_helpers module can build packages using
|
||||||
# setuptools
|
# setuptools
|
||||||
- name: Setuptools helpers test
|
- name: Setuptools helpers test
|
||||||
run: pytest tests/extra_setuptools
|
run: |
|
||||||
|
pip install setuptools
|
||||||
|
pytest tests/extra_setuptools
|
||||||
if: "!(matrix.runs-on == 'windows-2022')"
|
if: "!(matrix.runs-on == 'windows-2022')"
|
||||||
|
|
||||||
|
|
||||||
@ -202,13 +208,13 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Python ${{ matrix.python-version }} (deadsnakes)
|
- name: Setup Python ${{ matrix.python-version }} (deadsnakes)
|
||||||
uses: deadsnakes/action@v3.0.0
|
uses: deadsnakes/action@v3.0.1
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
debug: ${{ matrix.python-debug }}
|
debug: ${{ matrix.python-debug }}
|
||||||
|
|
||||||
- name: Update CMake
|
- name: Update CMake
|
||||||
uses: jwlawson/actions-setup-cmake@v1.13
|
uses: jwlawson/actions-setup-cmake@v1.14
|
||||||
|
|
||||||
- name: Valgrind cache
|
- name: Valgrind cache
|
||||||
if: matrix.valgrind
|
if: matrix.valgrind
|
||||||
@ -241,8 +247,6 @@ jobs:
|
|||||||
python -m pip install -r tests/requirements.txt
|
python -m pip install -r tests/requirements.txt
|
||||||
|
|
||||||
- name: Configure
|
- name: Configure
|
||||||
env:
|
|
||||||
SETUPTOOLS_USE_DISTUTILS: stdlib
|
|
||||||
run: >
|
run: >
|
||||||
cmake -S . -B build
|
cmake -S . -B build
|
||||||
-DCMAKE_BUILD_TYPE=Debug
|
-DCMAKE_BUILD_TYPE=Debug
|
||||||
@ -333,8 +337,8 @@ jobs:
|
|||||||
# Testing NVCC; forces sources to behave like .cu files
|
# Testing NVCC; forces sources to behave like .cu files
|
||||||
cuda:
|
cuda:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: "🐍 3.10 • CUDA 11.7 • Ubuntu 22.04"
|
name: "🐍 3.10 • CUDA 12.2 • Ubuntu 22.04"
|
||||||
container: nvidia/cuda:11.7.0-devel-ubuntu22.04
|
container: nvidia/cuda:12.2.0-devel-ubuntu22.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@ -399,6 +403,7 @@ jobs:
|
|||||||
|
|
||||||
# Testing on CentOS 7 + PGI compilers, which seems to require more workarounds
|
# Testing on CentOS 7 + PGI compilers, which seems to require more workarounds
|
||||||
centos-nvhpc7:
|
centos-nvhpc7:
|
||||||
|
if: ${{ false }} # JOB DISABLED (NEEDS WORK): https://github.com/pybind/pybind11/issues/4690
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: "🐍 3 • CentOS7 / PGI 22.9 • x64"
|
name: "🐍 3 • CentOS7 / PGI 22.9 • x64"
|
||||||
container: centos:7
|
container: centos:7
|
||||||
@ -474,7 +479,7 @@ jobs:
|
|||||||
run: python3 -m pip install --upgrade pip
|
run: python3 -m pip install --upgrade pip
|
||||||
|
|
||||||
- name: Update CMake
|
- name: Update CMake
|
||||||
uses: jwlawson/actions-setup-cmake@v1.13
|
uses: jwlawson/actions-setup-cmake@v1.14
|
||||||
|
|
||||||
- name: Configure
|
- name: Configure
|
||||||
shell: bash
|
shell: bash
|
||||||
@ -497,6 +502,24 @@ jobs:
|
|||||||
- name: Interface test
|
- name: Interface test
|
||||||
run: cmake --build build --target test_cmake_build
|
run: cmake --build build --target test_cmake_build
|
||||||
|
|
||||||
|
- name: Configure - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||||
|
if: matrix.gcc == '12'
|
||||||
|
shell: bash
|
||||||
|
run: >
|
||||||
|
cmake -S . -B build_partial
|
||||||
|
-DPYBIND11_WERROR=ON
|
||||||
|
-DDOWNLOAD_CATCH=ON
|
||||||
|
-DCMAKE_CXX_STANDARD=${{ matrix.std }}
|
||||||
|
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
|
||||||
|
"-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp"
|
||||||
|
|
||||||
|
- name: Build - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||||
|
if: matrix.gcc == '12'
|
||||||
|
run: cmake --build build_partial -j 2
|
||||||
|
|
||||||
|
- name: Python tests - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||||
|
if: matrix.gcc == '12'
|
||||||
|
run: cmake --build build_partial --target pytest
|
||||||
|
|
||||||
# Testing on ICC using the oneAPI apt repo
|
# Testing on ICC using the oneAPI apt repo
|
||||||
icc:
|
icc:
|
||||||
@ -763,7 +786,7 @@ jobs:
|
|||||||
architecture: x86
|
architecture: x86
|
||||||
|
|
||||||
- name: Update CMake
|
- name: Update CMake
|
||||||
uses: jwlawson/actions-setup-cmake@v1.13
|
uses: jwlawson/actions-setup-cmake@v1.14
|
||||||
|
|
||||||
- name: Prepare MSVC
|
- name: Prepare MSVC
|
||||||
uses: ilammy/msvc-dev-cmd@v1.12.1
|
uses: ilammy/msvc-dev-cmd@v1.12.1
|
||||||
@ -816,7 +839,7 @@ jobs:
|
|||||||
architecture: x86
|
architecture: x86
|
||||||
|
|
||||||
- name: Update CMake
|
- name: Update CMake
|
||||||
uses: jwlawson/actions-setup-cmake@v1.13
|
uses: jwlawson/actions-setup-cmake@v1.14
|
||||||
|
|
||||||
- name: Prepare MSVC
|
- name: Prepare MSVC
|
||||||
uses: ilammy/msvc-dev-cmd@v1.12.1
|
uses: ilammy/msvc-dev-cmd@v1.12.1
|
||||||
@ -867,7 +890,7 @@ jobs:
|
|||||||
python3 -m pip install -r tests/requirements.txt
|
python3 -m pip install -r tests/requirements.txt
|
||||||
|
|
||||||
- name: Update CMake
|
- name: Update CMake
|
||||||
uses: jwlawson/actions-setup-cmake@v1.13
|
uses: jwlawson/actions-setup-cmake@v1.14
|
||||||
|
|
||||||
- name: Configure C++20
|
- name: Configure C++20
|
||||||
run: >
|
run: >
|
||||||
@ -889,6 +912,21 @@ jobs:
|
|||||||
- name: Interface test C++20
|
- name: Interface test C++20
|
||||||
run: cmake --build build --target test_cmake_build
|
run: cmake --build build --target test_cmake_build
|
||||||
|
|
||||||
|
- name: Configure C++20 - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||||
|
run: >
|
||||||
|
cmake -S . -B build_partial
|
||||||
|
-DPYBIND11_WERROR=ON
|
||||||
|
-DDOWNLOAD_CATCH=ON
|
||||||
|
-DDOWNLOAD_EIGEN=ON
|
||||||
|
-DCMAKE_CXX_STANDARD=20
|
||||||
|
"-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp"
|
||||||
|
|
||||||
|
- name: Build C++20 - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||||
|
run: cmake --build build_partial -j 2
|
||||||
|
|
||||||
|
- name: Python tests - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||||
|
run: cmake --build build_partial --target pytest
|
||||||
|
|
||||||
mingw:
|
mingw:
|
||||||
name: "🐍 3 • windows-latest • ${{ matrix.sys }}"
|
name: "🐍 3 • windows-latest • ${{ matrix.sys }}"
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
@ -1000,7 +1038,7 @@ jobs:
|
|||||||
python-version: ${{ matrix.python }}
|
python-version: ${{ matrix.python }}
|
||||||
|
|
||||||
- name: Update CMake
|
- name: Update CMake
|
||||||
uses: jwlawson/actions-setup-cmake@v1.13
|
uses: jwlawson/actions-setup-cmake@v1.14
|
||||||
|
|
||||||
- name: Install ninja-build tool
|
- name: Install ninja-build tool
|
||||||
uses: seanmiddleditch/gha-setup-ninja@v3
|
uses: seanmiddleditch/gha-setup-ninja@v3
|
||||||
@ -1070,7 +1108,7 @@ jobs:
|
|||||||
run: clang++ --version
|
run: clang++ --version
|
||||||
|
|
||||||
- name: Update CMake
|
- name: Update CMake
|
||||||
uses: jwlawson/actions-setup-cmake@v1.13
|
uses: jwlawson/actions-setup-cmake@v1.14
|
||||||
|
|
||||||
- name: Run pip installs
|
- name: Run pip installs
|
||||||
run: |
|
run: |
|
||||||
@ -1105,5 +1143,23 @@ jobs:
|
|||||||
- name: Interface test
|
- name: Interface test
|
||||||
run: cmake --build . --target test_cmake_build -j 2
|
run: cmake --build . --target test_cmake_build -j 2
|
||||||
|
|
||||||
|
- name: CMake Configure - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||||
|
run: >
|
||||||
|
cmake -S . -B build_partial
|
||||||
|
-DPYBIND11_WERROR=ON
|
||||||
|
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF
|
||||||
|
-DDOWNLOAD_CATCH=ON
|
||||||
|
-DDOWNLOAD_EIGEN=ON
|
||||||
|
-DCMAKE_CXX_COMPILER=clang++
|
||||||
|
-DCMAKE_CXX_STANDARD=17
|
||||||
|
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
|
||||||
|
"-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp"
|
||||||
|
|
||||||
|
- name: Build - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||||
|
run: cmake --build build_partial -j 2
|
||||||
|
|
||||||
|
- name: Python tests - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||||
|
run: cmake --build build_partial --target pytest -j 2
|
||||||
|
|
||||||
- name: Clean directory
|
- name: Clean directory
|
||||||
run: git clean -fdx
|
run: git clean -fdx
|
||||||
|
18
.github/workflows/configure.yml
vendored
18
.github/workflows/configure.yml
vendored
@ -9,7 +9,11 @@ on:
|
|||||||
- stable
|
- stable
|
||||||
- v*
|
- v*
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
PIP_BREAK_SYSTEM_PACKAGES: 1
|
||||||
# For cmake:
|
# For cmake:
|
||||||
VERBOSE: 1
|
VERBOSE: 1
|
||||||
|
|
||||||
@ -22,20 +26,24 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
runs-on: [ubuntu-20.04, macos-latest, windows-latest]
|
runs-on: [ubuntu-20.04, macos-latest, windows-latest]
|
||||||
arch: [x64]
|
arch: [x64]
|
||||||
cmake: ["3.23"]
|
cmake: ["3.26"]
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- runs-on: ubuntu-20.04
|
- runs-on: ubuntu-20.04
|
||||||
arch: x64
|
arch: x64
|
||||||
cmake: 3.4
|
cmake: "3.5"
|
||||||
|
|
||||||
|
- runs-on: ubuntu-20.04
|
||||||
|
arch: x64
|
||||||
|
cmake: "3.27"
|
||||||
|
|
||||||
- runs-on: macos-latest
|
- runs-on: macos-latest
|
||||||
arch: x64
|
arch: x64
|
||||||
cmake: 3.7
|
cmake: "3.7"
|
||||||
|
|
||||||
- runs-on: windows-2019
|
- runs-on: windows-2019
|
||||||
arch: x64 # x86 compilers seem to be missing on 2019 image
|
arch: x64 # x86 compilers seem to be missing on 2019 image
|
||||||
cmake: 3.18
|
cmake: "3.18"
|
||||||
|
|
||||||
name: 🐍 3.7 • CMake ${{ matrix.cmake }} • ${{ matrix.runs-on }}
|
name: 🐍 3.7 • CMake ${{ matrix.cmake }} • ${{ matrix.runs-on }}
|
||||||
runs-on: ${{ matrix.runs-on }}
|
runs-on: ${{ matrix.runs-on }}
|
||||||
@ -55,7 +63,7 @@ jobs:
|
|||||||
# An action for adding a specific version of CMake:
|
# An action for adding a specific version of CMake:
|
||||||
# https://github.com/jwlawson/actions-setup-cmake
|
# https://github.com/jwlawson/actions-setup-cmake
|
||||||
- name: Setup CMake ${{ matrix.cmake }}
|
- name: Setup CMake ${{ matrix.cmake }}
|
||||||
uses: jwlawson/actions-setup-cmake@v1.13
|
uses: jwlawson/actions-setup-cmake@v1.14
|
||||||
with:
|
with:
|
||||||
cmake-version: ${{ matrix.cmake }}
|
cmake-version: ${{ matrix.cmake }}
|
||||||
|
|
||||||
|
7
.github/workflows/format.yml
vendored
7
.github/workflows/format.yml
vendored
@ -12,6 +12,9 @@ on:
|
|||||||
- stable
|
- stable
|
||||||
- "v*"
|
- "v*"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
env:
|
env:
|
||||||
FORCE_COLOR: 3
|
FORCE_COLOR: 3
|
||||||
# For cmake:
|
# For cmake:
|
||||||
@ -38,12 +41,12 @@ jobs:
|
|||||||
# in .github/CONTRIBUTING.md and update as needed.
|
# in .github/CONTRIBUTING.md and update as needed.
|
||||||
name: Clang-Tidy
|
name: Clang-Tidy
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: silkeh/clang:13
|
container: silkeh/clang:15-bullseye
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install requirements
|
- name: Install requirements
|
||||||
run: apt-get update && apt-get install -y python3-dev python3-pytest
|
run: apt-get update && apt-get install -y git python3-dev python3-pytest
|
||||||
|
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: >
|
run: >
|
||||||
|
5
.github/workflows/labeler.yml
vendored
5
.github/workflows/labeler.yml
vendored
@ -3,10 +3,15 @@ on:
|
|||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [closed]
|
types: [closed]
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
label:
|
label:
|
||||||
name: Labeler
|
name: Labeler
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/labeler@main
|
- uses: actions/labeler@main
|
||||||
|
8
.github/workflows/pip.yml
vendored
8
.github/workflows/pip.yml
vendored
@ -12,7 +12,11 @@ on:
|
|||||||
types:
|
types:
|
||||||
- published
|
- published
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
PIP_BREAK_SYSTEM_PACKAGES: 1
|
||||||
PIP_ONLY_BINARY: numpy
|
PIP_ONLY_BINARY: numpy
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@ -98,13 +102,13 @@ jobs:
|
|||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
|
|
||||||
- name: Publish standard package
|
- name: Publish standard package
|
||||||
uses: pypa/gh-action-pypi-publish@v1.8.1
|
uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
with:
|
with:
|
||||||
password: ${{ secrets.pypi_password }}
|
password: ${{ secrets.pypi_password }}
|
||||||
packages-dir: standard/
|
packages-dir: standard/
|
||||||
|
|
||||||
- name: Publish global package
|
- name: Publish global package
|
||||||
uses: pypa/gh-action-pypi-publish@v1.8.1
|
uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
with:
|
with:
|
||||||
password: ${{ secrets.pypi_password_global }}
|
password: ${{ secrets.pypi_password_global }}
|
||||||
packages-dir: global/
|
packages-dir: global/
|
||||||
|
76
.github/workflows/upstream.yml
vendored
76
.github/workflows/upstream.yml
vendored
@ -1,114 +1,116 @@
|
|||||||
|
|
||||||
name: Upstream
|
name: Upstream
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: upstream-${{ github.ref }}
|
group: upstream-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
env:
|
env:
|
||||||
PIP_ONLY_BINARY: numpy
|
PIP_BREAK_SYSTEM_PACKAGES: 1
|
||||||
|
PIP_ONLY_BINARY: ":all:"
|
||||||
# For cmake:
|
# For cmake:
|
||||||
VERBOSE: 1
|
VERBOSE: 1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
standard:
|
standard:
|
||||||
name: "🐍 3.11 latest internals • ubuntu-latest • x64"
|
name: "🐍 3.12 latest • ubuntu-latest • x64"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
# Only runs when the 'python dev' label is selected
|
||||||
if: "contains(github.event.pull_request.labels.*.name, 'python dev')"
|
if: "contains(github.event.pull_request.labels.*.name, 'python dev')"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Python 3.11
|
- name: Setup Python 3.12
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: "3.11-dev"
|
python-version: "3.12-dev"
|
||||||
|
|
||||||
- name: Setup Boost (Linux)
|
- name: Setup Boost
|
||||||
if: runner.os == 'Linux'
|
|
||||||
run: sudo apt-get install libboost-dev
|
run: sudo apt-get install libboost-dev
|
||||||
|
|
||||||
- name: Update CMake
|
- name: Update CMake
|
||||||
uses: jwlawson/actions-setup-cmake@v1.13
|
uses: jwlawson/actions-setup-cmake@v1.14
|
||||||
|
|
||||||
- name: Prepare env
|
- name: Run pip installs
|
||||||
run: |
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
python -m pip install -r tests/requirements.txt
|
python -m pip install -r tests/requirements.txt
|
||||||
|
|
||||||
- name: Setup annotations on Linux
|
- name: Show platform info
|
||||||
if: runner.os == 'Linux'
|
run: |
|
||||||
run: python -m pip install pytest-github-actions-annotate-failures
|
python -m platform
|
||||||
|
cmake --version
|
||||||
|
pip list
|
||||||
|
|
||||||
# First build - C++11 mode and inplace
|
# First build - C++11 mode and inplace
|
||||||
- name: Configure C++11
|
- name: Configure C++11
|
||||||
run: >
|
run: >
|
||||||
cmake -S . -B .
|
cmake -S . -B build11
|
||||||
-DPYBIND11_WERROR=ON
|
-DPYBIND11_WERROR=ON
|
||||||
-DDOWNLOAD_CATCH=ON
|
-DDOWNLOAD_CATCH=ON
|
||||||
-DDOWNLOAD_EIGEN=ON
|
-DDOWNLOAD_EIGEN=ON
|
||||||
-DCMAKE_CXX_STANDARD=11
|
-DCMAKE_CXX_STANDARD=11
|
||||||
|
-DCMAKE_BUILD_TYPE=Debug
|
||||||
|
|
||||||
- name: Build C++11
|
- name: Build C++11
|
||||||
run: cmake --build . -j 2
|
run: cmake --build build11 -j 2
|
||||||
|
|
||||||
- name: Python tests C++11
|
- name: Python tests C++11
|
||||||
run: cmake --build . --target pytest -j 2
|
run: cmake --build build11 --target pytest -j 2
|
||||||
|
|
||||||
- name: C++11 tests
|
- name: C++11 tests
|
||||||
run: cmake --build . --target cpptest -j 2
|
run: cmake --build build11 --target cpptest -j 2
|
||||||
|
|
||||||
- name: Interface test C++11
|
- name: Interface test C++11
|
||||||
run: cmake --build . --target test_cmake_build
|
run: cmake --build build11 --target test_cmake_build
|
||||||
|
|
||||||
- name: Clean directory
|
|
||||||
run: git clean -fdx
|
|
||||||
|
|
||||||
# Second build - C++17 mode and in a build directory
|
# Second build - C++17 mode and in a build directory
|
||||||
- name: Configure C++17
|
- name: Configure C++17
|
||||||
run: >
|
run: >
|
||||||
cmake -S . -B build2
|
cmake -S . -B build17
|
||||||
-DPYBIND11_WERROR=ON
|
-DPYBIND11_WERROR=ON
|
||||||
-DDOWNLOAD_CATCH=ON
|
-DDOWNLOAD_CATCH=ON
|
||||||
-DDOWNLOAD_EIGEN=ON
|
-DDOWNLOAD_EIGEN=ON
|
||||||
-DCMAKE_CXX_STANDARD=17
|
-DCMAKE_CXX_STANDARD=17
|
||||||
${{ matrix.args }}
|
|
||||||
${{ matrix.args2 }}
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build C++17
|
||||||
run: cmake --build build2 -j 2
|
run: cmake --build build17 -j 2
|
||||||
|
|
||||||
- name: Python tests
|
- name: Python tests C++17
|
||||||
run: cmake --build build2 --target pytest
|
run: cmake --build build17 --target pytest
|
||||||
|
|
||||||
- name: C++ tests
|
- name: C++17 tests
|
||||||
run: cmake --build build2 --target cpptest
|
run: cmake --build build17 --target cpptest
|
||||||
|
|
||||||
# Third build - C++17 mode with unstable ABI
|
# Third build - C++17 mode with unstable ABI
|
||||||
- name: Configure (unstable ABI)
|
- name: Configure (unstable ABI)
|
||||||
run: >
|
run: >
|
||||||
cmake -S . -B build3
|
cmake -S . -B build17max
|
||||||
-DPYBIND11_WERROR=ON
|
-DPYBIND11_WERROR=ON
|
||||||
-DDOWNLOAD_CATCH=ON
|
-DDOWNLOAD_CATCH=ON
|
||||||
-DDOWNLOAD_EIGEN=ON
|
-DDOWNLOAD_EIGEN=ON
|
||||||
-DCMAKE_CXX_STANDARD=17
|
-DCMAKE_CXX_STANDARD=17
|
||||||
-DPYBIND11_INTERNALS_VERSION=10000000
|
-DPYBIND11_INTERNALS_VERSION=10000000
|
||||||
"-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp"
|
|
||||||
${{ matrix.args }}
|
|
||||||
|
|
||||||
- name: Build (unstable ABI)
|
- name: Build (unstable ABI)
|
||||||
run: cmake --build build3 -j 2
|
run: cmake --build build17max -j 2
|
||||||
|
|
||||||
- name: Python tests (unstable ABI)
|
- name: Python tests (unstable ABI)
|
||||||
run: cmake --build build3 --target pytest
|
run: cmake --build build17max --target pytest
|
||||||
|
|
||||||
- name: Interface test
|
- name: Interface test (unstable ABI)
|
||||||
run: cmake --build build3 --target test_cmake_build
|
run: cmake --build build17max --target test_cmake_build
|
||||||
|
|
||||||
# This makes sure the setup_helpers module can build packages using
|
# This makes sure the setup_helpers module can build packages using
|
||||||
# setuptools
|
# setuptools
|
||||||
- name: Setuptools helpers test
|
- name: Setuptools helpers test
|
||||||
run: pytest tests/extra_setuptools
|
run: |
|
||||||
|
pip install setuptools
|
||||||
|
pytest tests/extra_setuptools
|
||||||
|
@ -22,6 +22,49 @@ ci:
|
|||||||
exclude: ^tools/JoinPaths.cmake$
|
exclude: ^tools/JoinPaths.cmake$
|
||||||
|
|
||||||
repos:
|
repos:
|
||||||
|
|
||||||
|
# Clang format the codebase automatically
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||||
|
rev: "v16.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
|
||||||
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
|
rev: v0.0.276
|
||||||
|
hooks:
|
||||||
|
- id: ruff
|
||||||
|
args: ["--fix", "--show-fixes"]
|
||||||
|
|
||||||
|
# Check static types with mypy
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
|
rev: "v1.4.1"
|
||||||
|
hooks:
|
||||||
|
- id: mypy
|
||||||
|
args: []
|
||||||
|
exclude: ^(tests|docs)/
|
||||||
|
additional_dependencies:
|
||||||
|
- markdown-it-py<3 # Drop this together with dropping Python 3.7 support.
|
||||||
|
- nox
|
||||||
|
- rich
|
||||||
|
- types-setuptools
|
||||||
|
|
||||||
|
# CMake formatting
|
||||||
|
- repo: https://github.com/cheshirekow/cmake-format-precommit
|
||||||
|
rev: "v0.6.13"
|
||||||
|
hooks:
|
||||||
|
- id: cmake-format
|
||||||
|
additional_dependencies: [pyyaml]
|
||||||
|
types: [file]
|
||||||
|
files: (\.cmake|CMakeLists.txt)(.in)?$
|
||||||
|
|
||||||
# Standard hooks
|
# Standard hooks
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: "v4.4.0"
|
rev: "v4.4.0"
|
||||||
@ -39,106 +82,35 @@ repos:
|
|||||||
- id: requirements-txt-fixer
|
- id: requirements-txt-fixer
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
|
|
||||||
# Upgrade old Python syntax
|
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
|
||||||
rev: "v3.3.1"
|
|
||||||
hooks:
|
|
||||||
- id: pyupgrade
|
|
||||||
args: [--py36-plus]
|
|
||||||
|
|
||||||
# Nicely sort includes
|
|
||||||
- repo: https://github.com/PyCQA/isort
|
|
||||||
rev: "5.12.0"
|
|
||||||
hooks:
|
|
||||||
- id: isort
|
|
||||||
|
|
||||||
# Black, the code formatter, natively supports pre-commit
|
|
||||||
- repo: https://github.com/psf/black
|
|
||||||
rev: "23.1.0" # Keep in sync with blacken-docs
|
|
||||||
hooks:
|
|
||||||
- id: black
|
|
||||||
|
|
||||||
# Also code format the docs
|
# Also code format the docs
|
||||||
- repo: https://github.com/asottile/blacken-docs
|
- repo: https://github.com/asottile/blacken-docs
|
||||||
rev: "1.13.0"
|
rev: "1.14.0"
|
||||||
hooks:
|
hooks:
|
||||||
- id: blacken-docs
|
- id: blacken-docs
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- black==23.1.0 # keep in sync with black hook
|
- black==23.3.0 # keep in sync with black hook
|
||||||
|
|
||||||
# Changes tabs to spaces
|
# Changes tabs to spaces
|
||||||
- repo: https://github.com/Lucas-C/pre-commit-hooks
|
- repo: https://github.com/Lucas-C/pre-commit-hooks
|
||||||
rev: "v1.4.2"
|
rev: "v1.5.1"
|
||||||
hooks:
|
hooks:
|
||||||
- id: remove-tabs
|
- id: remove-tabs
|
||||||
|
|
||||||
|
# Avoid directional quotes
|
||||||
- repo: https://github.com/sirosen/texthooks
|
- repo: https://github.com/sirosen/texthooks
|
||||||
rev: "0.5.0"
|
rev: "0.5.0"
|
||||||
hooks:
|
hooks:
|
||||||
- id: fix-ligatures
|
- id: fix-ligatures
|
||||||
- id: fix-smartquotes
|
- id: fix-smartquotes
|
||||||
|
|
||||||
# Autoremoves unused imports
|
|
||||||
- repo: https://github.com/hadialqattan/pycln
|
|
||||||
rev: "v2.1.3"
|
|
||||||
hooks:
|
|
||||||
- id: pycln
|
|
||||||
stages: [manual]
|
|
||||||
|
|
||||||
# Checking for common mistakes
|
# Checking for common mistakes
|
||||||
- repo: https://github.com/pre-commit/pygrep-hooks
|
- repo: https://github.com/pre-commit/pygrep-hooks
|
||||||
rev: "v1.10.0"
|
rev: "v1.10.0"
|
||||||
hooks:
|
hooks:
|
||||||
- id: python-check-blanket-noqa
|
|
||||||
- id: python-check-blanket-type-ignore
|
|
||||||
- id: python-no-log-warn
|
|
||||||
- id: python-use-type-annotations
|
|
||||||
- id: rst-backticks
|
- id: rst-backticks
|
||||||
- id: rst-directive-colons
|
- id: rst-directive-colons
|
||||||
- id: rst-inline-touching-normal
|
- id: rst-inline-touching-normal
|
||||||
|
|
||||||
# Automatically remove noqa that are not used
|
|
||||||
- repo: https://github.com/asottile/yesqa
|
|
||||||
rev: "v1.4.0"
|
|
||||||
hooks:
|
|
||||||
- id: yesqa
|
|
||||||
additional_dependencies: &flake8_dependencies
|
|
||||||
- flake8-bugbear
|
|
||||||
- pep8-naming
|
|
||||||
|
|
||||||
# Flake8 also supports pre-commit natively (same author)
|
|
||||||
- repo: https://github.com/PyCQA/flake8
|
|
||||||
rev: "6.0.0"
|
|
||||||
hooks:
|
|
||||||
- id: flake8
|
|
||||||
exclude: ^(docs/.*|tools/.*)$
|
|
||||||
additional_dependencies: *flake8_dependencies
|
|
||||||
|
|
||||||
# PyLint has native support - not always usable, but works for us
|
|
||||||
- repo: https://github.com/PyCQA/pylint
|
|
||||||
rev: "v2.16.1"
|
|
||||||
hooks:
|
|
||||||
- id: pylint
|
|
||||||
files: ^pybind11
|
|
||||||
|
|
||||||
# CMake formatting
|
|
||||||
- repo: https://github.com/cheshirekow/cmake-format-precommit
|
|
||||||
rev: "v0.6.13"
|
|
||||||
hooks:
|
|
||||||
- id: cmake-format
|
|
||||||
additional_dependencies: [pyyaml]
|
|
||||||
types: [file]
|
|
||||||
files: (\.cmake|CMakeLists.txt)(.in)?$
|
|
||||||
|
|
||||||
# Check static types with mypy
|
|
||||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
||||||
rev: "v0.991"
|
|
||||||
hooks:
|
|
||||||
- id: mypy
|
|
||||||
args: []
|
|
||||||
exclude: ^(tests|docs)/
|
|
||||||
additional_dependencies: [nox, rich]
|
|
||||||
|
|
||||||
# Checks the manifest for missing files (native support)
|
# Checks the manifest for missing files (native support)
|
||||||
- repo: https://github.com/mgedmin/check-manifest
|
- repo: https://github.com/mgedmin/check-manifest
|
||||||
rev: "0.49"
|
rev: "0.49"
|
||||||
@ -152,15 +124,15 @@ repos:
|
|||||||
# Use tools/codespell_ignore_lines_from_errors.py
|
# Use tools/codespell_ignore_lines_from_errors.py
|
||||||
# to rebuild .codespell-ignore-lines
|
# to rebuild .codespell-ignore-lines
|
||||||
- repo: https://github.com/codespell-project/codespell
|
- repo: https://github.com/codespell-project/codespell
|
||||||
rev: "v2.2.2"
|
rev: "v2.2.5"
|
||||||
hooks:
|
hooks:
|
||||||
- id: codespell
|
- id: codespell
|
||||||
exclude: ".supp$"
|
exclude: ".supp$"
|
||||||
args: ["-x", ".codespell-ignore-lines"]
|
args: ["-x.codespell-ignore-lines", "-Lccompiler"]
|
||||||
|
|
||||||
# Check for common shell mistakes
|
# Check for common shell mistakes
|
||||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||||
rev: "v0.9.0.2"
|
rev: "v0.9.0.5"
|
||||||
hooks:
|
hooks:
|
||||||
- id: shellcheck
|
- id: shellcheck
|
||||||
|
|
||||||
@ -173,9 +145,9 @@ repos:
|
|||||||
entry: PyBind|Numpy|Cmake|CCache|PyTest
|
entry: PyBind|Numpy|Cmake|CCache|PyTest
|
||||||
exclude: ^\.pre-commit-config.yaml$
|
exclude: ^\.pre-commit-config.yaml$
|
||||||
|
|
||||||
# Clang format the codebase automatically
|
# PyLint has native support - not always usable, but works for us
|
||||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
- repo: https://github.com/PyCQA/pylint
|
||||||
rev: "v15.0.7"
|
rev: "v3.0.0a6"
|
||||||
hooks:
|
hooks:
|
||||||
- id: clang-format
|
- id: pylint
|
||||||
types_or: [c++, c, cuda]
|
files: ^pybind11
|
||||||
|
@ -5,15 +5,15 @@
|
|||||||
# All rights reserved. Use of this source code is governed by a
|
# All rights reserved. Use of this source code is governed by a
|
||||||
# BSD-style license that can be found in the LICENSE file.
|
# BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.4)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
# The `cmake_minimum_required(VERSION 3.4...3.22)` syntax does not work with
|
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
|
||||||
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
||||||
# the behavior using the following workaround:
|
# the behavior using the following workaround:
|
||||||
if(${CMAKE_VERSION} VERSION_LESS 3.22)
|
if(${CMAKE_VERSION} VERSION_LESS 3.26)
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||||
else()
|
else()
|
||||||
cmake_policy(VERSION 3.22)
|
cmake_policy(VERSION 3.26)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Avoid infinite recursion if tests include this as a subdirectory
|
# Avoid infinite recursion if tests include this as a subdirectory
|
||||||
@ -126,6 +126,7 @@ set(PYBIND11_HEADERS
|
|||||||
include/pybind11/complex.h
|
include/pybind11/complex.h
|
||||||
include/pybind11/options.h
|
include/pybind11/options.h
|
||||||
include/pybind11/eigen.h
|
include/pybind11/eigen.h
|
||||||
|
include/pybind11/eigen/common.h
|
||||||
include/pybind11/eigen/matrix.h
|
include/pybind11/eigen/matrix.h
|
||||||
include/pybind11/eigen/tensor.h
|
include/pybind11/eigen/tensor.h
|
||||||
include/pybind11/embed.h
|
include/pybind11/embed.h
|
||||||
@ -139,7 +140,8 @@ set(PYBIND11_HEADERS
|
|||||||
include/pybind11/pytypes.h
|
include/pybind11/pytypes.h
|
||||||
include/pybind11/stl.h
|
include/pybind11/stl.h
|
||||||
include/pybind11/stl_bind.h
|
include/pybind11/stl_bind.h
|
||||||
include/pybind11/stl/filesystem.h)
|
include/pybind11/stl/filesystem.h
|
||||||
|
include/pybind11/type_caster_pyobject_ptr.h)
|
||||||
|
|
||||||
# Compare with grep and warn if mismatched
|
# Compare with grep and warn if mismatched
|
||||||
if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12)
|
if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12)
|
||||||
|
@ -3,4 +3,4 @@ recursive-include pybind11/include/pybind11 *.h
|
|||||||
recursive-include pybind11 *.py
|
recursive-include pybind11 *.py
|
||||||
recursive-include pybind11 py.typed
|
recursive-include pybind11 py.typed
|
||||||
include pybind11/share/cmake/pybind11/*.cmake
|
include pybind11/share/cmake/pybind11/*.cmake
|
||||||
include LICENSE README.rst pyproject.toml setup.py setup.cfg
|
include LICENSE README.rst SECURITY.md pyproject.toml setup.py setup.cfg
|
||||||
|
13
SECURITY.md
Normal file
13
SECURITY.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
Security updates are applied only to the latest release.
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
|
||||||
|
|
||||||
|
Please disclose it at [security advisory](https://github.com/pybind/pybind11/security/advisories/new).
|
||||||
|
|
||||||
|
This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure.
|
@ -101,8 +101,11 @@ conversion has the same overhead as implicit conversion.
|
|||||||
m.def("str_output",
|
m.def("str_output",
|
||||||
[]() {
|
[]() {
|
||||||
std::string s = "Send your r\xe9sum\xe9 to Alice in HR"; // Latin-1
|
std::string s = "Send your r\xe9sum\xe9 to Alice in HR"; // Latin-1
|
||||||
py::str py_s = PyUnicode_DecodeLatin1(s.data(), s.length());
|
py::handle py_s = PyUnicode_DecodeLatin1(s.data(), s.length(), nullptr);
|
||||||
return py_s;
|
if (!py_s) {
|
||||||
|
throw py::error_already_set();
|
||||||
|
}
|
||||||
|
return py::reinterpret_steal<py::str>(py_s);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -113,7 +116,8 @@ conversion has the same overhead as implicit conversion.
|
|||||||
|
|
||||||
The `Python C API
|
The `Python C API
|
||||||
<https://docs.python.org/3/c-api/unicode.html#built-in-codecs>`_ provides
|
<https://docs.python.org/3/c-api/unicode.html#built-in-codecs>`_ provides
|
||||||
several built-in codecs.
|
several built-in codecs. Note that these all return *new* references, so
|
||||||
|
use :cpp:func:`reinterpret_steal` when converting them to a :cpp:class:`str`.
|
||||||
|
|
||||||
|
|
||||||
One could also use a third party encoding library such as libiconv to transcode
|
One could also use a third party encoding library such as libiconv to transcode
|
||||||
|
@ -18,7 +18,7 @@ information, see :doc:`/compiling`.
|
|||||||
|
|
||||||
.. code-block:: cmake
|
.. code-block:: cmake
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.4)
|
cmake_minimum_required(VERSION 3.5...3.26)
|
||||||
project(example)
|
project(example)
|
||||||
|
|
||||||
find_package(pybind11 REQUIRED) # or `add_subdirectory(pybind11)`
|
find_package(pybind11 REQUIRED) # or `add_subdirectory(pybind11)`
|
||||||
|
@ -10,10 +10,38 @@ Changes will be added here periodically from the "Suggested changelog entry"
|
|||||||
block in pull request descriptions.
|
block in pull request descriptions.
|
||||||
|
|
||||||
|
|
||||||
IN DEVELOPMENT
|
Version 2.11.0 (July 14, 2023)
|
||||||
--------------
|
-----------------------------
|
||||||
|
|
||||||
Changes will be summarized here periodically.
|
New features:
|
||||||
|
|
||||||
|
* The newly added ``pybind11::detail::is_move_constructible`` trait can be
|
||||||
|
specialized for cases in which ``std::is_move_constructible`` does not work
|
||||||
|
as needed. This is very similar to the long-established
|
||||||
|
``pybind11::detail::is_copy_constructible``.
|
||||||
|
`#4631 <https://github.com/pybind/pybind11/pull/4631>`_
|
||||||
|
|
||||||
|
* Introduce ``recursive_container_traits``.
|
||||||
|
`#4623 <https://github.com/pybind/pybind11/pull/4623>`_
|
||||||
|
|
||||||
|
* ``pybind11/type_caster_pyobject_ptr.h`` was added to support automatic
|
||||||
|
wrapping of APIs that make use of ``PyObject *``. This header needs to
|
||||||
|
included explicitly (i.e. it is not included implicitly
|
||||||
|
with ``pybind/pybind11.h``).
|
||||||
|
`#4601 <https://github.com/pybind/pybind11/pull/4601>`_
|
||||||
|
|
||||||
|
* ``format_descriptor<>`` & ``npy_format_descriptor<>`` ``PyObject *``
|
||||||
|
specializations were added. The latter enables ``py::array_t<PyObject *>``
|
||||||
|
to/from-python conversions.
|
||||||
|
`#4674 <https://github.com/pybind/pybind11/pull/4674>`_
|
||||||
|
|
||||||
|
* ``buffer_info`` gained an ``item_type_is_equivalent_to<T>()`` member
|
||||||
|
function.
|
||||||
|
`#4674 <https://github.com/pybind/pybind11/pull/4674>`_
|
||||||
|
|
||||||
|
* The ``capsule`` API gained a user-friendly constructor
|
||||||
|
(``py::capsule(ptr, "name", dtor)``).
|
||||||
|
`#4720 <https://github.com/pybind/pybind11/pull/4720>`_
|
||||||
|
|
||||||
Changes:
|
Changes:
|
||||||
|
|
||||||
@ -31,17 +59,68 @@ Changes:
|
|||||||
sizes slightly (~1.5%) but the error messages are much more informative.
|
sizes slightly (~1.5%) but the error messages are much more informative.
|
||||||
`#4463 <https://github.com/pybind/pybind11/pull/4463>`_
|
`#4463 <https://github.com/pybind/pybind11/pull/4463>`_
|
||||||
|
|
||||||
|
* The docstring generation for the ``std::array``-list caster was fixed.
|
||||||
|
Previously, signatures included the size of the list in a non-standard,
|
||||||
|
non-spec compliant way. The new format conforms to PEP 593.
|
||||||
|
**Tooling for processing the docstrings may need to be updated accordingly.**
|
||||||
|
`#4679 <https://github.com/pybind/pybind11/pull/4679>`_
|
||||||
|
|
||||||
|
* Setter return values (which are inaccessible for all practical purposes) are
|
||||||
|
no longer converted to Python (only to be discarded).
|
||||||
|
`#4621 <https://github.com/pybind/pybind11/pull/4621>`_
|
||||||
|
|
||||||
|
* Allow lambda specified to function definition to be ``noexcept(true)``
|
||||||
|
in C++17.
|
||||||
|
`#4593 <https://github.com/pybind/pybind11/pull/4593>`_
|
||||||
|
|
||||||
|
* Get rid of recursive template instantiations for concatenating type
|
||||||
|
signatures on C++17 and higher.
|
||||||
|
`#4587 <https://github.com/pybind/pybind11/pull/4587>`_
|
||||||
|
|
||||||
|
* Compatibility with Python 3.12 (beta). Note that the minimum pybind11
|
||||||
|
ABI version for Python 3.12 is version 5. (The default ABI version
|
||||||
|
for Python versions up to and including 3.11 is still version 4.).
|
||||||
|
`#4570 <https://github.com/pybind/pybind11/pull/4570>`_
|
||||||
|
|
||||||
|
* With ``PYBIND11_INTERNALS_VERSION 5`` (default for Python 3.12+), MSVC builds
|
||||||
|
use ``std::hash<std::type_index>`` and ``std::equal_to<std::type_index>``
|
||||||
|
instead of string-based type comparisons. This resolves issues when binding
|
||||||
|
types defined in the unnamed namespace.
|
||||||
|
`#4319 <https://github.com/pybind/pybind11/pull/4319>`_
|
||||||
|
|
||||||
|
* Python exception ``__notes__`` (introduced with Python 3.11) are now added to
|
||||||
|
the ``error_already_set::what()`` output.
|
||||||
|
`#4678 <https://github.com/pybind/pybind11/pull/4678>`_
|
||||||
|
|
||||||
Build system improvements:
|
Build system improvements:
|
||||||
|
|
||||||
|
* CMake 3.27 support was added, CMake 3.4 support was dropped.
|
||||||
|
FindPython will be used if ``FindPythonInterp`` is not present.
|
||||||
|
`#4719 <https://github.com/pybind/pybind11/pull/4719>`_
|
||||||
|
|
||||||
* Update clang-tidy to 15 in CI.
|
* Update clang-tidy to 15 in CI.
|
||||||
`#4387 <https://github.com/pybind/pybind11/pull/4387>`_
|
`#4387 <https://github.com/pybind/pybind11/pull/4387>`_
|
||||||
|
|
||||||
* Moved the linting framework over to Ruff.
|
* Moved the linting framework over to Ruff.
|
||||||
`#4483 <https://github.com/pybind/pybind11/pull/4483>`_
|
`#4483 <https://github.com/pybind/pybind11/pull/4483>`_
|
||||||
|
|
||||||
|
* Skip ``lto`` checks and target generation when
|
||||||
|
``CMAKE_INTERPROCEDURAL_OPTIMIZATION`` is defined.
|
||||||
|
`#4643 <https://github.com/pybind/pybind11/pull/4643>`_
|
||||||
|
|
||||||
|
* No longer inject ``-stdlib=libc++``, not needed for modern Pythons
|
||||||
|
(macOS 10.9+).
|
||||||
|
`#4639 <https://github.com/pybind/pybind11/pull/4639>`_
|
||||||
|
|
||||||
|
* PyPy 3.10 support was added, PyPy 3.7 support was dropped.
|
||||||
|
`#4728 <https://github.com/pybind/pybind11/pull/4728>`_
|
||||||
|
|
||||||
|
* Testing with Python 3.12 beta releases was added.
|
||||||
|
`#4713 <https://github.com/pybind/pybind11/pull/4713>`_
|
||||||
|
|
||||||
|
|
||||||
Version 2.10.4 (Mar 16, 2023)
|
Version 2.10.4 (Mar 16, 2023)
|
||||||
----------------------------
|
-----------------------------
|
||||||
|
|
||||||
Changes:
|
Changes:
|
||||||
|
|
||||||
|
@ -58,6 +58,16 @@ interactive Python session demonstrating this example is shown below:
|
|||||||
Static member functions can be bound in the same way using
|
Static member functions can be bound in the same way using
|
||||||
:func:`class_::def_static`.
|
:func:`class_::def_static`.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Binding C++ types in unnamed namespaces (also known as anonymous namespaces)
|
||||||
|
works reliably on many platforms, but not all. The `XFAIL_CONDITION` in
|
||||||
|
tests/test_unnamed_namespace_a.py encodes the currently known conditions.
|
||||||
|
For background see `#4319 <https://github.com/pybind/pybind11/pull/4319>`_.
|
||||||
|
If portability is a concern, it is therefore not recommended to bind C++
|
||||||
|
types in unnamed namespaces. It will be safest to manually pick unique
|
||||||
|
namespace names.
|
||||||
|
|
||||||
Keyword and default arguments
|
Keyword and default arguments
|
||||||
=============================
|
=============================
|
||||||
It is possible to specify keyword and default arguments using the syntax
|
It is possible to specify keyword and default arguments using the syntax
|
||||||
@ -539,3 +549,7 @@ The ``name`` property returns the name of the enum value as a unicode string.
|
|||||||
...
|
...
|
||||||
|
|
||||||
By default, these are omitted to conserve space.
|
By default, these are omitted to conserve space.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
Contrary to Python customs, enum values from the wrappers should not be compared using ``is``, but with ``==`` (see `#1177 <https://github.com/pybind/pybind11/issues/1177>`_ for background).
|
||||||
|
@ -241,7 +241,7 @@ extension module can be created with just a few lines of code:
|
|||||||
|
|
||||||
.. code-block:: cmake
|
.. code-block:: cmake
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.4...3.18)
|
cmake_minimum_required(VERSION 3.5...3.26)
|
||||||
project(example LANGUAGES CXX)
|
project(example LANGUAGES CXX)
|
||||||
|
|
||||||
add_subdirectory(pybind11)
|
add_subdirectory(pybind11)
|
||||||
@ -261,6 +261,9 @@ PyPI integration, can be found in the [cmake_example]_ repository.
|
|||||||
.. versionchanged:: 2.6
|
.. versionchanged:: 2.6
|
||||||
CMake 3.4+ is required.
|
CMake 3.4+ is required.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.11
|
||||||
|
CMake 3.5+ is required.
|
||||||
|
|
||||||
Further information can be found at :doc:`cmake/index`.
|
Further information can be found at :doc:`cmake/index`.
|
||||||
|
|
||||||
pybind11_add_module
|
pybind11_add_module
|
||||||
@ -495,7 +498,7 @@ You can use these targets to build complex applications. For example, the
|
|||||||
|
|
||||||
.. code-block:: cmake
|
.. code-block:: cmake
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.4)
|
cmake_minimum_required(VERSION 3.5...3.26)
|
||||||
project(example LANGUAGES CXX)
|
project(example LANGUAGES CXX)
|
||||||
|
|
||||||
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
|
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
|
||||||
@ -553,7 +556,7 @@ information about usage in C++, see :doc:`/advanced/embedding`.
|
|||||||
|
|
||||||
.. code-block:: cmake
|
.. code-block:: cmake
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.4...3.18)
|
cmake_minimum_required(VERSION 3.5...3.26)
|
||||||
project(example LANGUAGES CXX)
|
project(example LANGUAGES CXX)
|
||||||
|
|
||||||
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
|
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
|
||||||
|
@ -353,7 +353,7 @@ def prepare(app):
|
|||||||
f.write(contents)
|
f.write(contents)
|
||||||
|
|
||||||
|
|
||||||
def clean_up(app, exception):
|
def clean_up(app, exception): # noqa: ARG001
|
||||||
(DIR / "readme.rst").unlink()
|
(DIR / "readme.rst").unlink()
|
||||||
|
|
||||||
|
|
||||||
|
@ -284,7 +284,8 @@ There are three possible solutions:
|
|||||||
COMPONENTS Interpreter Development)`` on modern CMake (3.12+, 3.15+ better,
|
COMPONENTS Interpreter Development)`` on modern CMake (3.12+, 3.15+ better,
|
||||||
3.18.2+ best). Pybind11 in these cases uses the new CMake FindPython instead
|
3.18.2+ best). Pybind11 in these cases uses the new CMake FindPython instead
|
||||||
of the old, deprecated search tools, and these modules are much better at
|
of the old, deprecated search tools, and these modules are much better at
|
||||||
finding the correct Python.
|
finding the correct Python. If FindPythonLibs/Interp are not available
|
||||||
|
(CMake 3.27+), then this will be ignored and FindPython will be used.
|
||||||
3. Set ``PYBIND11_NOPYTHON`` to ``TRUE``. Pybind11 will not search for Python.
|
3. Set ``PYBIND11_NOPYTHON`` to ``TRUE``. Pybind11 will not search for Python.
|
||||||
However, you will have to use the target-based system, and do more setup
|
However, you will have to use the target-based system, and do more setup
|
||||||
yourself, because it does not know about or include things that depend on
|
yourself, because it does not know about or include things that depend on
|
||||||
|
@ -33,10 +33,12 @@ If you don't have nox, you should either use ``pipx run nox`` instead, or use
|
|||||||
- Run ``nox -s tests_packaging`` to ensure this was done correctly.
|
- Run ``nox -s tests_packaging`` to ensure this was done correctly.
|
||||||
- Ensure that all the information in ``setup.cfg`` is up-to-date, like
|
- Ensure that all the information in ``setup.cfg`` is up-to-date, like
|
||||||
supported Python versions.
|
supported Python versions.
|
||||||
- Add release date in ``docs/changelog.rst``.
|
- Add release date in ``docs/changelog.rst`` and integrate the output of
|
||||||
- Check to make sure
|
``nox -s make_changelog``.
|
||||||
`needs-changelog <https://github.com/pybind/pybind11/pulls?q=is%3Apr+is%3Aclosed+label%3A%22needs+changelog%22>`_
|
- Note that the ``make_changelog`` command inspects
|
||||||
issues are entered in the changelog (clear the label when done).
|
`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
|
- ``git add`` and ``git commit``, ``git push``. **Ensure CI passes**. (If it
|
||||||
fails due to a known flake issue, either ignore or restart CI.)
|
fails due to a known flake issue, either ignore or restart CI.)
|
||||||
- Add a release branch if this is a new minor version, or update the existing release branch if it is a patch version
|
- Add a release branch if this is a new minor version, or update the existing release branch if it is a patch version
|
||||||
|
@ -8,6 +8,20 @@ to a new version. But it goes into more detail. This includes things like
|
|||||||
deprecated APIs and their replacements, build system changes, general code
|
deprecated APIs and their replacements, build system changes, general code
|
||||||
modernization and other useful information.
|
modernization and other useful information.
|
||||||
|
|
||||||
|
.. _upgrade-guide-2.11:
|
||||||
|
|
||||||
|
v2.11
|
||||||
|
=====
|
||||||
|
|
||||||
|
* The minimum version of CMake is now 3.5. A future version will likely move to
|
||||||
|
requiring something like CMake 3.15. Note that CMake 3.27 is removing the
|
||||||
|
long-deprecated support for ``FindPythonInterp`` if you set 3.27 as the
|
||||||
|
minimum or maximum supported version. To prepare for that future, CMake 3.15+
|
||||||
|
using ``FindPython`` or setting ``PYBIND11_FINDPYTHON`` is highly recommended,
|
||||||
|
otherwise pybind11 will automatically switch to using ``FindPython`` if
|
||||||
|
``FindPythonInterp`` is not available.
|
||||||
|
|
||||||
|
|
||||||
.. _upgrade-guide-2.9:
|
.. _upgrade-guide-2.9:
|
||||||
|
|
||||||
v2.9
|
v2.9
|
||||||
|
@ -26,6 +26,9 @@ struct is_method {
|
|||||||
explicit is_method(const handle &c) : class_(c) {}
|
explicit is_method(const handle &c) : class_(c) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Annotation for setters
|
||||||
|
struct is_setter {};
|
||||||
|
|
||||||
/// Annotation for operators
|
/// Annotation for operators
|
||||||
struct is_operator {};
|
struct is_operator {};
|
||||||
|
|
||||||
@ -188,8 +191,8 @@ struct argument_record {
|
|||||||
struct function_record {
|
struct function_record {
|
||||||
function_record()
|
function_record()
|
||||||
: is_constructor(false), is_new_style_constructor(false), is_stateless(false),
|
: is_constructor(false), is_new_style_constructor(false), is_stateless(false),
|
||||||
is_operator(false), is_method(false), has_args(false), has_kwargs(false),
|
is_operator(false), is_method(false), is_setter(false), has_args(false),
|
||||||
prepend(false) {}
|
has_kwargs(false), prepend(false) {}
|
||||||
|
|
||||||
/// Function name
|
/// Function name
|
||||||
char *name = nullptr; /* why no C++ strings? They generate heavier code.. */
|
char *name = nullptr; /* why no C++ strings? They generate heavier code.. */
|
||||||
@ -230,6 +233,9 @@ struct function_record {
|
|||||||
/// True if this is a method
|
/// True if this is a method
|
||||||
bool is_method : 1;
|
bool is_method : 1;
|
||||||
|
|
||||||
|
/// True if this is a setter
|
||||||
|
bool is_setter : 1;
|
||||||
|
|
||||||
/// True if the function has a '*args' argument
|
/// True if the function has a '*args' argument
|
||||||
bool has_args : 1;
|
bool has_args : 1;
|
||||||
|
|
||||||
@ -426,6 +432,12 @@ struct process_attribute<is_method> : process_attribute_default<is_method> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Process an attribute which indicates that this function is a setter
|
||||||
|
template <>
|
||||||
|
struct process_attribute<is_setter> : process_attribute_default<is_setter> {
|
||||||
|
static void init(const is_setter &, function_record *r) { r->is_setter = true; }
|
||||||
|
};
|
||||||
|
|
||||||
/// Process an attribute which indicates the parent scope of a method
|
/// Process an attribute which indicates the parent scope of a method
|
||||||
template <>
|
template <>
|
||||||
struct process_attribute<scope> : process_attribute_default<scope> {
|
struct process_attribute<scope> : process_attribute_default<scope> {
|
||||||
|
@ -37,6 +37,9 @@ inline std::vector<ssize_t> f_strides(const std::vector<ssize_t> &shape, ssize_t
|
|||||||
return strides;
|
return strides;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T, typename SFINAE = void>
|
||||||
|
struct compare_buffer_info;
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_END(detail)
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
|
||||||
/// Information record describing a Python buffer object
|
/// Information record describing a Python buffer object
|
||||||
@ -150,6 +153,17 @@ struct buffer_info {
|
|||||||
Py_buffer *view() const { return m_view; }
|
Py_buffer *view() const { return m_view; }
|
||||||
Py_buffer *&view() { return m_view; }
|
Py_buffer *&view() { return m_view; }
|
||||||
|
|
||||||
|
/* True if the buffer item type is equivalent to `T`. */
|
||||||
|
// To define "equivalent" by example:
|
||||||
|
// `buffer_info::item_type_is_equivalent_to<int>(b)` and
|
||||||
|
// `buffer_info::item_type_is_equivalent_to<long>(b)` may both be true
|
||||||
|
// on some platforms, but `int` and `unsigned` will never be equivalent.
|
||||||
|
// For the ground truth, please inspect `detail::compare_buffer_info<>`.
|
||||||
|
template <typename T>
|
||||||
|
bool item_type_is_equivalent_to() const {
|
||||||
|
return detail::compare_buffer_info<T>::compare(*this);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct private_ctr_tag {};
|
struct private_ctr_tag {};
|
||||||
|
|
||||||
@ -170,9 +184,10 @@ private:
|
|||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
template <typename T, typename SFINAE = void>
|
template <typename T, typename SFINAE>
|
||||||
struct compare_buffer_info {
|
struct compare_buffer_info {
|
||||||
static bool compare(const buffer_info &b) {
|
static bool compare(const buffer_info &b) {
|
||||||
|
// NOLINTNEXTLINE(bugprone-sizeof-expression) Needed for `PyObject *`
|
||||||
return b.format == format_descriptor<T>::format() && b.itemsize == (ssize_t) sizeof(T);
|
return b.format == format_descriptor<T>::format() && b.itemsize == (ssize_t) sizeof(T);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -964,7 +964,7 @@ struct move_always<
|
|||||||
enable_if_t<
|
enable_if_t<
|
||||||
all_of<move_is_plain_type<T>,
|
all_of<move_is_plain_type<T>,
|
||||||
negation<is_copy_constructible<T>>,
|
negation<is_copy_constructible<T>>,
|
||||||
std::is_move_constructible<T>,
|
is_move_constructible<T>,
|
||||||
std::is_same<decltype(std::declval<make_caster<T>>().operator T &()), T &>>::value>>
|
std::is_same<decltype(std::declval<make_caster<T>>().operator T &()), T &>>::value>>
|
||||||
: std::true_type {};
|
: std::true_type {};
|
||||||
template <typename T, typename SFINAE = void>
|
template <typename T, typename SFINAE = void>
|
||||||
@ -975,7 +975,7 @@ struct move_if_unreferenced<
|
|||||||
enable_if_t<
|
enable_if_t<
|
||||||
all_of<move_is_plain_type<T>,
|
all_of<move_is_plain_type<T>,
|
||||||
negation<move_always<T>>,
|
negation<move_always<T>>,
|
||||||
std::is_move_constructible<T>,
|
is_move_constructible<T>,
|
||||||
std::is_same<decltype(std::declval<make_caster<T>>().operator T &()), T &>>::value>>
|
std::is_same<decltype(std::declval<make_caster<T>>().operator T &()), T &>>::value>>
|
||||||
: std::true_type {};
|
: std::true_type {};
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -1017,11 +1017,14 @@ type_caster<T, SFINAE> &load_type(type_caster<T, SFINAE> &conv, const handle &ha
|
|||||||
"Internal error: type_caster should only be used for C++ types");
|
"Internal error: type_caster should only be used for C++ types");
|
||||||
if (!conv.load(handle, true)) {
|
if (!conv.load(handle, true)) {
|
||||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||||
throw cast_error("Unable to cast Python instance to C++ type (#define "
|
throw cast_error(
|
||||||
"PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
"Unable to cast Python instance of type "
|
||||||
|
+ str(type::handle_of(handle)).cast<std::string>()
|
||||||
|
+ " to C++ type '?' (#define "
|
||||||
|
"PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
||||||
#else
|
#else
|
||||||
throw cast_error("Unable to cast Python instance of type "
|
throw cast_error("Unable to cast Python instance of type "
|
||||||
+ (std::string) str(type::handle_of(handle)) + " to C++ type '"
|
+ str(type::handle_of(handle)).cast<std::string>() + " to C++ type '"
|
||||||
+ type_id<T>() + "'");
|
+ type_id<T>() + "'");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -1038,7 +1041,11 @@ make_caster<T> load_type(const handle &handle) {
|
|||||||
PYBIND11_NAMESPACE_END(detail)
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
|
||||||
// pytype -> C++ type
|
// pytype -> C++ type
|
||||||
template <typename T, detail::enable_if_t<!detail::is_pyobject<T>::value, int> = 0>
|
template <typename T,
|
||||||
|
detail::enable_if_t<!detail::is_pyobject<T>::value
|
||||||
|
&& !detail::is_same_ignoring_cvref<T, PyObject *>::value,
|
||||||
|
int>
|
||||||
|
= 0>
|
||||||
T cast(const handle &handle) {
|
T cast(const handle &handle) {
|
||||||
using namespace detail;
|
using namespace detail;
|
||||||
static_assert(!cast_is_temporary_value_reference<T>::value,
|
static_assert(!cast_is_temporary_value_reference<T>::value,
|
||||||
@ -1052,6 +1059,34 @@ T cast(const handle &handle) {
|
|||||||
return T(reinterpret_borrow<object>(handle));
|
return T(reinterpret_borrow<object>(handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note that `cast<PyObject *>(obj)` increments the reference count of `obj`.
|
||||||
|
// This is necessary for the case that `obj` is a temporary, and could
|
||||||
|
// not possibly be different, given
|
||||||
|
// 1. the established convention that the passed `handle` is borrowed, and
|
||||||
|
// 2. we don't want to force all generic code using `cast<T>()` to special-case
|
||||||
|
// handling of `T` = `PyObject *` (to increment the reference count there).
|
||||||
|
// It is the responsibility of the caller to ensure that the reference count
|
||||||
|
// is decremented.
|
||||||
|
template <typename T,
|
||||||
|
typename Handle,
|
||||||
|
detail::enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value
|
||||||
|
&& detail::is_same_ignoring_cvref<Handle, handle>::value,
|
||||||
|
int>
|
||||||
|
= 0>
|
||||||
|
T cast(Handle &&handle) {
|
||||||
|
return handle.inc_ref().ptr();
|
||||||
|
}
|
||||||
|
// To optimize way an inc_ref/dec_ref cycle:
|
||||||
|
template <typename T,
|
||||||
|
typename Object,
|
||||||
|
detail::enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value
|
||||||
|
&& detail::is_same_ignoring_cvref<Object, object>::value,
|
||||||
|
int>
|
||||||
|
= 0>
|
||||||
|
T cast(Object &&obj) {
|
||||||
|
return obj.release().ptr();
|
||||||
|
}
|
||||||
|
|
||||||
// C++ type -> py::object
|
// C++ type -> py::object
|
||||||
template <typename T, detail::enable_if_t<!detail::is_pyobject<T>::value, int> = 0>
|
template <typename T, detail::enable_if_t<!detail::is_pyobject<T>::value, int> = 0>
|
||||||
object cast(T &&value,
|
object cast(T &&value,
|
||||||
@ -1085,12 +1120,13 @@ detail::enable_if_t<!detail::move_never<T>::value, T> move(object &&obj) {
|
|||||||
if (obj.ref_count() > 1) {
|
if (obj.ref_count() > 1) {
|
||||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||||
throw cast_error(
|
throw cast_error(
|
||||||
"Unable to cast Python instance to C++ rvalue: instance has multiple references"
|
"Unable to cast Python " + str(type::handle_of(obj)).cast<std::string>()
|
||||||
" (#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
+ " instance to C++ rvalue: instance has multiple references"
|
||||||
|
" (#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
||||||
#else
|
#else
|
||||||
throw cast_error("Unable to move from Python " + (std::string) str(type::handle_of(obj))
|
throw cast_error("Unable to move from Python "
|
||||||
+ " instance to C++ " + type_id<T>()
|
+ str(type::handle_of(obj)).cast<std::string>() + " instance to C++ "
|
||||||
+ " instance: instance has multiple references");
|
+ type_id<T>() + " instance: instance has multiple references");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1195,9 +1231,10 @@ PYBIND11_NAMESPACE_END(detail)
|
|||||||
// The overloads could coexist, i.e. the #if is not strictly speaking needed,
|
// The overloads could coexist, i.e. the #if is not strictly speaking needed,
|
||||||
// but it is an easy minor optimization.
|
// but it is an easy minor optimization.
|
||||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||||
inline cast_error cast_error_unable_to_convert_call_arg() {
|
inline cast_error cast_error_unable_to_convert_call_arg(const std::string &name) {
|
||||||
return cast_error("Unable to convert call argument to Python object (#define "
|
return cast_error("Unable to convert call argument '" + name
|
||||||
"PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
+ "' to Python object (#define "
|
||||||
|
"PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
inline cast_error cast_error_unable_to_convert_call_arg(const std::string &name,
|
inline cast_error cast_error_unable_to_convert_call_arg(const std::string &name,
|
||||||
@ -1220,7 +1257,7 @@ tuple make_tuple(Args &&...args_) {
|
|||||||
for (size_t i = 0; i < args.size(); i++) {
|
for (size_t i = 0; i < args.size(); i++) {
|
||||||
if (!args[i]) {
|
if (!args[i]) {
|
||||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||||
throw cast_error_unable_to_convert_call_arg();
|
throw cast_error_unable_to_convert_call_arg(std::to_string(i));
|
||||||
#else
|
#else
|
||||||
std::array<std::string, size> argtypes{{type_id<Args>()...}};
|
std::array<std::string, size> argtypes{{type_id<Args>()...}};
|
||||||
throw cast_error_unable_to_convert_call_arg(std::to_string(i), argtypes[i]);
|
throw cast_error_unable_to_convert_call_arg(std::to_string(i), argtypes[i]);
|
||||||
@ -1510,7 +1547,7 @@ private:
|
|||||||
detail::make_caster<T>::cast(std::forward<T>(x), policy, {}));
|
detail::make_caster<T>::cast(std::forward<T>(x), policy, {}));
|
||||||
if (!o) {
|
if (!o) {
|
||||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||||
throw cast_error_unable_to_convert_call_arg();
|
throw cast_error_unable_to_convert_call_arg(std::to_string(args_list.size()));
|
||||||
#else
|
#else
|
||||||
throw cast_error_unable_to_convert_call_arg(std::to_string(args_list.size()),
|
throw cast_error_unable_to_convert_call_arg(std::to_string(args_list.size()),
|
||||||
type_id<T>());
|
type_id<T>());
|
||||||
@ -1542,7 +1579,7 @@ private:
|
|||||||
}
|
}
|
||||||
if (!a.value) {
|
if (!a.value) {
|
||||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||||
throw cast_error_unable_to_convert_call_arg();
|
throw cast_error_unable_to_convert_call_arg(a.name);
|
||||||
#else
|
#else
|
||||||
throw cast_error_unable_to_convert_call_arg(a.name, a.type);
|
throw cast_error_unable_to_convert_call_arg(a.name, a.type);
|
||||||
#endif
|
#endif
|
||||||
|
@ -10,12 +10,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define PYBIND11_VERSION_MAJOR 2
|
#define PYBIND11_VERSION_MAJOR 2
|
||||||
#define PYBIND11_VERSION_MINOR 10
|
#define PYBIND11_VERSION_MINOR 11
|
||||||
#define PYBIND11_VERSION_PATCH 4
|
#define PYBIND11_VERSION_PATCH 0
|
||||||
|
|
||||||
// Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html
|
// Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html
|
||||||
// Additional convention: 0xD = dev
|
// Additional convention: 0xD = dev
|
||||||
#define PYBIND11_VERSION_HEX 0x020A0400
|
#define PYBIND11_VERSION_HEX 0x020B0000
|
||||||
|
|
||||||
// Define some generic pybind11 helper macros for warning management.
|
// Define some generic pybind11 helper macros for warning management.
|
||||||
//
|
//
|
||||||
@ -329,8 +329,7 @@ PYBIND11_WARNING_POP
|
|||||||
&& defined(_MSC_VER)) /* PyPy Windows: pytest hangs indefinitely at the end of the \
|
&& defined(_MSC_VER)) /* PyPy Windows: pytest hangs indefinitely at the end of the \
|
||||||
process (see PR #4268) */ \
|
process (see PR #4268) */ \
|
||||||
&& !defined(PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF)
|
&& !defined(PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF)
|
||||||
// The following define will be enabled by default in the 2.11 release
|
# define PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF
|
||||||
// define PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// #define PYBIND11_STR_LEGACY_PERMISSIVE
|
// #define PYBIND11_STR_LEGACY_PERMISSIVE
|
||||||
@ -662,6 +661,10 @@ template <class T>
|
|||||||
using remove_cvref_t = typename remove_cvref<T>::type;
|
using remove_cvref_t = typename remove_cvref<T>::type;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// Example usage: is_same_ignoring_cvref<T, PyObject *>::value
|
||||||
|
template <typename T, typename U>
|
||||||
|
using is_same_ignoring_cvref = std::is_same<detail::remove_cvref_t<T>, U>;
|
||||||
|
|
||||||
/// Index sequences
|
/// Index sequences
|
||||||
#if defined(PYBIND11_CPP14)
|
#if defined(PYBIND11_CPP14)
|
||||||
using std::index_sequence;
|
using std::index_sequence;
|
||||||
@ -755,7 +758,16 @@ template <typename C, typename R, typename... A>
|
|||||||
struct remove_class<R (C::*)(A...) const> {
|
struct remove_class<R (C::*)(A...) const> {
|
||||||
using type = R(A...);
|
using type = R(A...);
|
||||||
};
|
};
|
||||||
|
#ifdef __cpp_noexcept_function_type
|
||||||
|
template <typename C, typename R, typename... A>
|
||||||
|
struct remove_class<R (C::*)(A...) noexcept> {
|
||||||
|
using type = R(A...);
|
||||||
|
};
|
||||||
|
template <typename C, typename R, typename... A>
|
||||||
|
struct remove_class<R (C::*)(A...) const noexcept> {
|
||||||
|
using type = R(A...);
|
||||||
|
};
|
||||||
|
#endif
|
||||||
/// Helper template to strip away type modifiers
|
/// Helper template to strip away type modifiers
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct intrinsic_type {
|
struct intrinsic_type {
|
||||||
@ -1013,6 +1025,15 @@ PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used in
|
|||||||
template <typename T, typename SFINAE = void>
|
template <typename T, typename SFINAE = void>
|
||||||
struct format_descriptor {};
|
struct format_descriptor {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct format_descriptor<
|
||||||
|
T,
|
||||||
|
detail::enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value>> {
|
||||||
|
static constexpr const char c = 'O';
|
||||||
|
static constexpr const char value[2] = {c, '\0'};
|
||||||
|
static std::string format() { return std::string(1, c); }
|
||||||
|
};
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
// Returns the index of the given type in the type char array below, and in the list in numpy.h
|
// Returns the index of the given type in the type char array below, and in the list in numpy.h
|
||||||
// The order here is: bool; 8 ints ((signed,unsigned)x(8,16,32,64)bits); float,double,long double;
|
// The order here is: bool; 8 ints ((signed,unsigned)x(8,16,32,64)bits); float,double,long double;
|
||||||
@ -1226,8 +1247,9 @@ constexpr
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Pybind offers detailed error messages by default for all builts that are debug (through the
|
// Pybind offers detailed error messages by default for all builts that are debug (through the
|
||||||
// negation of ndebug). This can also be manually enabled by users, for any builds, through
|
// negation of NDEBUG). This can also be manually enabled by users, for any builds, through
|
||||||
// defining PYBIND11_DETAILED_ERROR_MESSAGES.
|
// defining PYBIND11_DETAILED_ERROR_MESSAGES. This information is primarily useful for those
|
||||||
|
// who are writing (as opposed to merely using) libraries that use pybind11.
|
||||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES) && !defined(NDEBUG)
|
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES) && !defined(NDEBUG)
|
||||||
# define PYBIND11_DETAILED_ERROR_MESSAGES
|
# define PYBIND11_DETAILED_ERROR_MESSAGES
|
||||||
#endif
|
#endif
|
||||||
|
@ -143,11 +143,24 @@ constexpr descr<N, Ts...> concat(const descr<N, Ts...> &descr) {
|
|||||||
return descr;
|
return descr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __cpp_fold_expressions
|
||||||
|
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2>
|
||||||
|
constexpr descr<N1 + N2 + 2, Ts1..., Ts2...> operator,(const descr<N1, Ts1...> &a,
|
||||||
|
const descr<N2, Ts2...> &b) {
|
||||||
|
return a + const_name(", ") + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N, typename... Ts, typename... Args>
|
||||||
|
constexpr auto concat(const descr<N, Ts...> &d, const Args &...args) {
|
||||||
|
return (d, ..., args);
|
||||||
|
}
|
||||||
|
#else
|
||||||
template <size_t N, typename... Ts, typename... Args>
|
template <size_t N, typename... Ts, typename... Args>
|
||||||
constexpr auto concat(const descr<N, Ts...> &d, const Args &...args)
|
constexpr auto concat(const descr<N, Ts...> &d, const Args &...args)
|
||||||
-> decltype(std::declval<descr<N + 2, Ts...>>() + concat(args...)) {
|
-> decltype(std::declval<descr<N + 2, Ts...>>() + concat(args...)) {
|
||||||
return d + const_name(", ") + concat(args...);
|
return d + const_name(", ") + concat(args...);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
template <size_t N, typename... Ts>
|
template <size_t N, typename... Ts>
|
||||||
constexpr descr<N + 2, Ts...> type_descr(const descr<N, Ts...> &descr) {
|
constexpr descr<N + 2, Ts...> type_descr(const descr<N, Ts...> &descr) {
|
||||||
|
@ -175,7 +175,7 @@ void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
|
|||||||
template <typename Class>
|
template <typename Class>
|
||||||
void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
|
void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
|
||||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||||
static_assert(std::is_move_constructible<Cpp<Class>>::value,
|
static_assert(is_move_constructible<Cpp<Class>>::value,
|
||||||
"pybind11::init() return-by-value factory function requires a movable class");
|
"pybind11::init() return-by-value factory function requires a movable class");
|
||||||
if (Class::has_alias && need_alias) {
|
if (Class::has_alias && need_alias) {
|
||||||
construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result));
|
construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result));
|
||||||
@ -190,7 +190,7 @@ void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
|
|||||||
template <typename Class>
|
template <typename Class>
|
||||||
void construct(value_and_holder &v_h, Alias<Class> &&result, bool) {
|
void construct(value_and_holder &v_h, Alias<Class> &&result, bool) {
|
||||||
static_assert(
|
static_assert(
|
||||||
std::is_move_constructible<Alias<Class>>::value,
|
is_move_constructible<Alias<Class>>::value,
|
||||||
"pybind11::init() return-by-alias-value factory function requires a movable alias class");
|
"pybind11::init() return-by-alias-value factory function requires a movable alias class");
|
||||||
v_h.value_ptr() = new Alias<Class>(std::move(result));
|
v_h.value_ptr() = new Alias<Class>(std::move(result));
|
||||||
}
|
}
|
||||||
|
@ -34,9 +34,18 @@
|
|||||||
/// further ABI-incompatible changes may be made before the ABI is officially
|
/// further ABI-incompatible changes may be made before the ABI is officially
|
||||||
/// changed to the new version.
|
/// changed to the new version.
|
||||||
#ifndef PYBIND11_INTERNALS_VERSION
|
#ifndef PYBIND11_INTERNALS_VERSION
|
||||||
# define PYBIND11_INTERNALS_VERSION 4
|
# if PY_VERSION_HEX >= 0x030C0000
|
||||||
|
// Version bump for Python 3.12+, before first 3.12 beta release.
|
||||||
|
# define PYBIND11_INTERNALS_VERSION 5
|
||||||
|
# else
|
||||||
|
# define PYBIND11_INTERNALS_VERSION 4
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// This requirement is mainly to reduce the support burden (see PR #4570).
|
||||||
|
static_assert(PY_VERSION_HEX < 0x030C0000 || PYBIND11_INTERNALS_VERSION >= 5,
|
||||||
|
"pybind11 ABI version 5 is the minimum for Python 3.12+");
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
|
||||||
using ExceptionTranslator = void (*)(std::exception_ptr);
|
using ExceptionTranslator = void (*)(std::exception_ptr);
|
||||||
@ -114,7 +123,8 @@ inline void tls_replace_value(PYBIND11_TLS_KEY_REF key, void *value) {
|
|||||||
// libstdc++, this doesn't happen: equality and the type_index hash are based on the type name,
|
// libstdc++, this doesn't happen: equality and the type_index hash are based on the type name,
|
||||||
// which works. If not under a known-good stl, provide our own name-based hash and equality
|
// which works. If not under a known-good stl, provide our own name-based hash and equality
|
||||||
// functions that use the type name.
|
// functions that use the type name.
|
||||||
#if defined(__GLIBCXX__)
|
#if (PYBIND11_INTERNALS_VERSION <= 4 && defined(__GLIBCXX__)) \
|
||||||
|
|| (PYBIND11_INTERNALS_VERSION >= 5 && !defined(_LIBCPP_VERSION))
|
||||||
inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; }
|
inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; }
|
||||||
using type_hash = std::hash<std::type_index>;
|
using type_hash = std::hash<std::type_index>;
|
||||||
using type_equal_to = std::equal_to<std::type_index>;
|
using type_equal_to = std::equal_to<std::type_index>;
|
||||||
@ -421,6 +431,38 @@ inline void translate_local_exception(std::exception_ptr p) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
inline object get_python_state_dict() {
|
||||||
|
object state_dict;
|
||||||
|
#if PYBIND11_INTERNALS_VERSION <= 4 || PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION)
|
||||||
|
state_dict = reinterpret_borrow<object>(PyEval_GetBuiltins());
|
||||||
|
#else
|
||||||
|
# if PY_VERSION_HEX < 0x03090000
|
||||||
|
PyInterpreterState *istate = _PyInterpreterState_Get();
|
||||||
|
# else
|
||||||
|
PyInterpreterState *istate = PyInterpreterState_Get();
|
||||||
|
# endif
|
||||||
|
if (istate) {
|
||||||
|
state_dict = reinterpret_borrow<object>(PyInterpreterState_GetDict(istate));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (!state_dict) {
|
||||||
|
raise_from(PyExc_SystemError, "pybind11::detail::get_python_state_dict() FAILED");
|
||||||
|
}
|
||||||
|
return state_dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline object get_internals_obj_from_state_dict(handle state_dict) {
|
||||||
|
return reinterpret_borrow<object>(dict_getitemstring(state_dict.ptr(), PYBIND11_INTERNALS_ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
return static_cast<internals **>(raw_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
/// Return a reference to the current `internals` data
|
/// Return a reference to the current `internals` data
|
||||||
PYBIND11_NOINLINE internals &get_internals() {
|
PYBIND11_NOINLINE internals &get_internals() {
|
||||||
auto **&internals_pp = get_internals_pp();
|
auto **&internals_pp = get_internals_pp();
|
||||||
@ -445,12 +487,12 @@ PYBIND11_NOINLINE internals &get_internals() {
|
|||||||
#endif
|
#endif
|
||||||
error_scope err_scope;
|
error_scope err_scope;
|
||||||
|
|
||||||
PYBIND11_STR_TYPE id(PYBIND11_INTERNALS_ID);
|
dict state_dict = get_python_state_dict();
|
||||||
auto builtins = handle(PyEval_GetBuiltins());
|
if (object internals_obj = get_internals_obj_from_state_dict(state_dict)) {
|
||||||
if (builtins.contains(id) && isinstance<capsule>(builtins[id])) {
|
internals_pp = get_internals_pp_from_capsule(internals_obj);
|
||||||
internals_pp = static_cast<internals **>(capsule(builtins[id]));
|
}
|
||||||
|
if (internals_pp && *internals_pp) {
|
||||||
// We loaded builtins through python's builtins, which means that our `error_already_set`
|
// We loaded the internals through `state_dict`, which means that our `error_already_set`
|
||||||
// and `builtin_exception` may be different local classes than the ones set up in the
|
// and `builtin_exception` may be different local classes than the ones set up in the
|
||||||
// initial exception translator, below, so add another for our local exception classes.
|
// initial exception translator, below, so add another for our local exception classes.
|
||||||
//
|
//
|
||||||
@ -469,12 +511,14 @@ PYBIND11_NOINLINE internals &get_internals() {
|
|||||||
#if defined(WITH_THREAD)
|
#if defined(WITH_THREAD)
|
||||||
|
|
||||||
PyThreadState *tstate = PyThreadState_Get();
|
PyThreadState *tstate = PyThreadState_Get();
|
||||||
|
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition)
|
||||||
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->tstate)) {
|
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->tstate)) {
|
||||||
pybind11_fail("get_internals: could not successfully initialize the tstate TSS key!");
|
pybind11_fail("get_internals: could not successfully initialize the tstate TSS key!");
|
||||||
}
|
}
|
||||||
PYBIND11_TLS_REPLACE_VALUE(internals_ptr->tstate, tstate);
|
PYBIND11_TLS_REPLACE_VALUE(internals_ptr->tstate, tstate);
|
||||||
|
|
||||||
# if PYBIND11_INTERNALS_VERSION > 4
|
# if PYBIND11_INTERNALS_VERSION > 4
|
||||||
|
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition)
|
||||||
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->loader_life_support_tls_key)) {
|
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->loader_life_support_tls_key)) {
|
||||||
pybind11_fail("get_internals: could not successfully initialize the "
|
pybind11_fail("get_internals: could not successfully initialize the "
|
||||||
"loader_life_support TSS key!");
|
"loader_life_support TSS key!");
|
||||||
@ -482,7 +526,7 @@ PYBIND11_NOINLINE internals &get_internals() {
|
|||||||
# endif
|
# endif
|
||||||
internals_ptr->istate = tstate->interp;
|
internals_ptr->istate = tstate->interp;
|
||||||
#endif
|
#endif
|
||||||
builtins[id] = capsule(internals_pp);
|
state_dict[PYBIND11_INTERNALS_ID] = capsule(internals_pp);
|
||||||
internals_ptr->registered_exception_translators.push_front(&translate_exception);
|
internals_ptr->registered_exception_translators.push_front(&translate_exception);
|
||||||
internals_ptr->static_property_type = make_static_property_type();
|
internals_ptr->static_property_type = make_static_property_type();
|
||||||
internals_ptr->default_metaclass = make_default_metaclass();
|
internals_ptr->default_metaclass = make_default_metaclass();
|
||||||
@ -514,6 +558,7 @@ struct local_internals {
|
|||||||
struct shared_loader_life_support_data {
|
struct shared_loader_life_support_data {
|
||||||
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
|
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
|
||||||
shared_loader_life_support_data() {
|
shared_loader_life_support_data() {
|
||||||
|
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition)
|
||||||
if (!PYBIND11_TLS_KEY_CREATE(loader_life_support_tls_key)) {
|
if (!PYBIND11_TLS_KEY_CREATE(loader_life_support_tls_key)) {
|
||||||
pybind11_fail("local_internals: could not successfully initialize the "
|
pybind11_fail("local_internals: could not successfully initialize the "
|
||||||
"loader_life_support TLS key!");
|
"loader_life_support TLS key!");
|
||||||
|
@ -258,9 +258,9 @@ struct value_and_holder {
|
|||||||
|
|
||||||
// Main constructor for a found value/holder:
|
// Main constructor for a found value/holder:
|
||||||
value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index)
|
value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index)
|
||||||
: inst{i}, index{index}, type{type}, vh{inst->simple_layout
|
: inst{i}, index{index}, type{type},
|
||||||
? inst->simple_value_holder
|
vh{inst->simple_layout ? inst->simple_value_holder
|
||||||
: &inst->nonsimple.values_and_holders[vpos]} {}
|
: &inst->nonsimple.values_and_holders[vpos]} {}
|
||||||
|
|
||||||
// Default constructor (used to signal a value-and-holder not found by get_value_and_holder())
|
// Default constructor (used to signal a value-and-holder not found by get_value_and_holder())
|
||||||
value_and_holder() = default;
|
value_and_holder() = default;
|
||||||
@ -822,23 +822,179 @@ using movable_cast_op_type
|
|||||||
typename std::add_rvalue_reference<intrinsic_t<T>>::type,
|
typename std::add_rvalue_reference<intrinsic_t<T>>::type,
|
||||||
typename std::add_lvalue_reference<intrinsic_t<T>>::type>>;
|
typename std::add_lvalue_reference<intrinsic_t<T>>::type>>;
|
||||||
|
|
||||||
|
// Does the container have a mapped type and is it recursive?
|
||||||
|
// Implemented by specializations below.
|
||||||
|
template <typename Container, typename SFINAE = void>
|
||||||
|
struct container_mapped_type_traits {
|
||||||
|
static constexpr bool has_mapped_type = false;
|
||||||
|
static constexpr bool has_recursive_mapped_type = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
struct container_mapped_type_traits<
|
||||||
|
Container,
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same<typename Container::mapped_type, Container>::value>::type> {
|
||||||
|
static constexpr bool has_mapped_type = true;
|
||||||
|
static constexpr bool has_recursive_mapped_type = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
struct container_mapped_type_traits<
|
||||||
|
Container,
|
||||||
|
typename std::enable_if<
|
||||||
|
negation<std::is_same<typename Container::mapped_type, Container>>::value>::type> {
|
||||||
|
static constexpr bool has_mapped_type = true;
|
||||||
|
static constexpr bool has_recursive_mapped_type = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Does the container have a value type and is it recursive?
|
||||||
|
// Implemented by specializations below.
|
||||||
|
template <typename Container, typename SFINAE = void>
|
||||||
|
struct container_value_type_traits : std::false_type {
|
||||||
|
static constexpr bool has_value_type = false;
|
||||||
|
static constexpr bool has_recursive_value_type = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
struct container_value_type_traits<
|
||||||
|
Container,
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same<typename Container::value_type, Container>::value>::type> {
|
||||||
|
static constexpr bool has_value_type = true;
|
||||||
|
static constexpr bool has_recursive_value_type = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
struct container_value_type_traits<
|
||||||
|
Container,
|
||||||
|
typename std::enable_if<
|
||||||
|
negation<std::is_same<typename Container::value_type, Container>>::value>::type> {
|
||||||
|
static constexpr bool has_value_type = true;
|
||||||
|
static constexpr bool has_recursive_value_type = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tag to be used for representing the bottom of recursively defined types.
|
||||||
|
* Define this tag so we don't have to use void.
|
||||||
|
*/
|
||||||
|
struct recursive_bottom {};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Implementation detail of `recursive_container_traits` below.
|
||||||
|
* `T` is the `value_type` of the container, which might need to be modified to
|
||||||
|
* avoid recursive types and const types.
|
||||||
|
*/
|
||||||
|
template <typename T, bool is_this_a_map>
|
||||||
|
struct impl_type_to_check_recursively {
|
||||||
|
/*
|
||||||
|
* If the container is recursive, then no further recursion should be done.
|
||||||
|
*/
|
||||||
|
using if_recursive = recursive_bottom;
|
||||||
|
/*
|
||||||
|
* Otherwise yield `T` unchanged.
|
||||||
|
*/
|
||||||
|
using if_not_recursive = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For pairs - only as value type of a map -, the first type should remove the `const`.
|
||||||
|
* Also, if the map is recursive, then the recursive checking should consider
|
||||||
|
* the first type only.
|
||||||
|
*/
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct impl_type_to_check_recursively<std::pair<A, B>, /* is_this_a_map = */ true> {
|
||||||
|
using if_recursive = typename std::remove_const<A>::type;
|
||||||
|
using if_not_recursive = std::pair<typename std::remove_const<A>::type, B>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Implementation of `recursive_container_traits` below.
|
||||||
|
*/
|
||||||
|
template <typename Container, typename SFINAE = void>
|
||||||
|
struct impl_recursive_container_traits {
|
||||||
|
using type_to_check_recursively = recursive_bottom;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
struct impl_recursive_container_traits<
|
||||||
|
Container,
|
||||||
|
typename std::enable_if<container_value_type_traits<Container>::has_value_type>::type> {
|
||||||
|
static constexpr bool is_recursive
|
||||||
|
= container_mapped_type_traits<Container>::has_recursive_mapped_type
|
||||||
|
|| container_value_type_traits<Container>::has_recursive_value_type;
|
||||||
|
/*
|
||||||
|
* This member dictates which type Pybind11 should check recursively in traits
|
||||||
|
* such as `is_move_constructible`, `is_copy_constructible`, `is_move_assignable`, ...
|
||||||
|
* Direct access to `value_type` should be avoided:
|
||||||
|
* 1. `value_type` might recursively contain the type again
|
||||||
|
* 2. `value_type` of STL map types is `std::pair<A const, B>`, the `const`
|
||||||
|
* should be removed.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
using type_to_check_recursively = typename std::conditional<
|
||||||
|
is_recursive,
|
||||||
|
typename impl_type_to_check_recursively<
|
||||||
|
typename Container::value_type,
|
||||||
|
container_mapped_type_traits<Container>::has_mapped_type>::if_recursive,
|
||||||
|
typename impl_type_to_check_recursively<
|
||||||
|
typename Container::value_type,
|
||||||
|
container_mapped_type_traits<Container>::has_mapped_type>::if_not_recursive>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This trait defines the `type_to_check_recursively` which is needed to properly
|
||||||
|
* handle recursively defined traits such as `is_move_constructible` without going
|
||||||
|
* into an infinite recursion.
|
||||||
|
* Should be used instead of directly accessing the `value_type`.
|
||||||
|
* It cancels the recursion by returning the `recursive_bottom` tag.
|
||||||
|
*
|
||||||
|
* The default definition of `type_to_check_recursively` is as follows:
|
||||||
|
*
|
||||||
|
* 1. By default, it is `recursive_bottom`, so that the recursion is canceled.
|
||||||
|
* 2. If the type is non-recursive and defines a `value_type`, then the `value_type` is used.
|
||||||
|
* If the `value_type` is a pair and a `mapped_type` is defined,
|
||||||
|
* then the `const` is removed from the first type.
|
||||||
|
* 3. If the type is recursive and `value_type` is not a pair, then `recursive_bottom` is returned.
|
||||||
|
* 4. If the type is recursive and `value_type` is a pair and a `mapped_type` is defined,
|
||||||
|
* then `const` is removed from the first type and the first type is returned.
|
||||||
|
*
|
||||||
|
* This behavior can be extended by the user as seen in test_stl_binders.cpp.
|
||||||
|
*
|
||||||
|
* This struct is exactly the same as impl_recursive_container_traits.
|
||||||
|
* The duplication achieves that user-defined specializations don't compete
|
||||||
|
* with internal specializations, but take precedence.
|
||||||
|
*/
|
||||||
|
template <typename Container, typename SFINAE = void>
|
||||||
|
struct recursive_container_traits : impl_recursive_container_traits<Container> {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_move_constructible
|
||||||
|
: all_of<std::is_move_constructible<T>,
|
||||||
|
is_move_constructible<
|
||||||
|
typename recursive_container_traits<T>::type_to_check_recursively>> {};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct is_move_constructible<recursive_bottom> : std::true_type {};
|
||||||
|
|
||||||
|
// Likewise for std::pair
|
||||||
|
// (after C++17 it is mandatory that the move constructor not exist when the two types aren't
|
||||||
|
// themselves move constructible, but this can not be relied upon when T1 or T2 are themselves
|
||||||
|
// containers).
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
struct is_move_constructible<std::pair<T1, T2>>
|
||||||
|
: all_of<is_move_constructible<T1>, is_move_constructible<T2>> {};
|
||||||
|
|
||||||
// std::is_copy_constructible isn't quite enough: it lets std::vector<T> (and similar) through when
|
// std::is_copy_constructible isn't quite enough: it lets std::vector<T> (and similar) through when
|
||||||
// T is non-copyable, but code containing such a copy constructor fails to actually compile.
|
// T is non-copyable, but code containing such a copy constructor fails to actually compile.
|
||||||
template <typename T, typename SFINAE = void>
|
template <typename T>
|
||||||
struct is_copy_constructible : std::is_copy_constructible<T> {};
|
struct is_copy_constructible
|
||||||
|
: all_of<std::is_copy_constructible<T>,
|
||||||
|
is_copy_constructible<
|
||||||
|
typename recursive_container_traits<T>::type_to_check_recursively>> {};
|
||||||
|
|
||||||
// Specialization for types that appear to be copy constructible but also look like stl containers
|
template <>
|
||||||
// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if
|
struct is_copy_constructible<recursive_bottom> : std::true_type {};
|
||||||
// so, copy constructability depends on whether the value_type is copy constructible.
|
|
||||||
template <typename Container>
|
|
||||||
struct is_copy_constructible<
|
|
||||||
Container,
|
|
||||||
enable_if_t<
|
|
||||||
all_of<std::is_copy_constructible<Container>,
|
|
||||||
std::is_same<typename Container::value_type &, typename Container::reference>,
|
|
||||||
// Avoid infinite recursion
|
|
||||||
negation<std::is_same<Container, typename Container::value_type>>>::value>>
|
|
||||||
: is_copy_constructible<typename Container::value_type> {};
|
|
||||||
|
|
||||||
// Likewise for std::pair
|
// Likewise for std::pair
|
||||||
// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't
|
// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't
|
||||||
@ -849,14 +1005,16 @@ struct is_copy_constructible<std::pair<T1, T2>>
|
|||||||
: all_of<is_copy_constructible<T1>, is_copy_constructible<T2>> {};
|
: all_of<is_copy_constructible<T1>, is_copy_constructible<T2>> {};
|
||||||
|
|
||||||
// The same problems arise with std::is_copy_assignable, so we use the same workaround.
|
// The same problems arise with std::is_copy_assignable, so we use the same workaround.
|
||||||
template <typename T, typename SFINAE = void>
|
template <typename T>
|
||||||
struct is_copy_assignable : std::is_copy_assignable<T> {};
|
struct is_copy_assignable
|
||||||
template <typename Container>
|
: all_of<
|
||||||
struct is_copy_assignable<Container,
|
std::is_copy_assignable<T>,
|
||||||
enable_if_t<all_of<std::is_copy_assignable<Container>,
|
is_copy_assignable<typename recursive_container_traits<T>::type_to_check_recursively>> {
|
||||||
std::is_same<typename Container::value_type &,
|
};
|
||||||
typename Container::reference>>::value>>
|
|
||||||
: is_copy_assignable<typename Container::value_type> {};
|
template <>
|
||||||
|
struct is_copy_assignable<recursive_bottom> : std::true_type {};
|
||||||
|
|
||||||
template <typename T1, typename T2>
|
template <typename T1, typename T2>
|
||||||
struct is_copy_assignable<std::pair<T1, T2>>
|
struct is_copy_assignable<std::pair<T1, T2>>
|
||||||
: all_of<is_copy_assignable<T1>, is_copy_assignable<T2>> {};
|
: all_of<is_copy_assignable<T1>, is_copy_assignable<T2>> {};
|
||||||
@ -994,7 +1152,7 @@ protected:
|
|||||||
return [](const void *arg) -> void * { return new T(*reinterpret_cast<const T *>(arg)); };
|
return [](const void *arg) -> void * { return new T(*reinterpret_cast<const T *>(arg)); };
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename = enable_if_t<std::is_move_constructible<T>::value>>
|
template <typename T, typename = enable_if_t<is_move_constructible<T>::value>>
|
||||||
static auto make_move_constructor(const T *)
|
static auto make_move_constructor(const T *)
|
||||||
-> decltype(new T(std::declval<T &&>()), Constructor{}) {
|
-> decltype(new T(std::declval<T &&>()), Constructor{}) {
|
||||||
return [](const void *arg) -> void * {
|
return [](const void *arg) -> void * {
|
||||||
|
9
include/pybind11/eigen/common.h
Normal file
9
include/pybind11/eigen/common.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) 2023 The pybind Community.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Common message for `static_assert()`s, which are useful to easily
|
||||||
|
// preempt much less obvious errors.
|
||||||
|
#define PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED \
|
||||||
|
"Pointer types (in particular `PyObject *`) are not supported as scalar types for Eigen " \
|
||||||
|
"types."
|
@ -10,6 +10,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../numpy.h"
|
#include "../numpy.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
/* HINT: To suppress warnings originating from the Eigen headers, use -isystem.
|
/* HINT: To suppress warnings originating from the Eigen headers, use -isystem.
|
||||||
See also:
|
See also:
|
||||||
@ -287,6 +288,8 @@ handle eigen_encapsulate(Type *src) {
|
|||||||
template <typename Type>
|
template <typename Type>
|
||||||
struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
|
struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
|
||||||
using Scalar = typename Type::Scalar;
|
using Scalar = typename Type::Scalar;
|
||||||
|
static_assert(!std::is_pointer<Scalar>::value,
|
||||||
|
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||||
using props = EigenProps<Type>;
|
using props = EigenProps<Type>;
|
||||||
|
|
||||||
bool load(handle src, bool convert) {
|
bool load(handle src, bool convert) {
|
||||||
@ -405,6 +408,9 @@ private:
|
|||||||
// Base class for casting reference/map/block/etc. objects back to python.
|
// Base class for casting reference/map/block/etc. objects back to python.
|
||||||
template <typename MapType>
|
template <typename MapType>
|
||||||
struct eigen_map_caster {
|
struct eigen_map_caster {
|
||||||
|
static_assert(!std::is_pointer<typename MapType::Scalar>::value,
|
||||||
|
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using props = EigenProps<MapType>;
|
using props = EigenProps<MapType>;
|
||||||
|
|
||||||
@ -457,6 +463,8 @@ private:
|
|||||||
using Type = Eigen::Ref<PlainObjectType, 0, StrideType>;
|
using Type = Eigen::Ref<PlainObjectType, 0, StrideType>;
|
||||||
using props = EigenProps<Type>;
|
using props = EigenProps<Type>;
|
||||||
using Scalar = typename props::Scalar;
|
using Scalar = typename props::Scalar;
|
||||||
|
static_assert(!std::is_pointer<Scalar>::value,
|
||||||
|
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||||
using MapType = Eigen::Map<PlainObjectType, 0, StrideType>;
|
using MapType = Eigen::Map<PlainObjectType, 0, StrideType>;
|
||||||
using Array
|
using Array
|
||||||
= array_t<Scalar,
|
= array_t<Scalar,
|
||||||
@ -604,6 +612,9 @@ private:
|
|||||||
// regular Eigen::Matrix, then casting that.
|
// regular Eigen::Matrix, then casting that.
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
struct type_caster<Type, enable_if_t<is_eigen_other<Type>::value>> {
|
struct type_caster<Type, enable_if_t<is_eigen_other<Type>::value>> {
|
||||||
|
static_assert(!std::is_pointer<typename Type::Scalar>::value,
|
||||||
|
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
using Matrix
|
using Matrix
|
||||||
= Eigen::Matrix<typename Type::Scalar, Type::RowsAtCompileTime, Type::ColsAtCompileTime>;
|
= Eigen::Matrix<typename Type::Scalar, Type::RowsAtCompileTime, Type::ColsAtCompileTime>;
|
||||||
@ -632,6 +643,8 @@ public:
|
|||||||
template <typename Type>
|
template <typename Type>
|
||||||
struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
||||||
using Scalar = typename Type::Scalar;
|
using Scalar = typename Type::Scalar;
|
||||||
|
static_assert(!std::is_pointer<Scalar>::value,
|
||||||
|
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||||
using StorageIndex = remove_reference_t<decltype(*std::declval<Type>().outerIndexPtr())>;
|
using StorageIndex = remove_reference_t<decltype(*std::declval<Type>().outerIndexPtr())>;
|
||||||
using Index = typename Type::Index;
|
using Index = typename Type::Index;
|
||||||
static constexpr bool rowMajor = Type::IsRowMajor;
|
static constexpr bool rowMajor = Type::IsRowMajor;
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../numpy.h"
|
#include "../numpy.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
|
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
|
||||||
static_assert(__GNUC__ > 5, "Eigen Tensor support in pybind11 requires GCC > 5.0");
|
static_assert(__GNUC__ > 5, "Eigen Tensor support in pybind11 requires GCC > 5.0");
|
||||||
@ -164,6 +165,8 @@ PYBIND11_WARNING_POP
|
|||||||
|
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
struct type_caster<Type, typename eigen_tensor_helper<Type>::ValidType> {
|
struct type_caster<Type, typename eigen_tensor_helper<Type>::ValidType> {
|
||||||
|
static_assert(!std::is_pointer<typename Type::Scalar>::value,
|
||||||
|
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||||
using Helper = eigen_tensor_helper<Type>;
|
using Helper = eigen_tensor_helper<Type>;
|
||||||
static constexpr auto temp_name = get_tensor_descriptor<Type, false>::value;
|
static constexpr auto temp_name = get_tensor_descriptor<Type, false>::value;
|
||||||
PYBIND11_TYPE_CASTER(Type, temp_name);
|
PYBIND11_TYPE_CASTER(Type, temp_name);
|
||||||
@ -359,6 +362,8 @@ struct get_storage_pointer_type<MapType, void_t<typename MapType::PointerArgType
|
|||||||
template <typename Type, int Options>
|
template <typename Type, int Options>
|
||||||
struct type_caster<Eigen::TensorMap<Type, Options>,
|
struct type_caster<Eigen::TensorMap<Type, Options>,
|
||||||
typename eigen_tensor_helper<remove_cv_t<Type>>::ValidType> {
|
typename eigen_tensor_helper<remove_cv_t<Type>>::ValidType> {
|
||||||
|
static_assert(!std::is_pointer<typename Type::Scalar>::value,
|
||||||
|
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||||
using MapType = Eigen::TensorMap<Type, Options>;
|
using MapType = Eigen::TensorMap<Type, Options>;
|
||||||
using Helper = eigen_tensor_helper<remove_cv_t<Type>>;
|
using Helper = eigen_tensor_helper<remove_cv_t<Type>>;
|
||||||
|
|
||||||
|
@ -198,9 +198,10 @@ inline void initialize_interpreter(bool init_signal_handlers = true,
|
|||||||
init_signal_handlers, argc, argv, add_program_dir_to_path);
|
init_signal_handlers, argc, argv, add_program_dir_to_path);
|
||||||
#else
|
#else
|
||||||
PyConfig config;
|
PyConfig config;
|
||||||
PyConfig_InitIsolatedConfig(&config);
|
PyConfig_InitPythonConfig(&config);
|
||||||
config.isolated = 0;
|
// See PR #4473 for background
|
||||||
config.use_environment = 1;
|
config.parse_argv = 0;
|
||||||
|
|
||||||
config.install_signal_handlers = init_signal_handlers ? 1 : 0;
|
config.install_signal_handlers = init_signal_handlers ? 1 : 0;
|
||||||
initialize_interpreter(&config, argc, argv, add_program_dir_to_path);
|
initialize_interpreter(&config, argc, argv, add_program_dir_to_path);
|
||||||
#endif
|
#endif
|
||||||
@ -242,16 +243,14 @@ inline void initialize_interpreter(bool init_signal_handlers = true,
|
|||||||
|
|
||||||
\endrst */
|
\endrst */
|
||||||
inline void finalize_interpreter() {
|
inline void finalize_interpreter() {
|
||||||
handle builtins(PyEval_GetBuiltins());
|
|
||||||
const char *id = PYBIND11_INTERNALS_ID;
|
|
||||||
|
|
||||||
// Get the internals pointer (without creating it if it doesn't exist). It's possible for the
|
// Get the internals pointer (without creating it if it doesn't exist). It's possible for the
|
||||||
// internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()`
|
// internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()`
|
||||||
// during destruction), so we get the pointer-pointer here and check it after Py_Finalize().
|
// during destruction), so we get the pointer-pointer here and check it after Py_Finalize().
|
||||||
detail::internals **internals_ptr_ptr = detail::get_internals_pp();
|
detail::internals **internals_ptr_ptr = detail::get_internals_pp();
|
||||||
// It could also be stashed in builtins, so look there too:
|
// It could also be stashed in state_dict, so look there too:
|
||||||
if (builtins.contains(id) && isinstance<capsule>(builtins[id])) {
|
if (object internals_obj
|
||||||
internals_ptr_ptr = capsule(builtins[id]);
|
= get_internals_obj_from_state_dict(detail::get_python_state_dict())) {
|
||||||
|
internals_ptr_ptr = detail::get_internals_pp_from_capsule(internals_obj);
|
||||||
}
|
}
|
||||||
// Local internals contains data managed by the current interpreter, so we must clear them to
|
// Local internals contains data managed by the current interpreter, so we must clear them to
|
||||||
// avoid undefined behaviors when initializing another interpreter
|
// avoid undefined behaviors when initializing another interpreter
|
||||||
|
@ -564,6 +564,8 @@ public:
|
|||||||
m_ptr = from_args(args).release().ptr();
|
m_ptr = from_args(args).release().ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return dtype for the given typenum (one of the NPY_TYPES).
|
||||||
|
/// https://numpy.org/devdocs/reference/c-api/array.html#c.PyArray_DescrFromType
|
||||||
explicit dtype(int typenum)
|
explicit dtype(int typenum)
|
||||||
: object(detail::npy_api::get().PyArray_DescrFromType_(typenum), stolen_t{}) {
|
: object(detail::npy_api::get().PyArray_DescrFromType_(typenum), stolen_t{}) {
|
||||||
if (m_ptr == nullptr) {
|
if (m_ptr == nullptr) {
|
||||||
@ -1283,12 +1285,16 @@ private:
|
|||||||
public:
|
public:
|
||||||
static constexpr int value = values[detail::is_fmt_numeric<T>::index];
|
static constexpr int value = values[detail::is_fmt_numeric<T>::index];
|
||||||
|
|
||||||
static pybind11::dtype dtype() {
|
static pybind11::dtype dtype() { return pybind11::dtype(/*typenum*/ value); }
|
||||||
if (auto *ptr = npy_api::get().PyArray_DescrFromType_(value)) {
|
};
|
||||||
return reinterpret_steal<pybind11::dtype>(ptr);
|
|
||||||
}
|
template <typename T>
|
||||||
pybind11_fail("Unsupported buffer format!");
|
struct npy_format_descriptor<T, enable_if_t<is_same_ignoring_cvref<T, PyObject *>::value>> {
|
||||||
}
|
static constexpr auto name = const_name("object");
|
||||||
|
|
||||||
|
static constexpr int value = npy_api::NPY_OBJECT_;
|
||||||
|
|
||||||
|
static pybind11::dtype dtype() { return pybind11::dtype(/*typenum*/ value); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#define PYBIND11_DECL_CHAR_FMT \
|
#define PYBIND11_DECL_CHAR_FMT \
|
||||||
|
@ -84,6 +84,7 @@ public:
|
|||||||
cpp_function() = default;
|
cpp_function() = default;
|
||||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||||
cpp_function(std::nullptr_t) {}
|
cpp_function(std::nullptr_t) {}
|
||||||
|
cpp_function(std::nullptr_t, const is_setter &) {}
|
||||||
|
|
||||||
/// Construct a cpp_function from a vanilla function pointer
|
/// Construct a cpp_function from a vanilla function pointer
|
||||||
template <typename Return, typename... Args, typename... Extra>
|
template <typename Return, typename... Args, typename... Extra>
|
||||||
@ -244,10 +245,16 @@ protected:
|
|||||||
using Guard = extract_guard_t<Extra...>;
|
using Guard = extract_guard_t<Extra...>;
|
||||||
|
|
||||||
/* Perform the function call */
|
/* Perform the function call */
|
||||||
handle result
|
handle result;
|
||||||
= cast_out::cast(std::move(args_converter).template call<Return, Guard>(cap->f),
|
if (call.func.is_setter) {
|
||||||
policy,
|
(void) std::move(args_converter).template call<Return, Guard>(cap->f);
|
||||||
call.parent);
|
result = none().release();
|
||||||
|
} else {
|
||||||
|
result = cast_out::cast(
|
||||||
|
std::move(args_converter).template call<Return, Guard>(cap->f),
|
||||||
|
policy,
|
||||||
|
call.parent);
|
||||||
|
}
|
||||||
|
|
||||||
/* Invoke call policy post-call hook */
|
/* Invoke call policy post-call hook */
|
||||||
process_attributes<Extra...>::postcall(call, result);
|
process_attributes<Extra...>::postcall(call, result);
|
||||||
@ -501,8 +508,8 @@ protected:
|
|||||||
rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS;
|
rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS;
|
||||||
|
|
||||||
capsule rec_capsule(unique_rec.release(),
|
capsule rec_capsule(unique_rec.release(),
|
||||||
|
detail::get_function_record_capsule_name(),
|
||||||
[](void *ptr) { destruct((detail::function_record *) ptr); });
|
[](void *ptr) { destruct((detail::function_record *) ptr); });
|
||||||
rec_capsule.set_name(detail::get_function_record_capsule_name());
|
|
||||||
guarded_strdup.release();
|
guarded_strdup.release();
|
||||||
|
|
||||||
object scope_module;
|
object scope_module;
|
||||||
@ -1729,7 +1736,8 @@ public:
|
|||||||
template <typename Getter, typename Setter, typename... Extra>
|
template <typename Getter, typename Setter, typename... Extra>
|
||||||
class_ &
|
class_ &
|
||||||
def_property(const char *name, const Getter &fget, const Setter &fset, const Extra &...extra) {
|
def_property(const char *name, const Getter &fget, const Setter &fset, const Extra &...extra) {
|
||||||
return def_property(name, fget, cpp_function(method_adaptor<type>(fset)), extra...);
|
return def_property(
|
||||||
|
name, fget, cpp_function(method_adaptor<type>(fset), is_setter()), extra...);
|
||||||
}
|
}
|
||||||
template <typename Getter, typename... Extra>
|
template <typename Getter, typename... Extra>
|
||||||
class_ &def_property(const char *name,
|
class_ &def_property(const char *name,
|
||||||
|
@ -471,13 +471,24 @@ inline const char *obj_class_name(PyObject *obj) {
|
|||||||
|
|
||||||
std::string error_string();
|
std::string error_string();
|
||||||
|
|
||||||
|
// The code in this struct is very unusual, to minimize the chances of
|
||||||
|
// masking bugs (elsewhere) by errors during the error handling (here).
|
||||||
|
// This is meant to be a lifeline for troubleshooting long-running processes
|
||||||
|
// that crash under conditions that are virtually impossible to reproduce.
|
||||||
|
// Low-level implementation alternatives are preferred to higher-level ones
|
||||||
|
// that might raise cascading exceptions. Last-ditch-kind-of attempts are made
|
||||||
|
// to report as much of the original error as possible, even if there are
|
||||||
|
// secondary issues obtaining some of the details.
|
||||||
struct error_fetch_and_normalize {
|
struct error_fetch_and_normalize {
|
||||||
// Immediate normalization is long-established behavior (starting with
|
// This comment only applies to Python <= 3.11:
|
||||||
// https://github.com/pybind/pybind11/commit/135ba8deafb8bf64a15b24d1513899eb600e2011
|
// Immediate normalization is long-established behavior (starting with
|
||||||
// from Sep 2016) and safest. Normalization could be deferred, but this could mask
|
// https://github.com/pybind/pybind11/commit/135ba8deafb8bf64a15b24d1513899eb600e2011
|
||||||
// errors elsewhere, the performance gain is very minor in typical situations
|
// from Sep 2016) and safest. Normalization could be deferred, but this could mask
|
||||||
// (usually the dominant bottleneck is EH unwinding), and the implementation here
|
// errors elsewhere, the performance gain is very minor in typical situations
|
||||||
// would be more complex.
|
// (usually the dominant bottleneck is EH unwinding), and the implementation here
|
||||||
|
// would be more complex.
|
||||||
|
// Starting with Python 3.12, PyErr_Fetch() normalizes exceptions immediately.
|
||||||
|
// Any errors during normalization are tracked under __notes__.
|
||||||
explicit error_fetch_and_normalize(const char *called) {
|
explicit error_fetch_and_normalize(const char *called) {
|
||||||
PyErr_Fetch(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr());
|
PyErr_Fetch(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr());
|
||||||
if (!m_type) {
|
if (!m_type) {
|
||||||
@ -492,6 +503,14 @@ struct error_fetch_and_normalize {
|
|||||||
"of the original active exception type.");
|
"of the original active exception type.");
|
||||||
}
|
}
|
||||||
m_lazy_error_string = exc_type_name_orig;
|
m_lazy_error_string = exc_type_name_orig;
|
||||||
|
#if PY_VERSION_HEX >= 0x030C0000
|
||||||
|
// The presence of __notes__ is likely due to exception normalization
|
||||||
|
// errors, although that is not necessarily true, therefore insert a
|
||||||
|
// hint only:
|
||||||
|
if (PyObject_HasAttrString(m_value.ptr(), "__notes__")) {
|
||||||
|
m_lazy_error_string += "[WITH __notes__]";
|
||||||
|
}
|
||||||
|
#else
|
||||||
// PyErr_NormalizeException() may change the exception type if there are cascading
|
// PyErr_NormalizeException() may change the exception type if there are cascading
|
||||||
// failures. This can potentially be extremely confusing.
|
// failures. This can potentially be extremely confusing.
|
||||||
PyErr_NormalizeException(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr());
|
PyErr_NormalizeException(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr());
|
||||||
@ -506,12 +525,12 @@ struct error_fetch_and_normalize {
|
|||||||
+ " failed to obtain the name "
|
+ " failed to obtain the name "
|
||||||
"of the normalized active exception type.");
|
"of the normalized active exception type.");
|
||||||
}
|
}
|
||||||
#if defined(PYPY_VERSION_NUM) && PYPY_VERSION_NUM < 0x07030a00
|
# if defined(PYPY_VERSION_NUM) && PYPY_VERSION_NUM < 0x07030a00
|
||||||
// This behavior runs the risk of masking errors in the error handling, but avoids a
|
// This behavior runs the risk of masking errors in the error handling, but avoids a
|
||||||
// conflict with PyPy, which relies on the normalization here to change OSError to
|
// conflict with PyPy, which relies on the normalization here to change OSError to
|
||||||
// FileNotFoundError (https://github.com/pybind/pybind11/issues/4075).
|
// FileNotFoundError (https://github.com/pybind/pybind11/issues/4075).
|
||||||
m_lazy_error_string = exc_type_name_norm;
|
m_lazy_error_string = exc_type_name_norm;
|
||||||
#else
|
# else
|
||||||
if (exc_type_name_norm != m_lazy_error_string) {
|
if (exc_type_name_norm != m_lazy_error_string) {
|
||||||
std::string msg = std::string(called)
|
std::string msg = std::string(called)
|
||||||
+ ": MISMATCH of original and normalized "
|
+ ": MISMATCH of original and normalized "
|
||||||
@ -523,6 +542,7 @@ struct error_fetch_and_normalize {
|
|||||||
msg += ": " + format_value_and_trace();
|
msg += ": " + format_value_and_trace();
|
||||||
pybind11_fail(msg);
|
pybind11_fail(msg);
|
||||||
}
|
}
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -558,6 +578,40 @@ struct error_fetch_and_normalize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if PY_VERSION_HEX >= 0x030B0000
|
||||||
|
auto notes
|
||||||
|
= reinterpret_steal<object>(PyObject_GetAttrString(m_value.ptr(), "__notes__"));
|
||||||
|
if (!notes) {
|
||||||
|
PyErr_Clear(); // No notes is good news.
|
||||||
|
} else {
|
||||||
|
auto len_notes = PyList_Size(notes.ptr());
|
||||||
|
if (len_notes < 0) {
|
||||||
|
result += "\nFAILURE obtaining len(__notes__): " + detail::error_string();
|
||||||
|
} else {
|
||||||
|
result += "\n__notes__ (len=" + std::to_string(len_notes) + "):";
|
||||||
|
for (ssize_t i = 0; i < len_notes; i++) {
|
||||||
|
PyObject *note = PyList_GET_ITEM(notes.ptr(), i);
|
||||||
|
auto note_bytes = reinterpret_steal<object>(
|
||||||
|
PyUnicode_AsEncodedString(note, "utf-8", "backslashreplace"));
|
||||||
|
if (!note_bytes) {
|
||||||
|
result += "\nFAILURE obtaining __notes__[" + std::to_string(i)
|
||||||
|
+ "]: " + detail::error_string();
|
||||||
|
} else {
|
||||||
|
char *buffer = nullptr;
|
||||||
|
Py_ssize_t length = 0;
|
||||||
|
if (PyBytes_AsStringAndSize(note_bytes.ptr(), &buffer, &length)
|
||||||
|
== -1) {
|
||||||
|
result += "\nFAILURE formatting __notes__[" + std::to_string(i)
|
||||||
|
+ "]: " + detail::error_string();
|
||||||
|
} else {
|
||||||
|
result += '\n';
|
||||||
|
result += std::string(buffer, static_cast<std::size_t>(length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
result = "<MESSAGE UNAVAILABLE>";
|
result = "<MESSAGE UNAVAILABLE>";
|
||||||
}
|
}
|
||||||
@ -1871,28 +1925,13 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Capsule name is nullptr.
|
||||||
capsule(const void *value, void (*destructor)(void *)) {
|
capsule(const void *value, void (*destructor)(void *)) {
|
||||||
m_ptr = PyCapsule_New(const_cast<void *>(value), nullptr, [](PyObject *o) {
|
initialize_with_void_ptr_destructor(value, nullptr, destructor);
|
||||||
// guard if destructor called while err indicator is set
|
}
|
||||||
error_scope error_guard;
|
|
||||||
auto destructor = reinterpret_cast<void (*)(void *)>(PyCapsule_GetContext(o));
|
|
||||||
if (destructor == nullptr && PyErr_Occurred()) {
|
|
||||||
throw error_already_set();
|
|
||||||
}
|
|
||||||
const char *name = get_name_in_error_scope(o);
|
|
||||||
void *ptr = PyCapsule_GetPointer(o, name);
|
|
||||||
if (ptr == nullptr) {
|
|
||||||
throw error_already_set();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (destructor != nullptr) {
|
capsule(const void *value, const char *name, void (*destructor)(void *)) {
|
||||||
destructor(ptr);
|
initialize_with_void_ptr_destructor(value, name, destructor);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!m_ptr || PyCapsule_SetContext(m_ptr, reinterpret_cast<void *>(destructor)) != 0) {
|
|
||||||
throw error_already_set();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit capsule(void (*destructor)()) {
|
explicit capsule(void (*destructor)()) {
|
||||||
@ -1960,6 +1999,32 @@ private:
|
|||||||
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void initialize_with_void_ptr_destructor(const void *value,
|
||||||
|
const char *name,
|
||||||
|
void (*destructor)(void *)) {
|
||||||
|
m_ptr = PyCapsule_New(const_cast<void *>(value), name, [](PyObject *o) {
|
||||||
|
// guard if destructor called while err indicator is set
|
||||||
|
error_scope error_guard;
|
||||||
|
auto destructor = reinterpret_cast<void (*)(void *)>(PyCapsule_GetContext(o));
|
||||||
|
if (destructor == nullptr && PyErr_Occurred()) {
|
||||||
|
throw error_already_set();
|
||||||
|
}
|
||||||
|
const char *name = get_name_in_error_scope(o);
|
||||||
|
void *ptr = PyCapsule_GetPointer(o, name);
|
||||||
|
if (ptr == nullptr) {
|
||||||
|
throw error_already_set();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destructor != nullptr) {
|
||||||
|
destructor(ptr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!m_ptr || PyCapsule_SetContext(m_ptr, reinterpret_cast<void *>(destructor)) != 0) {
|
||||||
|
throw error_already_set();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class tuple : public object {
|
class tuple : public object {
|
||||||
|
@ -273,11 +273,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
PYBIND11_TYPE_CASTER(ArrayType,
|
PYBIND11_TYPE_CASTER(ArrayType,
|
||||||
const_name("List[") + value_conv::name
|
const_name<Resizable>(const_name(""), const_name("Annotated["))
|
||||||
|
+ const_name("List[") + value_conv::name + const_name("]")
|
||||||
+ const_name<Resizable>(const_name(""),
|
+ const_name<Resizable>(const_name(""),
|
||||||
const_name("[") + const_name<Size>()
|
const_name(", FixedSize(")
|
||||||
+ const_name("]"))
|
+ const_name<Size>() + const_name(")]")));
|
||||||
+ const_name("]"));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Type, size_t Size>
|
template <typename Type, size_t Size>
|
||||||
@ -316,6 +316,7 @@ struct optional_caster {
|
|||||||
if (!std::is_lvalue_reference<T>::value) {
|
if (!std::is_lvalue_reference<T>::value) {
|
||||||
policy = return_value_policy_override<Value>::policy(policy);
|
policy = return_value_policy_override<Value>::policy(policy);
|
||||||
}
|
}
|
||||||
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
||||||
return value_conv::cast(*std::forward<T>(src), policy, parent);
|
return value_conv::cast(*std::forward<T>(src), policy, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,9 +61,11 @@ struct is_comparable<
|
|||||||
/* For a vector/map data structure, recursively check the value type
|
/* For a vector/map data structure, recursively check the value type
|
||||||
(which is std::pair for maps) */
|
(which is std::pair for maps) */
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct is_comparable<T, enable_if_t<container_traits<T>::is_vector>> {
|
struct is_comparable<T, enable_if_t<container_traits<T>::is_vector>>
|
||||||
static constexpr const bool value = is_comparable<typename T::value_type>::value;
|
: is_comparable<typename recursive_container_traits<T>::type_to_check_recursively> {};
|
||||||
};
|
|
||||||
|
template <>
|
||||||
|
struct is_comparable<recursive_bottom> : std::true_type {};
|
||||||
|
|
||||||
/* For pairs, recursively check the two data types */
|
/* For pairs, recursively check the two data types */
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -355,13 +357,17 @@ void vector_accessor(enable_if_t<vector_needs_copy<Vector>::value, Class_> &cl)
|
|||||||
using DiffType = typename Vector::difference_type;
|
using DiffType = typename Vector::difference_type;
|
||||||
using ItType = typename Vector::iterator;
|
using ItType = typename Vector::iterator;
|
||||||
cl.def("__getitem__", [](const Vector &v, DiffType i) -> T {
|
cl.def("__getitem__", [](const Vector &v, DiffType i) -> T {
|
||||||
if (i < 0 && (i += v.size()) < 0) {
|
if (i < 0) {
|
||||||
|
i += v.size();
|
||||||
|
if (i < 0) {
|
||||||
|
throw index_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto i_st = static_cast<SizeType>(i);
|
||||||
|
if (i_st >= v.size()) {
|
||||||
throw index_error();
|
throw index_error();
|
||||||
}
|
}
|
||||||
if ((SizeType) i >= v.size()) {
|
return v[i_st];
|
||||||
throw index_error();
|
|
||||||
}
|
|
||||||
return v[(SizeType) i];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
cl.def(
|
cl.def(
|
||||||
|
61
include/pybind11/type_caster_pyobject_ptr.h
Normal file
61
include/pybind11/type_caster_pyobject_ptr.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright (c) 2023 The pybind Community.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "detail/common.h"
|
||||||
|
#include "detail/descr.h"
|
||||||
|
#include "cast.h"
|
||||||
|
#include "pytypes.h"
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class type_caster<PyObject> {
|
||||||
|
public:
|
||||||
|
static constexpr auto name = const_name("object"); // See discussion under PR #4601.
|
||||||
|
|
||||||
|
// This overload is purely to guard against accidents.
|
||||||
|
template <typename T,
|
||||||
|
detail::enable_if_t<!is_same_ignoring_cvref<T, PyObject *>::value, int> = 0>
|
||||||
|
static handle cast(T &&, return_value_policy, handle /*parent*/) {
|
||||||
|
static_assert(is_same_ignoring_cvref<T, PyObject *>::value,
|
||||||
|
"Invalid C++ type T for to-Python conversion (type_caster<PyObject>).");
|
||||||
|
return nullptr; // Unreachable.
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle cast(PyObject *src, return_value_policy policy, handle /*parent*/) {
|
||||||
|
if (src == nullptr) {
|
||||||
|
throw error_already_set();
|
||||||
|
}
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
raise_from(PyExc_SystemError, "src != nullptr but PyErr_Occurred()");
|
||||||
|
throw error_already_set();
|
||||||
|
}
|
||||||
|
if (policy == return_value_policy::take_ownership) {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
if (policy == return_value_policy::reference
|
||||||
|
|| policy == return_value_policy::automatic_reference) {
|
||||||
|
return handle(src).inc_ref();
|
||||||
|
}
|
||||||
|
pybind11_fail("type_caster<PyObject>::cast(): unsupported return_value_policy: "
|
||||||
|
+ std::to_string(static_cast<int>(policy)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load(handle src, bool) {
|
||||||
|
value = reinterpret_borrow<object>(src);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using cast_op_type = PyObject *;
|
||||||
|
|
||||||
|
explicit operator PyObject *() { return value.ptr(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
object value;
|
||||||
|
};
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
@ -1,6 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
if sys.version_info < (3, 6):
|
if sys.version_info < (3, 6): # noqa: UP036
|
||||||
msg = "pybind11 does not support Python < 3.6. 2.9 was the last release supporting Python 2.7 and 3.5."
|
msg = "pybind11 does not support Python < 3.6. 2.9 was the last release supporting Python 2.7 and 3.5."
|
||||||
raise ImportError(msg)
|
raise ImportError(msg)
|
||||||
|
|
||||||
|
@ -8,5 +8,5 @@ def _to_int(s: str) -> Union[int, str]:
|
|||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
__version__ = "2.10.4"
|
__version__ = "2.11.0"
|
||||||
version_info = tuple(_to_int(s) for s in __version__.split("."))
|
version_info = tuple(_to_int(s) for s in __version__.split("."))
|
||||||
|
@ -3,7 +3,7 @@ import os
|
|||||||
DIR = os.path.abspath(os.path.dirname(__file__))
|
DIR = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
|
||||||
def get_include(user: bool = False) -> str: # pylint: disable=unused-argument
|
def get_include(user: bool = False) -> str: # noqa: ARG001
|
||||||
"""
|
"""
|
||||||
Return the path to the pybind11 include directory. The historical "user"
|
Return the path to the pybind11 include directory. The historical "user"
|
||||||
argument is unused, and may be removed.
|
argument is unused, and may be removed.
|
||||||
|
@ -66,8 +66,8 @@ try:
|
|||||||
from setuptools import Extension as _Extension
|
from setuptools import Extension as _Extension
|
||||||
from setuptools.command.build_ext import build_ext as _build_ext
|
from setuptools.command.build_ext import build_ext as _build_ext
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from distutils.command.build_ext import build_ext as _build_ext
|
from distutils.command.build_ext import build_ext as _build_ext # type: ignore[assignment]
|
||||||
from distutils.extension import Extension as _Extension
|
from distutils.extension import Extension as _Extension # type: ignore[assignment]
|
||||||
|
|
||||||
import distutils.ccompiler
|
import distutils.ccompiler
|
||||||
import distutils.errors
|
import distutils.errors
|
||||||
@ -84,7 +84,7 @@ STD_TMPL = "/std:c++{}" if WIN else "-std=c++{}"
|
|||||||
# directory into your path if it sits beside your setup.py.
|
# directory into your path if it sits beside your setup.py.
|
||||||
|
|
||||||
|
|
||||||
class Pybind11Extension(_Extension): # type: ignore[misc]
|
class Pybind11Extension(_Extension):
|
||||||
"""
|
"""
|
||||||
Build a C++11+ Extension module with pybind11. This automatically adds the
|
Build a C++11+ Extension module with pybind11. This automatically adds the
|
||||||
recommended flags when you init the extension and assumes C++ sources - you
|
recommended flags when you init the extension and assumes C++ sources - you
|
||||||
@ -144,7 +144,6 @@ class Pybind11Extension(_Extension): # type: ignore[misc]
|
|||||||
self.cxx_std = cxx_std
|
self.cxx_std = cxx_std
|
||||||
|
|
||||||
cflags = []
|
cflags = []
|
||||||
ldflags = []
|
|
||||||
if WIN:
|
if WIN:
|
||||||
cflags += ["/EHsc", "/bigobj"]
|
cflags += ["/EHsc", "/bigobj"]
|
||||||
else:
|
else:
|
||||||
@ -154,11 +153,7 @@ class Pybind11Extension(_Extension): # type: ignore[misc]
|
|||||||
c_cpp_flags = shlex.split(env_cflags) + shlex.split(env_cppflags)
|
c_cpp_flags = shlex.split(env_cflags) + shlex.split(env_cppflags)
|
||||||
if not any(opt.startswith("-g") for opt in c_cpp_flags):
|
if not any(opt.startswith("-g") for opt in c_cpp_flags):
|
||||||
cflags += ["-g0"]
|
cflags += ["-g0"]
|
||||||
if MACOS:
|
|
||||||
cflags += ["-stdlib=libc++"]
|
|
||||||
ldflags += ["-stdlib=libc++"]
|
|
||||||
self._add_cflags(cflags)
|
self._add_cflags(cflags)
|
||||||
self._add_ldflags(ldflags)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cxx_std(self) -> int:
|
def cxx_std(self) -> int:
|
||||||
@ -271,7 +266,7 @@ def auto_cpp_level(compiler: Any) -> Union[str, int]:
|
|||||||
raise RuntimeError(msg)
|
raise RuntimeError(msg)
|
||||||
|
|
||||||
|
|
||||||
class build_ext(_build_ext): # type: ignore[misc] # noqa: N801
|
class build_ext(_build_ext): # noqa: N801
|
||||||
"""
|
"""
|
||||||
Customized build_ext that allows an auto-search for the highest supported
|
Customized build_ext that allows an auto-search for the highest supported
|
||||||
C++ level for Pybind11Extension. This is only needed for the auto-search
|
C++ level for Pybind11Extension. This is only needed for the auto-search
|
||||||
@ -341,7 +336,7 @@ def naive_recompile(obj: str, src: str) -> bool:
|
|||||||
return os.stat(obj).st_mtime < os.stat(src).st_mtime
|
return os.stat(obj).st_mtime < os.stat(src).st_mtime
|
||||||
|
|
||||||
|
|
||||||
def no_recompile(obg: str, src: str) -> bool: # pylint: disable=unused-argument
|
def no_recompile(obg: str, src: str) -> bool: # noqa: ARG001
|
||||||
"""
|
"""
|
||||||
This is the safest but slowest choice (and is the default) - will always
|
This is the safest but slowest choice (and is the default) - will always
|
||||||
recompile sources.
|
recompile sources.
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
requires = ["setuptools>=42", "cmake>=3.18", "ninja"]
|
requires = ["setuptools>=42", "cmake>=3.18", "ninja"]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
|
||||||
[tool.check-manifest]
|
[tool.check-manifest]
|
||||||
ignore = [
|
ignore = [
|
||||||
"tests/**",
|
"tests/**",
|
||||||
@ -15,11 +16,6 @@ ignore = [
|
|||||||
"noxfile.py",
|
"noxfile.py",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.isort]
|
|
||||||
# Needs the compiled .so modules and env.py from tests
|
|
||||||
known_first_party = "env,pybind11_cross_module_tests,pybind11_tests,"
|
|
||||||
# For black compatibility
|
|
||||||
profile = "black"
|
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
files = ["pybind11"]
|
files = ["pybind11"]
|
||||||
@ -30,7 +26,7 @@ enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
|
|||||||
warn_unreachable = true
|
warn_unreachable = true
|
||||||
|
|
||||||
[[tool.mypy.overrides]]
|
[[tool.mypy.overrides]]
|
||||||
module = ["ghapi.*", "setuptools.*"]
|
module = ["ghapi.*"]
|
||||||
ignore_missing_imports = true
|
ignore_missing_imports = true
|
||||||
|
|
||||||
|
|
||||||
@ -58,4 +54,45 @@ messages_control.disable = [
|
|||||||
"invalid-name",
|
"invalid-name",
|
||||||
"protected-access",
|
"protected-access",
|
||||||
"missing-module-docstring",
|
"missing-module-docstring",
|
||||||
|
"unused-argument", # covered by Ruff ARG
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
select = [
|
||||||
|
"E", "F", "W", # flake8
|
||||||
|
"B", # flake8-bugbear
|
||||||
|
"I", # isort
|
||||||
|
"N", # pep8-naming
|
||||||
|
"ARG", # flake8-unused-arguments
|
||||||
|
"C4", # flake8-comprehensions
|
||||||
|
"EM", # flake8-errmsg
|
||||||
|
"ICN", # flake8-import-conventions
|
||||||
|
"ISC", # flake8-implicit-str-concat
|
||||||
|
"PGH", # pygrep-hooks
|
||||||
|
"PIE", # flake8-pie
|
||||||
|
"PL", # pylint
|
||||||
|
"PT", # flake8-pytest-style
|
||||||
|
"RET", # flake8-return
|
||||||
|
"RUF100", # Ruff-specific
|
||||||
|
"SIM", # flake8-simplify
|
||||||
|
"UP", # pyupgrade
|
||||||
|
"YTT", # flake8-2020
|
||||||
|
]
|
||||||
|
ignore = [
|
||||||
|
"PLR", # Design related pylint
|
||||||
|
"E501", # Line too long (Black is enough)
|
||||||
|
"PT011", # Too broad with raises in pytest
|
||||||
|
"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"]
|
||||||
|
"tests/test_call_policies.py" = ["PLC1901"]
|
||||||
|
@ -20,6 +20,7 @@ classifiers =
|
|||||||
Programming Language :: Python :: 3.9
|
Programming Language :: Python :: 3.9
|
||||||
Programming Language :: Python :: 3.10
|
Programming Language :: Python :: 3.10
|
||||||
Programming Language :: Python :: 3.11
|
Programming Language :: Python :: 3.11
|
||||||
|
Programming Language :: Python :: 3.12
|
||||||
License :: OSI Approved :: BSD License
|
License :: OSI Approved :: BSD License
|
||||||
Programming Language :: Python :: Implementation :: PyPy
|
Programming Language :: Python :: Implementation :: PyPy
|
||||||
Programming Language :: Python :: Implementation :: CPython
|
Programming Language :: Python :: Implementation :: CPython
|
||||||
@ -40,11 +41,3 @@ project_urls =
|
|||||||
[options]
|
[options]
|
||||||
python_requires = >=3.6
|
python_requires = >=3.6
|
||||||
zip_safe = False
|
zip_safe = False
|
||||||
|
|
||||||
|
|
||||||
[flake8]
|
|
||||||
max-line-length = 120
|
|
||||||
show_source = True
|
|
||||||
exclude = .git, __pycache__, build, dist, docs, tools, venv
|
|
||||||
extend-ignore = E203, E722
|
|
||||||
extend-select = B902, B904
|
|
||||||
|
2
setup.py
2
setup.py
@ -96,7 +96,7 @@ def get_and_replace(
|
|||||||
|
|
||||||
# Use our input files instead when making the SDist (and anything that depends
|
# Use our input files instead when making the SDist (and anything that depends
|
||||||
# on it, like a wheel)
|
# on it, like a wheel)
|
||||||
class SDist(setuptools.command.sdist.sdist): # type: ignore[misc]
|
class SDist(setuptools.command.sdist.sdist):
|
||||||
def make_release_tree(self, base_dir: str, files: List[str]) -> None:
|
def make_release_tree(self, base_dir: str, files: List[str]) -> None:
|
||||||
super().make_release_tree(base_dir, files)
|
super().make_release_tree(base_dir, files)
|
||||||
|
|
||||||
|
@ -5,20 +5,17 @@
|
|||||||
# All rights reserved. Use of this source code is governed by a
|
# All rights reserved. Use of this source code is governed by a
|
||||||
# BSD-style license that can be found in the LICENSE file.
|
# BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.4)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
|
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
|
||||||
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
||||||
# the behavior using the following workaround:
|
# the behavior using the following workaround:
|
||||||
if(${CMAKE_VERSION} VERSION_LESS 3.21)
|
if(${CMAKE_VERSION} VERSION_LESS 3.26)
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||||
else()
|
else()
|
||||||
cmake_policy(VERSION 3.21)
|
cmake_policy(VERSION 3.26)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Only needed for CMake < 3.5 support
|
|
||||||
include(CMakeParseArguments)
|
|
||||||
|
|
||||||
# Filter out items; print an optional message if any items filtered. This ignores extensions.
|
# Filter out items; print an optional message if any items filtered. This ignores extensions.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
@ -154,7 +151,11 @@ set(PYBIND11_TEST_FILES
|
|||||||
test_stl_binders
|
test_stl_binders
|
||||||
test_tagbased_polymorphic
|
test_tagbased_polymorphic
|
||||||
test_thread
|
test_thread
|
||||||
|
test_type_caster_pyobject_ptr
|
||||||
test_union
|
test_union
|
||||||
|
test_unnamed_namespace_a
|
||||||
|
test_unnamed_namespace_b
|
||||||
|
test_vector_unique_ptr_member
|
||||||
test_virtual_functions)
|
test_virtual_functions)
|
||||||
|
|
||||||
# Invoking cmake with something like:
|
# Invoking cmake with something like:
|
||||||
|
@ -8,8 +8,8 @@ import contextlib
|
|||||||
import difflib
|
import difflib
|
||||||
import gc
|
import gc
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
@ -25,8 +25,9 @@ except Exception:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session", autouse=True)
|
@pytest.fixture(scope="session", autouse=True)
|
||||||
def always_forkserver_on_unix():
|
def use_multiprocessing_forkserver_on_linux():
|
||||||
if os.name == "nt":
|
if sys.platform != "linux":
|
||||||
|
# The default on Windows and macOS is "spawn": If it's not broken, don't fix it.
|
||||||
return
|
return
|
||||||
|
|
||||||
# Full background: https://github.com/pybind/pybind11/issues/4105#issuecomment-1301004592
|
# Full background: https://github.com/pybind/pybind11/issues/4105#issuecomment-1301004592
|
||||||
@ -34,8 +35,6 @@ def always_forkserver_on_unix():
|
|||||||
# It is actually a well-known pitfall, unfortunately without guard rails.
|
# It is actually a well-known pitfall, unfortunately without guard rails.
|
||||||
# "forkserver" is more performant than "spawn" (~9s vs ~13s for tests/test_gil_scoped.py,
|
# "forkserver" is more performant than "spawn" (~9s vs ~13s for tests/test_gil_scoped.py,
|
||||||
# visit the issuecomment link above for details).
|
# visit the issuecomment link above for details).
|
||||||
# Windows does not have fork() and the associated pitfall, therefore it is best left
|
|
||||||
# running with defaults.
|
|
||||||
multiprocessing.set_start_method("forkserver")
|
multiprocessing.set_start_method("forkserver")
|
||||||
|
|
||||||
|
|
||||||
@ -83,9 +82,8 @@ class Output:
|
|||||||
b = _strip_and_dedent(other).splitlines()
|
b = _strip_and_dedent(other).splitlines()
|
||||||
if a == b:
|
if a == b:
|
||||||
return True
|
return True
|
||||||
else:
|
self.explanation = _make_explanation(a, b)
|
||||||
self.explanation = _make_explanation(a, b)
|
return False
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class Unordered(Output):
|
class Unordered(Output):
|
||||||
@ -96,9 +94,8 @@ class Unordered(Output):
|
|||||||
b = _split_and_sort(other)
|
b = _split_and_sort(other)
|
||||||
if a == b:
|
if a == b:
|
||||||
return True
|
return True
|
||||||
else:
|
self.explanation = _make_explanation(a, b)
|
||||||
self.explanation = _make_explanation(a, b)
|
return False
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class Capture:
|
class Capture:
|
||||||
@ -119,9 +116,8 @@ class Capture:
|
|||||||
b = other
|
b = other
|
||||||
if a == b:
|
if a == b:
|
||||||
return True
|
return True
|
||||||
else:
|
self.explanation = a.explanation
|
||||||
self.explanation = a.explanation
|
return False
|
||||||
return False
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.out
|
return self.out
|
||||||
@ -138,7 +134,7 @@ class Capture:
|
|||||||
return Output(self.err)
|
return Output(self.err)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture()
|
||||||
def capture(capsys):
|
def capture(capsys):
|
||||||
"""Extended `capsys` with context manager and custom equality operators"""
|
"""Extended `capsys` with context manager and custom equality operators"""
|
||||||
return Capture(capsys)
|
return Capture(capsys)
|
||||||
@ -159,25 +155,22 @@ class SanitizedString:
|
|||||||
b = _strip_and_dedent(other)
|
b = _strip_and_dedent(other)
|
||||||
if a == b:
|
if a == b:
|
||||||
return True
|
return True
|
||||||
else:
|
self.explanation = _make_explanation(a.splitlines(), b.splitlines())
|
||||||
self.explanation = _make_explanation(a.splitlines(), b.splitlines())
|
return False
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _sanitize_general(s):
|
def _sanitize_general(s):
|
||||||
s = s.strip()
|
s = s.strip()
|
||||||
s = s.replace("pybind11_tests.", "m.")
|
s = s.replace("pybind11_tests.", "m.")
|
||||||
s = _long_marker.sub(r"\1", s)
|
return _long_marker.sub(r"\1", s)
|
||||||
return s
|
|
||||||
|
|
||||||
|
|
||||||
def _sanitize_docstring(thing):
|
def _sanitize_docstring(thing):
|
||||||
s = thing.__doc__
|
s = thing.__doc__
|
||||||
s = _sanitize_general(s)
|
return _sanitize_general(s)
|
||||||
return s
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture()
|
||||||
def doc():
|
def doc():
|
||||||
"""Sanitize docstrings and add custom failure explanation"""
|
"""Sanitize docstrings and add custom failure explanation"""
|
||||||
return SanitizedString(_sanitize_docstring)
|
return SanitizedString(_sanitize_docstring)
|
||||||
@ -186,30 +179,20 @@ def doc():
|
|||||||
def _sanitize_message(thing):
|
def _sanitize_message(thing):
|
||||||
s = str(thing)
|
s = str(thing)
|
||||||
s = _sanitize_general(s)
|
s = _sanitize_general(s)
|
||||||
s = _hexadecimal.sub("0", s)
|
return _hexadecimal.sub("0", s)
|
||||||
return s
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture()
|
||||||
def msg():
|
def msg():
|
||||||
"""Sanitize messages and add custom failure explanation"""
|
"""Sanitize messages and add custom failure explanation"""
|
||||||
return SanitizedString(_sanitize_message)
|
return SanitizedString(_sanitize_message)
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyUnusedLocal
|
def pytest_assertrepr_compare(op, left, right): # noqa: ARG001
|
||||||
def pytest_assertrepr_compare(op, left, right):
|
|
||||||
"""Hook to insert custom failure explanation"""
|
"""Hook to insert custom failure explanation"""
|
||||||
if hasattr(left, "explanation"):
|
if hasattr(left, "explanation"):
|
||||||
return left.explanation
|
return left.explanation
|
||||||
|
return None
|
||||||
|
|
||||||
@contextlib.contextmanager
|
|
||||||
def suppress(exception):
|
|
||||||
"""Suppress the desired exception"""
|
|
||||||
try:
|
|
||||||
yield
|
|
||||||
except exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def gc_collect():
|
def gc_collect():
|
||||||
@ -220,7 +203,7 @@ def gc_collect():
|
|||||||
|
|
||||||
|
|
||||||
def pytest_configure():
|
def pytest_configure():
|
||||||
pytest.suppress = suppress
|
pytest.suppress = contextlib.suppress
|
||||||
pytest.gc_collect = gc_collect
|
pytest.gc_collect = gc_collect
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,5 +24,4 @@ def deprecated_call():
|
|||||||
pytest_major_minor = (int(pieces[0]), int(pieces[1]))
|
pytest_major_minor = (int(pieces[0]), int(pieces[1]))
|
||||||
if pytest_major_minor < (3, 9):
|
if pytest_major_minor < (3, 9):
|
||||||
return pytest.warns((DeprecationWarning, PendingDeprecationWarning))
|
return pytest.warns((DeprecationWarning, PendingDeprecationWarning))
|
||||||
else:
|
return pytest.deprecated_call()
|
||||||
return pytest.deprecated_call()
|
|
||||||
|
@ -43,6 +43,7 @@ main_headers = {
|
|||||||
"include/pybind11/pytypes.h",
|
"include/pybind11/pytypes.h",
|
||||||
"include/pybind11/stl.h",
|
"include/pybind11/stl.h",
|
||||||
"include/pybind11/stl_bind.h",
|
"include/pybind11/stl_bind.h",
|
||||||
|
"include/pybind11/type_caster_pyobject_ptr.h",
|
||||||
}
|
}
|
||||||
|
|
||||||
detail_headers = {
|
detail_headers = {
|
||||||
@ -56,6 +57,7 @@ detail_headers = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
eigen_headers = {
|
eigen_headers = {
|
||||||
|
"include/pybind11/eigen/common.h",
|
||||||
"include/pybind11/eigen/matrix.h",
|
"include/pybind11/eigen/matrix.h",
|
||||||
"include/pybind11/eigen/tensor.h",
|
"include/pybind11/eigen/tensor.h",
|
||||||
}
|
}
|
||||||
@ -110,6 +112,7 @@ sdist_files = {
|
|||||||
"MANIFEST.in",
|
"MANIFEST.in",
|
||||||
"README.rst",
|
"README.rst",
|
||||||
"PKG-INFO",
|
"PKG-INFO",
|
||||||
|
"SECURITY.md",
|
||||||
}
|
}
|
||||||
|
|
||||||
local_sdist_files = {
|
local_sdist_files = {
|
||||||
|
@ -6,4 +6,4 @@ numpy==1.22.2; platform_python_implementation!="PyPy" and python_version>="3.10"
|
|||||||
pytest==7.0.0
|
pytest==7.0.0
|
||||||
pytest-timeout
|
pytest-timeout
|
||||||
scipy==1.5.4; 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.10.0; platform_python_implementation!="PyPy" and python_version=="3.10"
|
||||||
|
@ -4,7 +4,7 @@ asyncio = pytest.importorskip("asyncio")
|
|||||||
m = pytest.importorskip("pybind11_tests.async_module")
|
m = pytest.importorskip("pybind11_tests.async_module")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture()
|
||||||
def event_loop():
|
def event_loop():
|
||||||
loop = asyncio.new_event_loop()
|
loop = asyncio.new_event_loop()
|
||||||
yield loop
|
yield loop
|
||||||
@ -16,7 +16,7 @@ async def get_await_result(x):
|
|||||||
|
|
||||||
|
|
||||||
def test_await(event_loop):
|
def test_await(event_loop):
|
||||||
assert 5 == event_loop.run_until_complete(get_await_result(m.SupportsAsync()))
|
assert event_loop.run_until_complete(get_await_result(m.SupportsAsync())) == 5
|
||||||
|
|
||||||
|
|
||||||
def test_await_missing(event_loop):
|
def test_await_missing(event_loop):
|
||||||
|
@ -7,12 +7,47 @@
|
|||||||
BSD-style license that can be found in the LICENSE file.
|
BSD-style license that can be found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <pybind11/complex.h>
|
||||||
#include <pybind11/stl.h>
|
#include <pybind11/stl.h>
|
||||||
|
|
||||||
#include "constructor_stats.h"
|
#include "constructor_stats.h"
|
||||||
#include "pybind11_tests.h"
|
#include "pybind11_tests.h"
|
||||||
|
|
||||||
TEST_SUBMODULE(buffers, m) {
|
TEST_SUBMODULE(buffers, m) {
|
||||||
|
m.attr("long_double_and_double_have_same_size") = (sizeof(long double) == sizeof(double));
|
||||||
|
|
||||||
|
m.def("format_descriptor_format_buffer_info_equiv",
|
||||||
|
[](const std::string &cpp_name, const py::buffer &buffer) {
|
||||||
|
// https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables
|
||||||
|
static auto *format_table = new std::map<std::string, std::string>;
|
||||||
|
static auto *equiv_table
|
||||||
|
= new std::map<std::string, bool (py::buffer_info::*)() const>;
|
||||||
|
if (format_table->empty()) {
|
||||||
|
#define PYBIND11_ASSIGN_HELPER(...) \
|
||||||
|
(*format_table)[#__VA_ARGS__] = py::format_descriptor<__VA_ARGS__>::format(); \
|
||||||
|
(*equiv_table)[#__VA_ARGS__] = &py::buffer_info::item_type_is_equivalent_to<__VA_ARGS__>;
|
||||||
|
PYBIND11_ASSIGN_HELPER(PyObject *)
|
||||||
|
PYBIND11_ASSIGN_HELPER(bool)
|
||||||
|
PYBIND11_ASSIGN_HELPER(std::int8_t)
|
||||||
|
PYBIND11_ASSIGN_HELPER(std::uint8_t)
|
||||||
|
PYBIND11_ASSIGN_HELPER(std::int16_t)
|
||||||
|
PYBIND11_ASSIGN_HELPER(std::uint16_t)
|
||||||
|
PYBIND11_ASSIGN_HELPER(std::int32_t)
|
||||||
|
PYBIND11_ASSIGN_HELPER(std::uint32_t)
|
||||||
|
PYBIND11_ASSIGN_HELPER(std::int64_t)
|
||||||
|
PYBIND11_ASSIGN_HELPER(std::uint64_t)
|
||||||
|
PYBIND11_ASSIGN_HELPER(float)
|
||||||
|
PYBIND11_ASSIGN_HELPER(double)
|
||||||
|
PYBIND11_ASSIGN_HELPER(long double)
|
||||||
|
PYBIND11_ASSIGN_HELPER(std::complex<float>)
|
||||||
|
PYBIND11_ASSIGN_HELPER(std::complex<double>)
|
||||||
|
PYBIND11_ASSIGN_HELPER(std::complex<long double>)
|
||||||
|
#undef PYBIND11_ASSIGN_HELPER
|
||||||
|
}
|
||||||
|
return std::pair<std::string, bool>(
|
||||||
|
(*format_table)[cpp_name], (buffer.request().*((*equiv_table)[cpp_name]))());
|
||||||
|
});
|
||||||
|
|
||||||
// test_from_python / test_to_python:
|
// test_from_python / test_to_python:
|
||||||
class Matrix {
|
class Matrix {
|
||||||
public:
|
public:
|
||||||
|
@ -10,6 +10,63 @@ from pybind11_tests import buffers as m
|
|||||||
|
|
||||||
np = pytest.importorskip("numpy")
|
np = pytest.importorskip("numpy")
|
||||||
|
|
||||||
|
if m.long_double_and_double_have_same_size:
|
||||||
|
# Determined by the compiler used to build the pybind11 tests
|
||||||
|
# (e.g. MSVC gets here, but MinGW might not).
|
||||||
|
np_float128 = None
|
||||||
|
np_complex256 = None
|
||||||
|
else:
|
||||||
|
# Determined by the compiler used to build numpy (e.g. MinGW).
|
||||||
|
np_float128 = getattr(np, *["float128"] * 2)
|
||||||
|
np_complex256 = getattr(np, *["complex256"] * 2)
|
||||||
|
|
||||||
|
CPP_NAME_FORMAT_NP_DTYPE_TABLE = [
|
||||||
|
("PyObject *", "O", object),
|
||||||
|
("bool", "?", np.bool_),
|
||||||
|
("std::int8_t", "b", np.int8),
|
||||||
|
("std::uint8_t", "B", np.uint8),
|
||||||
|
("std::int16_t", "h", np.int16),
|
||||||
|
("std::uint16_t", "H", np.uint16),
|
||||||
|
("std::int32_t", "i", np.int32),
|
||||||
|
("std::uint32_t", "I", np.uint32),
|
||||||
|
("std::int64_t", "q", np.int64),
|
||||||
|
("std::uint64_t", "Q", np.uint64),
|
||||||
|
("float", "f", np.float32),
|
||||||
|
("double", "d", np.float64),
|
||||||
|
("long double", "g", np_float128),
|
||||||
|
("std::complex<float>", "Zf", np.complex64),
|
||||||
|
("std::complex<double>", "Zd", np.complex128),
|
||||||
|
("std::complex<long double>", "Zg", np_complex256),
|
||||||
|
]
|
||||||
|
CPP_NAME_FORMAT_TABLE = [
|
||||||
|
(cpp_name, format)
|
||||||
|
for cpp_name, format, np_dtype in CPP_NAME_FORMAT_NP_DTYPE_TABLE
|
||||||
|
if np_dtype is not None
|
||||||
|
]
|
||||||
|
CPP_NAME_NP_DTYPE_TABLE = [
|
||||||
|
(cpp_name, np_dtype) for cpp_name, _, np_dtype in CPP_NAME_FORMAT_NP_DTYPE_TABLE
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(("cpp_name", "np_dtype"), CPP_NAME_NP_DTYPE_TABLE)
|
||||||
|
def test_format_descriptor_format_buffer_info_equiv(cpp_name, np_dtype):
|
||||||
|
if np_dtype is None:
|
||||||
|
pytest.skip(
|
||||||
|
f"cpp_name=`{cpp_name}`: `long double` and `double` have same size."
|
||||||
|
)
|
||||||
|
if isinstance(np_dtype, str):
|
||||||
|
pytest.skip(f"np.{np_dtype} does not exist.")
|
||||||
|
np_array = np.array([], dtype=np_dtype)
|
||||||
|
for other_cpp_name, expected_format in CPP_NAME_FORMAT_TABLE:
|
||||||
|
format, np_array_is_matching = m.format_descriptor_format_buffer_info_equiv(
|
||||||
|
other_cpp_name, np_array
|
||||||
|
)
|
||||||
|
assert format == expected_format
|
||||||
|
if other_cpp_name == cpp_name:
|
||||||
|
assert np_array_is_matching
|
||||||
|
else:
|
||||||
|
assert not np_array_is_matching
|
||||||
|
|
||||||
|
|
||||||
def test_from_python():
|
def test_from_python():
|
||||||
with pytest.raises(RuntimeError) as excinfo:
|
with pytest.raises(RuntimeError) as excinfo:
|
||||||
@ -54,7 +111,8 @@ def test_to_python():
|
|||||||
mat2 = np.array(mat, copy=False)
|
mat2 = np.array(mat, copy=False)
|
||||||
assert mat2.shape == (5, 4)
|
assert mat2.shape == (5, 4)
|
||||||
assert abs(mat2).sum() == 11
|
assert abs(mat2).sum() == 11
|
||||||
assert mat2[2, 3] == 4 and mat2[3, 2] == 7
|
assert mat2[2, 3] == 4
|
||||||
|
assert mat2[3, 2] == 7
|
||||||
mat2[2, 3] = 5
|
mat2[2, 3] = 5
|
||||||
assert mat2[2, 3] == 5
|
assert mat2[2, 3] == 5
|
||||||
|
|
||||||
|
@ -126,8 +126,8 @@ def test_bytes_to_string():
|
|||||||
|
|
||||||
assert m.strlen(b"hi") == 2
|
assert m.strlen(b"hi") == 2
|
||||||
assert m.string_length(b"world") == 5
|
assert m.string_length(b"world") == 5
|
||||||
assert m.string_length("a\x00b".encode()) == 3
|
assert m.string_length(b"a\x00b") == 3
|
||||||
assert m.strlen("a\x00b".encode()) == 1 # C-string limitation
|
assert m.strlen(b"a\x00b") == 1 # C-string limitation
|
||||||
|
|
||||||
# passing in a utf8 encoded string should work
|
# passing in a utf8 encoded string should work
|
||||||
assert m.string_length("💩".encode()) == 4
|
assert m.string_length("💩".encode()) == 4
|
||||||
@ -421,13 +421,15 @@ def test_reference_wrapper():
|
|||||||
a2 = m.refwrap_list(copy=True)
|
a2 = m.refwrap_list(copy=True)
|
||||||
assert [x.value for x in a1] == [2, 3]
|
assert [x.value for x in a1] == [2, 3]
|
||||||
assert [x.value for x in a2] == [2, 3]
|
assert [x.value for x in a2] == [2, 3]
|
||||||
assert not a1[0] is a2[0] and not a1[1] is a2[1]
|
assert a1[0] is not a2[0]
|
||||||
|
assert a1[1] is not a2[1]
|
||||||
|
|
||||||
b1 = m.refwrap_list(copy=False)
|
b1 = m.refwrap_list(copy=False)
|
||||||
b2 = m.refwrap_list(copy=False)
|
b2 = m.refwrap_list(copy=False)
|
||||||
assert [x.value for x in b1] == [1, 2]
|
assert [x.value for x in b1] == [1, 2]
|
||||||
assert [x.value for x in b2] == [1, 2]
|
assert [x.value for x in b2] == [1, 2]
|
||||||
assert b1[0] is b2[0] and b1[1] is b2[1]
|
assert b1[0] is b2[0]
|
||||||
|
assert b1[1] is b2[1]
|
||||||
|
|
||||||
assert m.refwrap_iiw(IncType(5)) == 5
|
assert m.refwrap_iiw(IncType(5)) == 5
|
||||||
assert m.refwrap_call_iiw(IncType(10), m.refwrap_iiw) == [10, 10, 10, 10]
|
assert m.refwrap_call_iiw(IncType(10), m.refwrap_iiw) == [10, 10, 10, 10]
|
||||||
|
@ -5,6 +5,7 @@ import pytest
|
|||||||
|
|
||||||
import env # noqa: F401
|
import env # noqa: F401
|
||||||
from pybind11_tests import callbacks as m
|
from pybind11_tests import callbacks as m
|
||||||
|
from pybind11_tests import detailed_error_messages_enabled
|
||||||
|
|
||||||
|
|
||||||
def test_callbacks():
|
def test_callbacks():
|
||||||
@ -70,11 +71,20 @@ def test_keyword_args_and_generalized_unpacking():
|
|||||||
|
|
||||||
with pytest.raises(RuntimeError) as excinfo:
|
with pytest.raises(RuntimeError) as excinfo:
|
||||||
m.test_arg_conversion_error1(f)
|
m.test_arg_conversion_error1(f)
|
||||||
assert "Unable to convert call argument" in str(excinfo.value)
|
assert str(excinfo.value) == "Unable to convert call argument " + (
|
||||||
|
"'1' of type 'UnregisteredType' to Python object"
|
||||||
|
if detailed_error_messages_enabled
|
||||||
|
else "'1' to Python object (#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)"
|
||||||
|
)
|
||||||
|
|
||||||
with pytest.raises(RuntimeError) as excinfo:
|
with pytest.raises(RuntimeError) as excinfo:
|
||||||
m.test_arg_conversion_error2(f)
|
m.test_arg_conversion_error2(f)
|
||||||
assert "Unable to convert call argument" in str(excinfo.value)
|
assert str(excinfo.value) == "Unable to convert call argument " + (
|
||||||
|
"'expected_name' of type 'UnregisteredType' to Python object"
|
||||||
|
if detailed_error_messages_enabled
|
||||||
|
else "'expected_name' to Python object "
|
||||||
|
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_lambda_closure_cleanup():
|
def test_lambda_closure_cleanup():
|
||||||
|
@ -85,7 +85,7 @@ TEST_SUBMODULE(class_, m) {
|
|||||||
.def_static("new_instance", &NoConstructor::new_instance, "Return an instance");
|
.def_static("new_instance", &NoConstructor::new_instance, "Return an instance");
|
||||||
|
|
||||||
py::class_<NoConstructorNew>(m, "NoConstructorNew")
|
py::class_<NoConstructorNew>(m, "NoConstructorNew")
|
||||||
.def(py::init([](const NoConstructorNew &self) { return self; })) // Need a NOOP __init__
|
.def(py::init([]() { return nullptr; })) // Need a NOOP __init__
|
||||||
.def_static("__new__",
|
.def_static("__new__",
|
||||||
[](const py::object &) { return NoConstructorNew::new_instance(); });
|
[](const py::object &) { return NoConstructorNew::new_instance(); });
|
||||||
|
|
||||||
|
@ -6,10 +6,7 @@ from pybind11_tests import class_ as m
|
|||||||
|
|
||||||
|
|
||||||
def test_obj_class_name():
|
def test_obj_class_name():
|
||||||
if env.PYPY:
|
expected_name = "UserType" if env.PYPY else "pybind11_tests.UserType"
|
||||||
expected_name = "UserType"
|
|
||||||
else:
|
|
||||||
expected_name = "pybind11_tests.UserType"
|
|
||||||
assert m.obj_class_name(UserType(1)) == expected_name
|
assert m.obj_class_name(UserType(1)) == expected_name
|
||||||
assert m.obj_class_name(UserType) == expected_name
|
assert m.obj_class_name(UserType) == expected_name
|
||||||
|
|
||||||
@ -32,7 +29,7 @@ def test_instance(msg):
|
|||||||
assert cstats.alive() == 0
|
assert cstats.alive() == 0
|
||||||
|
|
||||||
|
|
||||||
def test_instance_new(msg):
|
def test_instance_new():
|
||||||
instance = m.NoConstructorNew() # .__new__(m.NoConstructor.__class__)
|
instance = m.NoConstructorNew() # .__new__(m.NoConstructor.__class__)
|
||||||
cstats = ConstructorStats.get(m.NoConstructorNew)
|
cstats = ConstructorStats.get(m.NoConstructorNew)
|
||||||
assert cstats.alive() == 1
|
assert cstats.alive() == 1
|
||||||
@ -221,7 +218,7 @@ def test_automatic_upcasting():
|
|||||||
|
|
||||||
|
|
||||||
def test_isinstance():
|
def test_isinstance():
|
||||||
objects = [tuple(), dict(), m.Pet("Polly", "parrot")] + [m.Dog("Molly")] * 4
|
objects = [(), {}, m.Pet("Polly", "parrot")] + [m.Dog("Molly")] * 4
|
||||||
expected = (True, True, True, True, True, False, False)
|
expected = (True, True, True, True, True, False, False)
|
||||||
assert m.check_instances(objects) == expected
|
assert m.check_instances(objects) == expected
|
||||||
|
|
||||||
@ -427,7 +424,7 @@ def test_exception_rvalue_abort():
|
|||||||
|
|
||||||
|
|
||||||
# https://github.com/pybind/pybind11/issues/1568
|
# https://github.com/pybind/pybind11/issues/1568
|
||||||
def test_multiple_instances_with_same_pointer(capture):
|
def test_multiple_instances_with_same_pointer():
|
||||||
n = 100
|
n = 100
|
||||||
instances = [m.SamePointer() for _ in range(n)]
|
instances = [m.SamePointer() for _ in range(n)]
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
# Built-in in CMake 3.5+
|
|
||||||
include(CMakeParseArguments)
|
|
||||||
|
|
||||||
add_custom_target(test_cmake_build)
|
add_custom_target(test_cmake_build)
|
||||||
|
|
||||||
function(pybind11_add_build_test name)
|
function(pybind11_add_build_test name)
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
cmake_minimum_required(VERSION 3.4)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
|
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
|
||||||
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
||||||
# the behavior using the following workaround:
|
# the behavior using the following workaround:
|
||||||
if(${CMAKE_VERSION} VERSION_LESS 3.18)
|
if(${CMAKE_VERSION} VERSION_LESS 3.26)
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||||
else()
|
else()
|
||||||
cmake_policy(VERSION 3.18)
|
cmake_policy(VERSION 3.26)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(test_installed_embed CXX)
|
project(test_installed_embed CXX)
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
cmake_minimum_required(VERSION 3.4)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
project(test_installed_module CXX)
|
project(test_installed_module CXX)
|
||||||
|
|
||||||
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
|
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
|
||||||
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
||||||
# the behavior using the following workaround:
|
# the behavior using the following workaround:
|
||||||
if(${CMAKE_VERSION} VERSION_LESS 3.18)
|
if(${CMAKE_VERSION} VERSION_LESS 3.26)
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||||
else()
|
else()
|
||||||
cmake_policy(VERSION 3.18)
|
cmake_policy(VERSION 3.26)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(test_installed_function CXX)
|
project(test_installed_function CXX)
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
cmake_minimum_required(VERSION 3.4)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
|
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
|
||||||
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
||||||
# the behavior using the following workaround:
|
# the behavior using the following workaround:
|
||||||
if(${CMAKE_VERSION} VERSION_LESS 3.18)
|
if(${CMAKE_VERSION} VERSION_LESS 3.26)
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||||
else()
|
else()
|
||||||
cmake_policy(VERSION 3.18)
|
cmake_policy(VERSION 3.26)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(test_installed_target CXX)
|
project(test_installed_target CXX)
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
cmake_minimum_required(VERSION 3.4)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
|
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
|
||||||
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
||||||
# the behavior using the following workaround:
|
# the behavior using the following workaround:
|
||||||
if(${CMAKE_VERSION} VERSION_LESS 3.18)
|
if(${CMAKE_VERSION} VERSION_LESS 3.26)
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||||
else()
|
else()
|
||||||
cmake_policy(VERSION 3.18)
|
cmake_policy(VERSION 3.26)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(test_subdirectory_embed CXX)
|
project(test_subdirectory_embed CXX)
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
cmake_minimum_required(VERSION 3.4)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
|
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
|
||||||
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
||||||
# the behavior using the following workaround:
|
# the behavior using the following workaround:
|
||||||
if(${CMAKE_VERSION} VERSION_LESS 3.18)
|
if(${CMAKE_VERSION} VERSION_LESS 3.26)
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||||
else()
|
else()
|
||||||
cmake_policy(VERSION 3.18)
|
cmake_policy(VERSION 3.26)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(test_subdirectory_function CXX)
|
project(test_subdirectory_function CXX)
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
cmake_minimum_required(VERSION 3.4)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
|
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
|
||||||
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
||||||
# the behavior using the following workaround:
|
# the behavior using the following workaround:
|
||||||
if(${CMAKE_VERSION} VERSION_LESS 3.18)
|
if(${CMAKE_VERSION} VERSION_LESS 3.26)
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||||
else()
|
else()
|
||||||
cmake_policy(VERSION 3.18)
|
cmake_policy(VERSION 3.26)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(test_subdirectory_target CXX)
|
project(test_subdirectory_target CXX)
|
||||||
|
@ -3,9 +3,9 @@ import pytest
|
|||||||
from pybind11_tests import const_name as m
|
from pybind11_tests import const_name as m
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("func", (m.const_name_tests, m.underscore_tests))
|
@pytest.mark.parametrize("func", [m.const_name_tests, m.underscore_tests])
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"selector, expected",
|
("selector", "expected"),
|
||||||
enumerate(
|
enumerate(
|
||||||
(
|
(
|
||||||
"",
|
"",
|
||||||
|
@ -148,4 +148,7 @@ TEST_SUBMODULE(constants_and_functions, m) {
|
|||||||
py::arg_v("y", 42, "<the answer>"),
|
py::arg_v("y", 42, "<the answer>"),
|
||||||
py::arg_v("z", default_value));
|
py::arg_v("z", default_value));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// test noexcept(true) lambda (#4565)
|
||||||
|
m.def("l1", []() noexcept(true) { return 0; });
|
||||||
}
|
}
|
||||||
|
@ -50,3 +50,7 @@ def test_function_record_leaks():
|
|||||||
m.register_large_capture_with_invalid_arguments(m)
|
m.register_large_capture_with_invalid_arguments(m)
|
||||||
with pytest.raises(RuntimeError):
|
with pytest.raises(RuntimeError):
|
||||||
m.register_with_raising_repr(m, RaisingRepr())
|
m.register_with_raising_repr(m, RaisingRepr())
|
||||||
|
|
||||||
|
|
||||||
|
def test_noexcept_lambda():
|
||||||
|
assert m.l1() == 0
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
#include "constructor_stats.h"
|
#include "constructor_stats.h"
|
||||||
#include "pybind11_tests.h"
|
#include "pybind11_tests.h"
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
template <typename derived>
|
template <typename derived>
|
||||||
struct empty {
|
struct empty {
|
||||||
static const derived &get_one() { return instance_; }
|
static const derived &get_one() { return instance_; }
|
||||||
@ -293,3 +295,239 @@ TEST_SUBMODULE(copy_move_policies, m) {
|
|||||||
// Make sure that cast from pytype rvalue to other pytype works
|
// Make sure that cast from pytype rvalue to other pytype works
|
||||||
m.def("get_pytype_rvalue_castissue", [](double i) { return py::float_(i).cast<py::int_>(); });
|
m.def("get_pytype_rvalue_castissue", [](double i) { return py::float_(i).cast<py::int_>(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rest of the file:
|
||||||
|
* static_assert based tests for pybind11 adaptations of
|
||||||
|
* std::is_move_constructible, std::is_copy_constructible and
|
||||||
|
* std::is_copy_assignable (no adaptation of std::is_move_assignable).
|
||||||
|
* Difference between pybind11 and std traits: pybind11 traits will also check
|
||||||
|
* the contained value_types.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct NotMovable {
|
||||||
|
NotMovable() = default;
|
||||||
|
NotMovable(NotMovable const &) = default;
|
||||||
|
NotMovable(NotMovable &&) = delete;
|
||||||
|
NotMovable &operator=(NotMovable const &) = default;
|
||||||
|
NotMovable &operator=(NotMovable &&) = delete;
|
||||||
|
};
|
||||||
|
static_assert(!std::is_move_constructible<NotMovable>::value,
|
||||||
|
"!std::is_move_constructible<NotMovable>::value");
|
||||||
|
static_assert(std::is_copy_constructible<NotMovable>::value,
|
||||||
|
"std::is_copy_constructible<NotMovable>::value");
|
||||||
|
static_assert(!pybind11::detail::is_move_constructible<NotMovable>::value,
|
||||||
|
"!pybind11::detail::is_move_constructible<NotMovable>::value");
|
||||||
|
static_assert(pybind11::detail::is_copy_constructible<NotMovable>::value,
|
||||||
|
"pybind11::detail::is_copy_constructible<NotMovable>::value");
|
||||||
|
static_assert(!std::is_move_assignable<NotMovable>::value,
|
||||||
|
"!std::is_move_assignable<NotMovable>::value");
|
||||||
|
static_assert(std::is_copy_assignable<NotMovable>::value,
|
||||||
|
"std::is_copy_assignable<NotMovable>::value");
|
||||||
|
// pybind11 does not have this
|
||||||
|
// static_assert(!pybind11::detail::is_move_assignable<NotMovable>::value,
|
||||||
|
// "!pybind11::detail::is_move_assignable<NotMovable>::value");
|
||||||
|
static_assert(pybind11::detail::is_copy_assignable<NotMovable>::value,
|
||||||
|
"pybind11::detail::is_copy_assignable<NotMovable>::value");
|
||||||
|
|
||||||
|
struct NotCopyable {
|
||||||
|
NotCopyable() = default;
|
||||||
|
NotCopyable(NotCopyable const &) = delete;
|
||||||
|
NotCopyable(NotCopyable &&) = default;
|
||||||
|
NotCopyable &operator=(NotCopyable const &) = delete;
|
||||||
|
NotCopyable &operator=(NotCopyable &&) = default;
|
||||||
|
};
|
||||||
|
static_assert(std::is_move_constructible<NotCopyable>::value,
|
||||||
|
"std::is_move_constructible<NotCopyable>::value");
|
||||||
|
static_assert(!std::is_copy_constructible<NotCopyable>::value,
|
||||||
|
"!std::is_copy_constructible<NotCopyable>::value");
|
||||||
|
static_assert(pybind11::detail::is_move_constructible<NotCopyable>::value,
|
||||||
|
"pybind11::detail::is_move_constructible<NotCopyable>::value");
|
||||||
|
static_assert(!pybind11::detail::is_copy_constructible<NotCopyable>::value,
|
||||||
|
"!pybind11::detail::is_copy_constructible<NotCopyable>::value");
|
||||||
|
static_assert(std::is_move_assignable<NotCopyable>::value,
|
||||||
|
"std::is_move_assignable<NotCopyable>::value");
|
||||||
|
static_assert(!std::is_copy_assignable<NotCopyable>::value,
|
||||||
|
"!std::is_copy_assignable<NotCopyable>::value");
|
||||||
|
// pybind11 does not have this
|
||||||
|
// static_assert(!pybind11::detail::is_move_assignable<NotCopyable>::value,
|
||||||
|
// "!pybind11::detail::is_move_assignable<NotCopyable>::value");
|
||||||
|
static_assert(!pybind11::detail::is_copy_assignable<NotCopyable>::value,
|
||||||
|
"!pybind11::detail::is_copy_assignable<NotCopyable>::value");
|
||||||
|
|
||||||
|
struct NotCopyableNotMovable {
|
||||||
|
NotCopyableNotMovable() = default;
|
||||||
|
NotCopyableNotMovable(NotCopyableNotMovable const &) = delete;
|
||||||
|
NotCopyableNotMovable(NotCopyableNotMovable &&) = delete;
|
||||||
|
NotCopyableNotMovable &operator=(NotCopyableNotMovable const &) = delete;
|
||||||
|
NotCopyableNotMovable &operator=(NotCopyableNotMovable &&) = delete;
|
||||||
|
};
|
||||||
|
static_assert(!std::is_move_constructible<NotCopyableNotMovable>::value,
|
||||||
|
"!std::is_move_constructible<NotCopyableNotMovable>::value");
|
||||||
|
static_assert(!std::is_copy_constructible<NotCopyableNotMovable>::value,
|
||||||
|
"!std::is_copy_constructible<NotCopyableNotMovable>::value");
|
||||||
|
static_assert(!pybind11::detail::is_move_constructible<NotCopyableNotMovable>::value,
|
||||||
|
"!pybind11::detail::is_move_constructible<NotCopyableNotMovable>::value");
|
||||||
|
static_assert(!pybind11::detail::is_copy_constructible<NotCopyableNotMovable>::value,
|
||||||
|
"!pybind11::detail::is_copy_constructible<NotCopyableNotMovable>::value");
|
||||||
|
static_assert(!std::is_move_assignable<NotCopyableNotMovable>::value,
|
||||||
|
"!std::is_move_assignable<NotCopyableNotMovable>::value");
|
||||||
|
static_assert(!std::is_copy_assignable<NotCopyableNotMovable>::value,
|
||||||
|
"!std::is_copy_assignable<NotCopyableNotMovable>::value");
|
||||||
|
// pybind11 does not have this
|
||||||
|
// static_assert(!pybind11::detail::is_move_assignable<NotCopyableNotMovable>::value,
|
||||||
|
// "!pybind11::detail::is_move_assignable<NotCopyableNotMovable>::value");
|
||||||
|
static_assert(!pybind11::detail::is_copy_assignable<NotCopyableNotMovable>::value,
|
||||||
|
"!pybind11::detail::is_copy_assignable<NotCopyableNotMovable>::value");
|
||||||
|
|
||||||
|
struct NotMovableVector : std::vector<NotMovable> {};
|
||||||
|
static_assert(std::is_move_constructible<NotMovableVector>::value,
|
||||||
|
"std::is_move_constructible<NotMovableVector>::value");
|
||||||
|
static_assert(std::is_copy_constructible<NotMovableVector>::value,
|
||||||
|
"std::is_copy_constructible<NotMovableVector>::value");
|
||||||
|
static_assert(!pybind11::detail::is_move_constructible<NotMovableVector>::value,
|
||||||
|
"!pybind11::detail::is_move_constructible<NotMovableVector>::value");
|
||||||
|
static_assert(pybind11::detail::is_copy_constructible<NotMovableVector>::value,
|
||||||
|
"pybind11::detail::is_copy_constructible<NotMovableVector>::value");
|
||||||
|
static_assert(std::is_move_assignable<NotMovableVector>::value,
|
||||||
|
"std::is_move_assignable<NotMovableVector>::value");
|
||||||
|
static_assert(std::is_copy_assignable<NotMovableVector>::value,
|
||||||
|
"std::is_copy_assignable<NotMovableVector>::value");
|
||||||
|
// pybind11 does not have this
|
||||||
|
// static_assert(!pybind11::detail::is_move_assignable<NotMovableVector>::value,
|
||||||
|
// "!pybind11::detail::is_move_assignable<NotMovableVector>::value");
|
||||||
|
static_assert(pybind11::detail::is_copy_assignable<NotMovableVector>::value,
|
||||||
|
"pybind11::detail::is_copy_assignable<NotMovableVector>::value");
|
||||||
|
|
||||||
|
struct NotCopyableVector : std::vector<NotCopyable> {};
|
||||||
|
static_assert(std::is_move_constructible<NotCopyableVector>::value,
|
||||||
|
"std::is_move_constructible<NotCopyableVector>::value");
|
||||||
|
static_assert(std::is_copy_constructible<NotCopyableVector>::value,
|
||||||
|
"std::is_copy_constructible<NotCopyableVector>::value");
|
||||||
|
static_assert(pybind11::detail::is_move_constructible<NotCopyableVector>::value,
|
||||||
|
"pybind11::detail::is_move_constructible<NotCopyableVector>::value");
|
||||||
|
static_assert(!pybind11::detail::is_copy_constructible<NotCopyableVector>::value,
|
||||||
|
"!pybind11::detail::is_copy_constructible<NotCopyableVector>::value");
|
||||||
|
static_assert(std::is_move_assignable<NotCopyableVector>::value,
|
||||||
|
"std::is_move_assignable<NotCopyableVector>::value");
|
||||||
|
static_assert(std::is_copy_assignable<NotCopyableVector>::value,
|
||||||
|
"std::is_copy_assignable<NotCopyableVector>::value");
|
||||||
|
// pybind11 does not have this
|
||||||
|
// static_assert(!pybind11::detail::is_move_assignable<NotCopyableVector>::value,
|
||||||
|
// "!pybind11::detail::is_move_assignable<NotCopyableVector>::value");
|
||||||
|
static_assert(!pybind11::detail::is_copy_assignable<NotCopyableVector>::value,
|
||||||
|
"!pybind11::detail::is_copy_assignable<NotCopyableVector>::value");
|
||||||
|
|
||||||
|
struct NotCopyableNotMovableVector : std::vector<NotCopyableNotMovable> {};
|
||||||
|
static_assert(std::is_move_constructible<NotCopyableNotMovableVector>::value,
|
||||||
|
"std::is_move_constructible<NotCopyableNotMovableVector>::value");
|
||||||
|
static_assert(std::is_copy_constructible<NotCopyableNotMovableVector>::value,
|
||||||
|
"std::is_copy_constructible<NotCopyableNotMovableVector>::value");
|
||||||
|
static_assert(!pybind11::detail::is_move_constructible<NotCopyableNotMovableVector>::value,
|
||||||
|
"!pybind11::detail::is_move_constructible<NotCopyableNotMovableVector>::value");
|
||||||
|
static_assert(!pybind11::detail::is_copy_constructible<NotCopyableNotMovableVector>::value,
|
||||||
|
"!pybind11::detail::is_copy_constructible<NotCopyableNotMovableVector>::value");
|
||||||
|
static_assert(std::is_move_assignable<NotCopyableNotMovableVector>::value,
|
||||||
|
"std::is_move_assignable<NotCopyableNotMovableVector>::value");
|
||||||
|
static_assert(std::is_copy_assignable<NotCopyableNotMovableVector>::value,
|
||||||
|
"std::is_copy_assignable<NotCopyableNotMovableVector>::value");
|
||||||
|
// pybind11 does not have this
|
||||||
|
// static_assert(!pybind11::detail::is_move_assignable<NotCopyableNotMovableVector>::value,
|
||||||
|
// "!pybind11::detail::is_move_assignable<NotCopyableNotMovableVector>::value");
|
||||||
|
static_assert(!pybind11::detail::is_copy_assignable<NotCopyableNotMovableVector>::value,
|
||||||
|
"!pybind11::detail::is_copy_assignable<NotCopyableNotMovableVector>::value");
|
||||||
|
|
||||||
|
struct NotMovableMap : std::map<int, NotMovable> {};
|
||||||
|
static_assert(std::is_move_constructible<NotMovableMap>::value,
|
||||||
|
"std::is_move_constructible<NotMovableMap>::value");
|
||||||
|
static_assert(std::is_copy_constructible<NotMovableMap>::value,
|
||||||
|
"std::is_copy_constructible<NotMovableMap>::value");
|
||||||
|
static_assert(!pybind11::detail::is_move_constructible<NotMovableMap>::value,
|
||||||
|
"!pybind11::detail::is_move_constructible<NotMovableMap>::value");
|
||||||
|
static_assert(pybind11::detail::is_copy_constructible<NotMovableMap>::value,
|
||||||
|
"pybind11::detail::is_copy_constructible<NotMovableMap>::value");
|
||||||
|
static_assert(std::is_move_assignable<NotMovableMap>::value,
|
||||||
|
"std::is_move_assignable<NotMovableMap>::value");
|
||||||
|
static_assert(std::is_copy_assignable<NotMovableMap>::value,
|
||||||
|
"std::is_copy_assignable<NotMovableMap>::value");
|
||||||
|
// pybind11 does not have this
|
||||||
|
// static_assert(!pybind11::detail::is_move_assignable<NotMovableMap>::value,
|
||||||
|
// "!pybind11::detail::is_move_assignable<NotMovableMap>::value");
|
||||||
|
static_assert(pybind11::detail::is_copy_assignable<NotMovableMap>::value,
|
||||||
|
"pybind11::detail::is_copy_assignable<NotMovableMap>::value");
|
||||||
|
|
||||||
|
struct NotCopyableMap : std::map<int, NotCopyable> {};
|
||||||
|
static_assert(std::is_move_constructible<NotCopyableMap>::value,
|
||||||
|
"std::is_move_constructible<NotCopyableMap>::value");
|
||||||
|
static_assert(std::is_copy_constructible<NotCopyableMap>::value,
|
||||||
|
"std::is_copy_constructible<NotCopyableMap>::value");
|
||||||
|
static_assert(pybind11::detail::is_move_constructible<NotCopyableMap>::value,
|
||||||
|
"pybind11::detail::is_move_constructible<NotCopyableMap>::value");
|
||||||
|
static_assert(!pybind11::detail::is_copy_constructible<NotCopyableMap>::value,
|
||||||
|
"!pybind11::detail::is_copy_constructible<NotCopyableMap>::value");
|
||||||
|
static_assert(std::is_move_assignable<NotCopyableMap>::value,
|
||||||
|
"std::is_move_assignable<NotCopyableMap>::value");
|
||||||
|
static_assert(std::is_copy_assignable<NotCopyableMap>::value,
|
||||||
|
"std::is_copy_assignable<NotCopyableMap>::value");
|
||||||
|
// pybind11 does not have this
|
||||||
|
// static_assert(!pybind11::detail::is_move_assignable<NotCopyableMap>::value,
|
||||||
|
// "!pybind11::detail::is_move_assignable<NotCopyableMap>::value");
|
||||||
|
static_assert(!pybind11::detail::is_copy_assignable<NotCopyableMap>::value,
|
||||||
|
"!pybind11::detail::is_copy_assignable<NotCopyableMap>::value");
|
||||||
|
|
||||||
|
struct NotCopyableNotMovableMap : std::map<int, NotCopyableNotMovable> {};
|
||||||
|
static_assert(std::is_move_constructible<NotCopyableNotMovableMap>::value,
|
||||||
|
"std::is_move_constructible<NotCopyableNotMovableMap>::value");
|
||||||
|
static_assert(std::is_copy_constructible<NotCopyableNotMovableMap>::value,
|
||||||
|
"std::is_copy_constructible<NotCopyableNotMovableMap>::value");
|
||||||
|
static_assert(!pybind11::detail::is_move_constructible<NotCopyableNotMovableMap>::value,
|
||||||
|
"!pybind11::detail::is_move_constructible<NotCopyableNotMovableMap>::value");
|
||||||
|
static_assert(!pybind11::detail::is_copy_constructible<NotCopyableNotMovableMap>::value,
|
||||||
|
"!pybind11::detail::is_copy_constructible<NotCopyableNotMovableMap>::value");
|
||||||
|
static_assert(std::is_move_assignable<NotCopyableNotMovableMap>::value,
|
||||||
|
"std::is_move_assignable<NotCopyableNotMovableMap>::value");
|
||||||
|
static_assert(std::is_copy_assignable<NotCopyableNotMovableMap>::value,
|
||||||
|
"std::is_copy_assignable<NotCopyableNotMovableMap>::value");
|
||||||
|
// pybind11 does not have this
|
||||||
|
// static_assert(!pybind11::detail::is_move_assignable<NotCopyableNotMovableMap>::value,
|
||||||
|
// "!pybind11::detail::is_move_assignable<NotCopyableNotMovableMap>::value");
|
||||||
|
static_assert(!pybind11::detail::is_copy_assignable<NotCopyableNotMovableMap>::value,
|
||||||
|
"!pybind11::detail::is_copy_assignable<NotCopyableNotMovableMap>::value");
|
||||||
|
|
||||||
|
struct RecursiveVector : std::vector<RecursiveVector> {};
|
||||||
|
static_assert(std::is_move_constructible<RecursiveVector>::value,
|
||||||
|
"std::is_move_constructible<RecursiveVector>::value");
|
||||||
|
static_assert(std::is_copy_constructible<RecursiveVector>::value,
|
||||||
|
"std::is_copy_constructible<RecursiveVector>::value");
|
||||||
|
static_assert(pybind11::detail::is_move_constructible<RecursiveVector>::value,
|
||||||
|
"pybind11::detail::is_move_constructible<RecursiveVector>::value");
|
||||||
|
static_assert(pybind11::detail::is_copy_constructible<RecursiveVector>::value,
|
||||||
|
"pybind11::detail::is_copy_constructible<RecursiveVector>::value");
|
||||||
|
static_assert(std::is_move_assignable<RecursiveVector>::value,
|
||||||
|
"std::is_move_assignable<RecursiveVector>::value");
|
||||||
|
static_assert(std::is_copy_assignable<RecursiveVector>::value,
|
||||||
|
"std::is_copy_assignable<RecursiveVector>::value");
|
||||||
|
// pybind11 does not have this
|
||||||
|
// static_assert(!pybind11::detail::is_move_assignable<RecursiveVector>::value,
|
||||||
|
// "!pybind11::detail::is_move_assignable<RecursiveVector>::value");
|
||||||
|
static_assert(pybind11::detail::is_copy_assignable<RecursiveVector>::value,
|
||||||
|
"pybind11::detail::is_copy_assignable<RecursiveVector>::value");
|
||||||
|
|
||||||
|
struct RecursiveMap : std::map<int, RecursiveMap> {};
|
||||||
|
static_assert(std::is_move_constructible<RecursiveMap>::value,
|
||||||
|
"std::is_move_constructible<RecursiveMap>::value");
|
||||||
|
static_assert(std::is_copy_constructible<RecursiveMap>::value,
|
||||||
|
"std::is_copy_constructible<RecursiveMap>::value");
|
||||||
|
static_assert(pybind11::detail::is_move_constructible<RecursiveMap>::value,
|
||||||
|
"pybind11::detail::is_move_constructible<RecursiveMap>::value");
|
||||||
|
static_assert(pybind11::detail::is_copy_constructible<RecursiveMap>::value,
|
||||||
|
"pybind11::detail::is_copy_constructible<RecursiveMap>::value");
|
||||||
|
static_assert(std::is_move_assignable<RecursiveMap>::value,
|
||||||
|
"std::is_move_assignable<RecursiveMap>::value");
|
||||||
|
static_assert(std::is_copy_assignable<RecursiveMap>::value,
|
||||||
|
"std::is_copy_assignable<RecursiveMap>::value");
|
||||||
|
// pybind11 does not have this
|
||||||
|
// static_assert(!pybind11::detail::is_move_assignable<RecursiveMap>::value,
|
||||||
|
// "!pybind11::detail::is_move_assignable<RecursiveMap>::value");
|
||||||
|
static_assert(pybind11::detail::is_copy_assignable<RecursiveMap>::value,
|
||||||
|
"pybind11::detail::is_copy_assignable<RecursiveMap>::value");
|
||||||
|
@ -100,7 +100,8 @@ def test_custom_caster_destruction():
|
|||||||
cstats = m.destruction_tester_cstats()
|
cstats = m.destruction_tester_cstats()
|
||||||
# This one *doesn't* have take_ownership: the pointer should be used but not destroyed:
|
# This one *doesn't* have take_ownership: the pointer should be used but not destroyed:
|
||||||
z = m.custom_caster_no_destroy()
|
z = m.custom_caster_no_destroy()
|
||||||
assert cstats.alive() == 1 and cstats.default_constructions == 1
|
assert cstats.alive() == 1
|
||||||
|
assert cstats.default_constructions == 1
|
||||||
assert z
|
assert z
|
||||||
|
|
||||||
# take_ownership applied: this constructs a new object, casts it, then destroys it:
|
# take_ownership applied: this constructs a new object, casts it, then destroys it:
|
||||||
|
@ -7,7 +7,7 @@ import env # noqa: F401
|
|||||||
from pybind11_tests import custom_type_setup as m
|
from pybind11_tests import custom_type_setup as m
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture()
|
||||||
def gc_tester():
|
def gc_tester():
|
||||||
"""Tests that an object is garbage collected.
|
"""Tests that an object is garbage collected.
|
||||||
|
|
||||||
|
@ -263,79 +263,96 @@ def test_eigen_return_references():
|
|||||||
primary = np.ones((10, 10))
|
primary = np.ones((10, 10))
|
||||||
a = m.ReturnTester()
|
a = m.ReturnTester()
|
||||||
a_get1 = a.get()
|
a_get1 = a.get()
|
||||||
assert not a_get1.flags.owndata and a_get1.flags.writeable
|
assert not a_get1.flags.owndata
|
||||||
|
assert a_get1.flags.writeable
|
||||||
assign_both(a_get1, primary, 3, 3, 5)
|
assign_both(a_get1, primary, 3, 3, 5)
|
||||||
a_get2 = a.get_ptr()
|
a_get2 = a.get_ptr()
|
||||||
assert not a_get2.flags.owndata and a_get2.flags.writeable
|
assert not a_get2.flags.owndata
|
||||||
|
assert a_get2.flags.writeable
|
||||||
assign_both(a_get1, primary, 2, 3, 6)
|
assign_both(a_get1, primary, 2, 3, 6)
|
||||||
|
|
||||||
a_view1 = a.view()
|
a_view1 = a.view()
|
||||||
assert not a_view1.flags.owndata and not a_view1.flags.writeable
|
assert not a_view1.flags.owndata
|
||||||
|
assert not a_view1.flags.writeable
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
a_view1[2, 3] = 4
|
a_view1[2, 3] = 4
|
||||||
a_view2 = a.view_ptr()
|
a_view2 = a.view_ptr()
|
||||||
assert not a_view2.flags.owndata and not a_view2.flags.writeable
|
assert not a_view2.flags.owndata
|
||||||
|
assert not a_view2.flags.writeable
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
a_view2[2, 3] = 4
|
a_view2[2, 3] = 4
|
||||||
|
|
||||||
a_copy1 = a.copy_get()
|
a_copy1 = a.copy_get()
|
||||||
assert a_copy1.flags.owndata and a_copy1.flags.writeable
|
assert a_copy1.flags.owndata
|
||||||
|
assert a_copy1.flags.writeable
|
||||||
np.testing.assert_array_equal(a_copy1, primary)
|
np.testing.assert_array_equal(a_copy1, primary)
|
||||||
a_copy1[7, 7] = -44 # Shouldn't affect anything else
|
a_copy1[7, 7] = -44 # Shouldn't affect anything else
|
||||||
c1want = array_copy_but_one(primary, 7, 7, -44)
|
c1want = array_copy_but_one(primary, 7, 7, -44)
|
||||||
a_copy2 = a.copy_view()
|
a_copy2 = a.copy_view()
|
||||||
assert a_copy2.flags.owndata and a_copy2.flags.writeable
|
assert a_copy2.flags.owndata
|
||||||
|
assert a_copy2.flags.writeable
|
||||||
np.testing.assert_array_equal(a_copy2, primary)
|
np.testing.assert_array_equal(a_copy2, primary)
|
||||||
a_copy2[4, 4] = -22 # Shouldn't affect anything else
|
a_copy2[4, 4] = -22 # Shouldn't affect anything else
|
||||||
c2want = array_copy_but_one(primary, 4, 4, -22)
|
c2want = array_copy_but_one(primary, 4, 4, -22)
|
||||||
|
|
||||||
a_ref1 = a.ref()
|
a_ref1 = a.ref()
|
||||||
assert not a_ref1.flags.owndata and a_ref1.flags.writeable
|
assert not a_ref1.flags.owndata
|
||||||
|
assert a_ref1.flags.writeable
|
||||||
assign_both(a_ref1, primary, 1, 1, 15)
|
assign_both(a_ref1, primary, 1, 1, 15)
|
||||||
a_ref2 = a.ref_const()
|
a_ref2 = a.ref_const()
|
||||||
assert not a_ref2.flags.owndata and not a_ref2.flags.writeable
|
assert not a_ref2.flags.owndata
|
||||||
|
assert not a_ref2.flags.writeable
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
a_ref2[5, 5] = 33
|
a_ref2[5, 5] = 33
|
||||||
a_ref3 = a.ref_safe()
|
a_ref3 = a.ref_safe()
|
||||||
assert not a_ref3.flags.owndata and a_ref3.flags.writeable
|
assert not a_ref3.flags.owndata
|
||||||
|
assert a_ref3.flags.writeable
|
||||||
assign_both(a_ref3, primary, 0, 7, 99)
|
assign_both(a_ref3, primary, 0, 7, 99)
|
||||||
a_ref4 = a.ref_const_safe()
|
a_ref4 = a.ref_const_safe()
|
||||||
assert not a_ref4.flags.owndata and not a_ref4.flags.writeable
|
assert not a_ref4.flags.owndata
|
||||||
|
assert not a_ref4.flags.writeable
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
a_ref4[7, 0] = 987654321
|
a_ref4[7, 0] = 987654321
|
||||||
|
|
||||||
a_copy3 = a.copy_ref()
|
a_copy3 = a.copy_ref()
|
||||||
assert a_copy3.flags.owndata and a_copy3.flags.writeable
|
assert a_copy3.flags.owndata
|
||||||
|
assert a_copy3.flags.writeable
|
||||||
np.testing.assert_array_equal(a_copy3, primary)
|
np.testing.assert_array_equal(a_copy3, primary)
|
||||||
a_copy3[8, 1] = 11
|
a_copy3[8, 1] = 11
|
||||||
c3want = array_copy_but_one(primary, 8, 1, 11)
|
c3want = array_copy_but_one(primary, 8, 1, 11)
|
||||||
a_copy4 = a.copy_ref_const()
|
a_copy4 = a.copy_ref_const()
|
||||||
assert a_copy4.flags.owndata and a_copy4.flags.writeable
|
assert a_copy4.flags.owndata
|
||||||
|
assert a_copy4.flags.writeable
|
||||||
np.testing.assert_array_equal(a_copy4, primary)
|
np.testing.assert_array_equal(a_copy4, primary)
|
||||||
a_copy4[8, 4] = 88
|
a_copy4[8, 4] = 88
|
||||||
c4want = array_copy_but_one(primary, 8, 4, 88)
|
c4want = array_copy_but_one(primary, 8, 4, 88)
|
||||||
|
|
||||||
a_block1 = a.block(3, 3, 2, 2)
|
a_block1 = a.block(3, 3, 2, 2)
|
||||||
assert not a_block1.flags.owndata and a_block1.flags.writeable
|
assert not a_block1.flags.owndata
|
||||||
|
assert a_block1.flags.writeable
|
||||||
a_block1[0, 0] = 55
|
a_block1[0, 0] = 55
|
||||||
primary[3, 3] = 55
|
primary[3, 3] = 55
|
||||||
a_block2 = a.block_safe(2, 2, 3, 2)
|
a_block2 = a.block_safe(2, 2, 3, 2)
|
||||||
assert not a_block2.flags.owndata and a_block2.flags.writeable
|
assert not a_block2.flags.owndata
|
||||||
|
assert a_block2.flags.writeable
|
||||||
a_block2[2, 1] = -123
|
a_block2[2, 1] = -123
|
||||||
primary[4, 3] = -123
|
primary[4, 3] = -123
|
||||||
a_block3 = a.block_const(6, 7, 4, 3)
|
a_block3 = a.block_const(6, 7, 4, 3)
|
||||||
assert not a_block3.flags.owndata and not a_block3.flags.writeable
|
assert not a_block3.flags.owndata
|
||||||
|
assert not a_block3.flags.writeable
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
a_block3[2, 2] = -44444
|
a_block3[2, 2] = -44444
|
||||||
|
|
||||||
a_copy5 = a.copy_block(2, 2, 2, 3)
|
a_copy5 = a.copy_block(2, 2, 2, 3)
|
||||||
assert a_copy5.flags.owndata and a_copy5.flags.writeable
|
assert a_copy5.flags.owndata
|
||||||
|
assert a_copy5.flags.writeable
|
||||||
np.testing.assert_array_equal(a_copy5, primary[2:4, 2:5])
|
np.testing.assert_array_equal(a_copy5, primary[2:4, 2:5])
|
||||||
a_copy5[1, 1] = 777
|
a_copy5[1, 1] = 777
|
||||||
c5want = array_copy_but_one(primary[2:4, 2:5], 1, 1, 777)
|
c5want = array_copy_but_one(primary[2:4, 2:5], 1, 1, 777)
|
||||||
|
|
||||||
a_corn1 = a.corners()
|
a_corn1 = a.corners()
|
||||||
assert not a_corn1.flags.owndata and a_corn1.flags.writeable
|
assert not a_corn1.flags.owndata
|
||||||
|
assert a_corn1.flags.writeable
|
||||||
a_corn1 *= 50
|
a_corn1 *= 50
|
||||||
a_corn1[1, 1] = 999
|
a_corn1[1, 1] = 999
|
||||||
primary[0, 0] = 50
|
primary[0, 0] = 50
|
||||||
@ -343,7 +360,8 @@ def test_eigen_return_references():
|
|||||||
primary[9, 0] = 50
|
primary[9, 0] = 50
|
||||||
primary[9, 9] = 999
|
primary[9, 9] = 999
|
||||||
a_corn2 = a.corners_const()
|
a_corn2 = a.corners_const()
|
||||||
assert not a_corn2.flags.owndata and not a_corn2.flags.writeable
|
assert not a_corn2.flags.owndata
|
||||||
|
assert not a_corn2.flags.writeable
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
a_corn2[1, 0] = 51
|
a_corn2[1, 0] = 51
|
||||||
|
|
||||||
@ -503,10 +521,14 @@ def test_numpy_ref_mutators():
|
|||||||
|
|
||||||
assert [zc[1, 2], zcro[1, 2], zr[1, 2], zrro[1, 2]] == [23] * 4
|
assert [zc[1, 2], zcro[1, 2], zr[1, 2], zrro[1, 2]] == [23] * 4
|
||||||
|
|
||||||
assert not zc.flags.owndata and zc.flags.writeable
|
assert not zc.flags.owndata
|
||||||
assert not zr.flags.owndata and zr.flags.writeable
|
assert zc.flags.writeable
|
||||||
assert not zcro.flags.owndata and not zcro.flags.writeable
|
assert not zr.flags.owndata
|
||||||
assert not zrro.flags.owndata and not zrro.flags.writeable
|
assert zr.flags.writeable
|
||||||
|
assert not zcro.flags.owndata
|
||||||
|
assert not zcro.flags.writeable
|
||||||
|
assert not zrro.flags.owndata
|
||||||
|
assert not zrro.flags.writeable
|
||||||
|
|
||||||
zc[1, 2] = 99
|
zc[1, 2] = 99
|
||||||
expect = np.array([[11.0, 12, 13], [21, 22, 99], [31, 32, 33]])
|
expect = np.array([[11.0, 12, 13], [21, 22, 99], [31, 32, 33]])
|
||||||
@ -530,7 +552,8 @@ def test_numpy_ref_mutators():
|
|||||||
# the const should drop away)
|
# the const should drop away)
|
||||||
y1 = np.array(m.get_cm_const_ref())
|
y1 = np.array(m.get_cm_const_ref())
|
||||||
|
|
||||||
assert y1.flags.owndata and y1.flags.writeable
|
assert y1.flags.owndata
|
||||||
|
assert y1.flags.writeable
|
||||||
# We should get copies of the eigen data, which was modified above:
|
# We should get copies of the eigen data, which was modified above:
|
||||||
assert y1[1, 2] == 99
|
assert y1[1, 2] == 99
|
||||||
y1[1, 2] += 12
|
y1[1, 2] += 12
|
||||||
@ -603,38 +626,38 @@ def test_nocopy_wrapper():
|
|||||||
# All but the second should fail with m.get_elem_nocopy:
|
# All but the second should fail with m.get_elem_nocopy:
|
||||||
with pytest.raises(TypeError) as excinfo:
|
with pytest.raises(TypeError) as excinfo:
|
||||||
m.get_elem_nocopy(int_matrix_colmajor)
|
m.get_elem_nocopy(int_matrix_colmajor)
|
||||||
assert "get_elem_nocopy(): incompatible function arguments." in str(
|
assert "get_elem_nocopy(): incompatible function arguments." in str(excinfo.value)
|
||||||
excinfo.value
|
assert ", flags.f_contiguous" in str(excinfo.value)
|
||||||
) and ", flags.f_contiguous" in str(excinfo.value)
|
|
||||||
assert m.get_elem_nocopy(dbl_matrix_colmajor) == 8
|
assert m.get_elem_nocopy(dbl_matrix_colmajor) == 8
|
||||||
with pytest.raises(TypeError) as excinfo:
|
with pytest.raises(TypeError) as excinfo:
|
||||||
m.get_elem_nocopy(int_matrix_rowmajor)
|
m.get_elem_nocopy(int_matrix_rowmajor)
|
||||||
assert "get_elem_nocopy(): incompatible function arguments." in str(
|
assert "get_elem_nocopy(): incompatible function arguments." in str(excinfo.value)
|
||||||
excinfo.value
|
assert ", flags.f_contiguous" in str(excinfo.value)
|
||||||
) and ", flags.f_contiguous" in str(excinfo.value)
|
|
||||||
with pytest.raises(TypeError) as excinfo:
|
with pytest.raises(TypeError) as excinfo:
|
||||||
m.get_elem_nocopy(dbl_matrix_rowmajor)
|
m.get_elem_nocopy(dbl_matrix_rowmajor)
|
||||||
assert "get_elem_nocopy(): incompatible function arguments." in str(
|
assert "get_elem_nocopy(): incompatible function arguments." in str(excinfo.value)
|
||||||
excinfo.value
|
assert ", flags.f_contiguous" in str(excinfo.value)
|
||||||
) and ", flags.f_contiguous" in str(excinfo.value)
|
|
||||||
|
|
||||||
# For the row-major test, we take a long matrix in row-major, so only the third is allowed:
|
# For the row-major test, we take a long matrix in row-major, so only the third is allowed:
|
||||||
with pytest.raises(TypeError) as excinfo:
|
with pytest.raises(TypeError) as excinfo:
|
||||||
m.get_elem_rm_nocopy(int_matrix_colmajor)
|
m.get_elem_rm_nocopy(int_matrix_colmajor)
|
||||||
assert "get_elem_rm_nocopy(): incompatible function arguments." in str(
|
assert "get_elem_rm_nocopy(): incompatible function arguments." in str(
|
||||||
excinfo.value
|
excinfo.value
|
||||||
) and ", flags.c_contiguous" in str(excinfo.value)
|
)
|
||||||
|
assert ", flags.c_contiguous" in str(excinfo.value)
|
||||||
with pytest.raises(TypeError) as excinfo:
|
with pytest.raises(TypeError) as excinfo:
|
||||||
m.get_elem_rm_nocopy(dbl_matrix_colmajor)
|
m.get_elem_rm_nocopy(dbl_matrix_colmajor)
|
||||||
assert "get_elem_rm_nocopy(): incompatible function arguments." in str(
|
assert "get_elem_rm_nocopy(): incompatible function arguments." in str(
|
||||||
excinfo.value
|
excinfo.value
|
||||||
) and ", flags.c_contiguous" in str(excinfo.value)
|
)
|
||||||
|
assert ", flags.c_contiguous" in str(excinfo.value)
|
||||||
assert m.get_elem_rm_nocopy(int_matrix_rowmajor) == 8
|
assert m.get_elem_rm_nocopy(int_matrix_rowmajor) == 8
|
||||||
with pytest.raises(TypeError) as excinfo:
|
with pytest.raises(TypeError) as excinfo:
|
||||||
m.get_elem_rm_nocopy(dbl_matrix_rowmajor)
|
m.get_elem_rm_nocopy(dbl_matrix_rowmajor)
|
||||||
assert "get_elem_rm_nocopy(): incompatible function arguments." in str(
|
assert "get_elem_rm_nocopy(): incompatible function arguments." in str(
|
||||||
excinfo.value
|
excinfo.value
|
||||||
) and ", flags.c_contiguous" in str(excinfo.value)
|
)
|
||||||
|
assert ", flags.c_contiguous" in str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
def test_eigen_ref_life_support():
|
def test_eigen_ref_life_support():
|
||||||
|
@ -11,14 +11,15 @@ try:
|
|||||||
submodules += [avoid.c_style, avoid.f_style]
|
submodules += [avoid.c_style, avoid.f_style]
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
# Ensure config, build, toolchain, etc. issues are not masked here:
|
# Ensure config, build, toolchain, etc. issues are not masked here:
|
||||||
raise RuntimeError(
|
msg = (
|
||||||
"import eigen_tensor_avoid_stl_array FAILED, while "
|
"import eigen_tensor_avoid_stl_array FAILED, while "
|
||||||
"import pybind11_tests.eigen_tensor succeeded. "
|
"import pybind11_tests.eigen_tensor succeeded. "
|
||||||
"Please ensure that "
|
"Please ensure that "
|
||||||
"test_eigen_tensor.cpp & "
|
"test_eigen_tensor.cpp & "
|
||||||
"eigen_tensor_avoid_stl_array.cpp "
|
"eigen_tensor_avoid_stl_array.cpp "
|
||||||
"are built together (or both are not built if Eigen is not available)."
|
"are built together (or both are not built if Eigen is not available)."
|
||||||
) from e
|
)
|
||||||
|
raise RuntimeError(msg) from e
|
||||||
|
|
||||||
tensor_ref = np.empty((3, 5, 2), dtype=np.int64)
|
tensor_ref = np.empty((3, 5, 2), dtype=np.int64)
|
||||||
|
|
||||||
@ -147,10 +148,7 @@ def test_bad_python_to_cpp_casts(m):
|
|||||||
m.round_trip_tensor_noconvert(tensor_ref.astype(np.float64))
|
m.round_trip_tensor_noconvert(tensor_ref.astype(np.float64))
|
||||||
)
|
)
|
||||||
|
|
||||||
if m.needed_options == "F":
|
bad_options = "C" if m.needed_options == "F" else "F"
|
||||||
bad_options = "C"
|
|
||||||
else:
|
|
||||||
bad_options = "F"
|
|
||||||
# Shape, dtype and the order need to be correct for a TensorMap cast
|
# Shape, dtype and the order need to be correct for a TensorMap cast
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
|
TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
|
||||||
@ -173,19 +171,19 @@ def test_bad_python_to_cpp_casts(m):
|
|||||||
np.zeros((3, 5), dtype=np.float64, order=m.needed_options)
|
np.zeros((3, 5), dtype=np.float64, order=m.needed_options)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
temp = np.zeros((3, 5, 2), dtype=np.float64, order=m.needed_options)
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
|
TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
|
||||||
):
|
):
|
||||||
temp = np.zeros((3, 5, 2), dtype=np.float64, order=m.needed_options)
|
|
||||||
m.round_trip_view_tensor(
|
m.round_trip_view_tensor(
|
||||||
temp[:, ::-1, :],
|
temp[:, ::-1, :],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
temp = np.zeros((3, 5, 2), dtype=np.float64, order=m.needed_options)
|
||||||
|
temp.setflags(write=False)
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
|
TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
|
||||||
):
|
):
|
||||||
temp = np.zeros((3, 5, 2), dtype=np.float64, order=m.needed_options)
|
|
||||||
temp.setflags(write=False)
|
|
||||||
m.round_trip_view_tensor(temp)
|
m.round_trip_view_tensor(temp)
|
||||||
|
|
||||||
|
|
||||||
@ -282,9 +280,9 @@ def test_doc_string(m, doc):
|
|||||||
order_flag = f"flags.{m.needed_options.lower()}_contiguous"
|
order_flag = f"flags.{m.needed_options.lower()}_contiguous"
|
||||||
assert doc(m.round_trip_view_tensor) == (
|
assert doc(m.round_trip_view_tensor) == (
|
||||||
f"round_trip_view_tensor(arg0: numpy.ndarray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}])"
|
f"round_trip_view_tensor(arg0: numpy.ndarray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}])"
|
||||||
+ f" -> numpy.ndarray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}]"
|
f" -> numpy.ndarray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}]"
|
||||||
)
|
)
|
||||||
assert doc(m.round_trip_const_view_tensor) == (
|
assert doc(m.round_trip_const_view_tensor) == (
|
||||||
f"round_trip_const_view_tensor(arg0: numpy.ndarray[numpy.float64[?, ?, ?], {order_flag}])"
|
f"round_trip_const_view_tensor(arg0: numpy.ndarray[numpy.float64[?, ?, ?], {order_flag}])"
|
||||||
+ " -> numpy.ndarray[numpy.float64[?, ?, ?]]"
|
" -> numpy.ndarray[numpy.float64[?, ?, ?]]"
|
||||||
)
|
)
|
||||||
|
@ -184,7 +184,7 @@ TEST_CASE("Custom PyConfig") {
|
|||||||
py::initialize_interpreter();
|
py::initialize_interpreter();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Custom PyConfig with argv") {
|
TEST_CASE("scoped_interpreter with PyConfig_InitIsolatedConfig and argv") {
|
||||||
py::finalize_interpreter();
|
py::finalize_interpreter();
|
||||||
{
|
{
|
||||||
PyConfig config;
|
PyConfig config;
|
||||||
@ -199,6 +199,26 @@ TEST_CASE("Custom PyConfig with argv") {
|
|||||||
}
|
}
|
||||||
py::initialize_interpreter();
|
py::initialize_interpreter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("scoped_interpreter with PyConfig_InitPythonConfig and argv") {
|
||||||
|
py::finalize_interpreter();
|
||||||
|
{
|
||||||
|
PyConfig config;
|
||||||
|
PyConfig_InitPythonConfig(&config);
|
||||||
|
|
||||||
|
// `initialize_interpreter() overrides the default value for config.parse_argv (`1`) by
|
||||||
|
// changing it to `0`. This test exercises `scoped_interpreter` with the default config.
|
||||||
|
char *argv[] = {strdup("a.out"), strdup("arg1")};
|
||||||
|
py::scoped_interpreter argv_scope(&config, 2, argv);
|
||||||
|
std::free(argv[0]);
|
||||||
|
std::free(argv[1]);
|
||||||
|
auto module = py::module::import("test_interpreter");
|
||||||
|
auto py_widget = module.attr("DerivedWidget")("The question");
|
||||||
|
const auto &cpp_widget = py_widget.cast<const Widget &>();
|
||||||
|
REQUIRE(cpp_widget.argv0() == "arg1");
|
||||||
|
}
|
||||||
|
py::initialize_interpreter();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TEST_CASE("Add program dir to path pre-PyConfig") {
|
TEST_CASE("Add program dir to path pre-PyConfig") {
|
||||||
@ -235,10 +255,10 @@ TEST_CASE("Add program dir to path using PyConfig") {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool has_pybind11_internals_builtin() {
|
bool has_state_dict_internals_obj() {
|
||||||
auto builtins = py::handle(PyEval_GetBuiltins());
|
return bool(
|
||||||
return builtins.contains(PYBIND11_INTERNALS_ID);
|
py::detail::get_internals_obj_from_state_dict(py::detail::get_python_state_dict()));
|
||||||
};
|
}
|
||||||
|
|
||||||
bool has_pybind11_internals_static() {
|
bool has_pybind11_internals_static() {
|
||||||
auto **&ipp = py::detail::get_internals_pp();
|
auto **&ipp = py::detail::get_internals_pp();
|
||||||
@ -248,7 +268,7 @@ bool has_pybind11_internals_static() {
|
|||||||
TEST_CASE("Restart the interpreter") {
|
TEST_CASE("Restart the interpreter") {
|
||||||
// Verify pre-restart state.
|
// Verify pre-restart state.
|
||||||
REQUIRE(py::module_::import("widget_module").attr("add")(1, 2).cast<int>() == 3);
|
REQUIRE(py::module_::import("widget_module").attr("add")(1, 2).cast<int>() == 3);
|
||||||
REQUIRE(has_pybind11_internals_builtin());
|
REQUIRE(has_state_dict_internals_obj());
|
||||||
REQUIRE(has_pybind11_internals_static());
|
REQUIRE(has_pybind11_internals_static());
|
||||||
REQUIRE(py::module_::import("external_module").attr("A")(123).attr("value").cast<int>()
|
REQUIRE(py::module_::import("external_module").attr("A")(123).attr("value").cast<int>()
|
||||||
== 123);
|
== 123);
|
||||||
@ -265,10 +285,10 @@ TEST_CASE("Restart the interpreter") {
|
|||||||
REQUIRE(Py_IsInitialized() == 1);
|
REQUIRE(Py_IsInitialized() == 1);
|
||||||
|
|
||||||
// Internals are deleted after a restart.
|
// Internals are deleted after a restart.
|
||||||
REQUIRE_FALSE(has_pybind11_internals_builtin());
|
REQUIRE_FALSE(has_state_dict_internals_obj());
|
||||||
REQUIRE_FALSE(has_pybind11_internals_static());
|
REQUIRE_FALSE(has_pybind11_internals_static());
|
||||||
pybind11::detail::get_internals();
|
pybind11::detail::get_internals();
|
||||||
REQUIRE(has_pybind11_internals_builtin());
|
REQUIRE(has_state_dict_internals_obj());
|
||||||
REQUIRE(has_pybind11_internals_static());
|
REQUIRE(has_pybind11_internals_static());
|
||||||
REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp())
|
REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp())
|
||||||
== py::module_::import("external_module").attr("internals_at")().cast<uintptr_t>());
|
== py::module_::import("external_module").attr("internals_at")().cast<uintptr_t>());
|
||||||
@ -283,13 +303,13 @@ TEST_CASE("Restart the interpreter") {
|
|||||||
py::detail::get_internals();
|
py::detail::get_internals();
|
||||||
*static_cast<bool *>(ran) = true;
|
*static_cast<bool *>(ran) = true;
|
||||||
});
|
});
|
||||||
REQUIRE_FALSE(has_pybind11_internals_builtin());
|
REQUIRE_FALSE(has_state_dict_internals_obj());
|
||||||
REQUIRE_FALSE(has_pybind11_internals_static());
|
REQUIRE_FALSE(has_pybind11_internals_static());
|
||||||
REQUIRE_FALSE(ran);
|
REQUIRE_FALSE(ran);
|
||||||
py::finalize_interpreter();
|
py::finalize_interpreter();
|
||||||
REQUIRE(ran);
|
REQUIRE(ran);
|
||||||
py::initialize_interpreter();
|
py::initialize_interpreter();
|
||||||
REQUIRE_FALSE(has_pybind11_internals_builtin());
|
REQUIRE_FALSE(has_state_dict_internals_obj());
|
||||||
REQUIRE_FALSE(has_pybind11_internals_static());
|
REQUIRE_FALSE(has_pybind11_internals_static());
|
||||||
|
|
||||||
// C++ modules can be reloaded.
|
// C++ modules can be reloaded.
|
||||||
@ -311,7 +331,7 @@ TEST_CASE("Subinterpreter") {
|
|||||||
|
|
||||||
REQUIRE(m.attr("add")(1, 2).cast<int>() == 3);
|
REQUIRE(m.attr("add")(1, 2).cast<int>() == 3);
|
||||||
}
|
}
|
||||||
REQUIRE(has_pybind11_internals_builtin());
|
REQUIRE(has_state_dict_internals_obj());
|
||||||
REQUIRE(has_pybind11_internals_static());
|
REQUIRE(has_pybind11_internals_static());
|
||||||
|
|
||||||
/// Create and switch to a subinterpreter.
|
/// Create and switch to a subinterpreter.
|
||||||
@ -321,7 +341,7 @@ TEST_CASE("Subinterpreter") {
|
|||||||
// Subinterpreters get their own copy of builtins. detail::get_internals() still
|
// Subinterpreters get their own copy of builtins. detail::get_internals() still
|
||||||
// works by returning from the static variable, i.e. all interpreters share a single
|
// works by returning from the static variable, i.e. all interpreters share a single
|
||||||
// global pybind11::internals;
|
// global pybind11::internals;
|
||||||
REQUIRE_FALSE(has_pybind11_internals_builtin());
|
REQUIRE_FALSE(has_state_dict_internals_obj());
|
||||||
REQUIRE(has_pybind11_internals_static());
|
REQUIRE(has_pybind11_internals_static());
|
||||||
|
|
||||||
// Modules tags should be gone.
|
// Modules tags should be gone.
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
# ruff: noqa: SIM201 SIM300 SIM202
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from pybind11_tests import enums as m
|
from pybind11_tests import enums as m
|
||||||
|
@ -339,4 +339,9 @@ TEST_SUBMODULE(exceptions, m) {
|
|||||||
}
|
}
|
||||||
return py::str("UNEXPECTED");
|
return py::str("UNEXPECTED");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
m.def("test_fn_cast_int", [](const py::function &fn) {
|
||||||
|
// function returns None instead of int, should give a useful error message
|
||||||
|
fn().cast<int>();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -94,8 +94,7 @@ def ignore_pytest_unraisable_warning(f):
|
|||||||
if hasattr(pytest, unraisable): # Python >= 3.8 and pytest >= 6
|
if hasattr(pytest, unraisable): # Python >= 3.8 and pytest >= 6
|
||||||
dec = pytest.mark.filterwarnings(f"ignore::pytest.{unraisable}")
|
dec = pytest.mark.filterwarnings(f"ignore::pytest.{unraisable}")
|
||||||
return dec(f)
|
return dec(f)
|
||||||
else:
|
return f
|
||||||
return f
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: find out why this fails on PyPy, https://foss.heptapod.net/pypy/pypy/-/issues/3583
|
# TODO: find out why this fails on PyPy, https://foss.heptapod.net/pypy/pypy/-/issues/3583
|
||||||
@ -183,7 +182,7 @@ def test_custom(msg):
|
|||||||
m.throws5_1()
|
m.throws5_1()
|
||||||
assert msg(excinfo.value) == "MyException5 subclass"
|
assert msg(excinfo.value) == "MyException5 subclass"
|
||||||
|
|
||||||
with pytest.raises(m.MyException5) as excinfo:
|
with pytest.raises(m.MyException5) as excinfo: # noqa: PT012
|
||||||
try:
|
try:
|
||||||
m.throws5()
|
m.throws5()
|
||||||
except m.MyException5_1 as err:
|
except m.MyException5_1 as err:
|
||||||
@ -212,7 +211,7 @@ def test_nested_throws(capture):
|
|||||||
m.try_catch(m.MyException5, throw_myex)
|
m.try_catch(m.MyException5, throw_myex)
|
||||||
assert str(excinfo.value) == "nested error"
|
assert str(excinfo.value) == "nested error"
|
||||||
|
|
||||||
def pycatch(exctype, f, *args):
|
def pycatch(exctype, f, *args): # noqa: ARG001
|
||||||
try:
|
try:
|
||||||
f(*args)
|
f(*args)
|
||||||
except m.MyException as e:
|
except m.MyException as e:
|
||||||
@ -303,12 +302,12 @@ class FlakyException(Exception):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"exc_type, exc_value, expected_what",
|
("exc_type", "exc_value", "expected_what"),
|
||||||
(
|
[
|
||||||
(ValueError, "plain_str", "ValueError: plain_str"),
|
(ValueError, "plain_str", "ValueError: plain_str"),
|
||||||
(ValueError, ("tuple_elem",), "ValueError: tuple_elem"),
|
(ValueError, ("tuple_elem",), "ValueError: tuple_elem"),
|
||||||
(FlakyException, ("happy",), "FlakyException: FlakyException.__str__"),
|
(FlakyException, ("happy",), "FlakyException: FlakyException.__str__"),
|
||||||
),
|
],
|
||||||
)
|
)
|
||||||
def test_error_already_set_what_with_happy_exceptions(
|
def test_error_already_set_what_with_happy_exceptions(
|
||||||
exc_type, exc_value, expected_what
|
exc_type, exc_value, expected_what
|
||||||
@ -318,8 +317,7 @@ def test_error_already_set_what_with_happy_exceptions(
|
|||||||
assert what == expected_what
|
assert what == expected_what
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif("env.PYPY", reason="PyErr_NormalizeException Segmentation fault")
|
def _test_flaky_exception_failure_point_init_before_py_3_12():
|
||||||
def test_flaky_exception_failure_point_init():
|
|
||||||
with pytest.raises(RuntimeError) as excinfo:
|
with pytest.raises(RuntimeError) as excinfo:
|
||||||
m.error_already_set_what(FlakyException, ("failure_point_init",))
|
m.error_already_set_what(FlakyException, ("failure_point_init",))
|
||||||
lines = str(excinfo.value).splitlines()
|
lines = str(excinfo.value).splitlines()
|
||||||
@ -333,7 +331,33 @@ def test_flaky_exception_failure_point_init():
|
|||||||
# Checking the first two lines of the traceback as formatted in error_string():
|
# Checking the first two lines of the traceback as formatted in error_string():
|
||||||
assert "test_exceptions.py(" in lines[3]
|
assert "test_exceptions.py(" in lines[3]
|
||||||
assert lines[3].endswith("): __init__")
|
assert lines[3].endswith("): __init__")
|
||||||
assert lines[4].endswith("): test_flaky_exception_failure_point_init")
|
assert lines[4].endswith(
|
||||||
|
"): _test_flaky_exception_failure_point_init_before_py_3_12"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _test_flaky_exception_failure_point_init_py_3_12():
|
||||||
|
# Behavior change in Python 3.12: https://github.com/python/cpython/issues/102594
|
||||||
|
what, py_err_set_after_what = m.error_already_set_what(
|
||||||
|
FlakyException, ("failure_point_init",)
|
||||||
|
)
|
||||||
|
assert not py_err_set_after_what
|
||||||
|
lines = what.splitlines()
|
||||||
|
assert lines[0].endswith("ValueError[WITH __notes__]: triggered_failure_point_init")
|
||||||
|
assert lines[1] == "__notes__ (len=1):"
|
||||||
|
assert "Normalization failed:" in lines[2]
|
||||||
|
assert "FlakyException" in lines[2]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
"env.PYPY and sys.version_info[:2] < (3, 12)",
|
||||||
|
reason="PyErr_NormalizeException Segmentation fault",
|
||||||
|
)
|
||||||
|
def test_flaky_exception_failure_point_init():
|
||||||
|
if sys.version_info[:2] < (3, 12):
|
||||||
|
_test_flaky_exception_failure_point_init_before_py_3_12()
|
||||||
|
else:
|
||||||
|
_test_flaky_exception_failure_point_init_py_3_12()
|
||||||
|
|
||||||
|
|
||||||
def test_flaky_exception_failure_point_str():
|
def test_flaky_exception_failure_point_str():
|
||||||
@ -342,10 +366,7 @@ def test_flaky_exception_failure_point_str():
|
|||||||
)
|
)
|
||||||
assert not py_err_set_after_what
|
assert not py_err_set_after_what
|
||||||
lines = what.splitlines()
|
lines = what.splitlines()
|
||||||
if env.PYPY and len(lines) == 3:
|
n = 3 if env.PYPY and len(lines) == 3 else 5
|
||||||
n = 3 # Traceback is missing.
|
|
||||||
else:
|
|
||||||
n = 5
|
|
||||||
assert (
|
assert (
|
||||||
lines[:n]
|
lines[:n]
|
||||||
== [
|
== [
|
||||||
@ -381,3 +402,12 @@ def test_pypy_oserror_normalization():
|
|||||||
# https://github.com/pybind/pybind11/issues/4075
|
# https://github.com/pybind/pybind11/issues/4075
|
||||||
what = m.test_pypy_oserror_normalization()
|
what = m.test_pypy_oserror_normalization()
|
||||||
assert "this_filename_must_not_exist" in what
|
assert "this_filename_must_not_exist" in what
|
||||||
|
|
||||||
|
|
||||||
|
def test_fn_cast_int_exception():
|
||||||
|
with pytest.raises(RuntimeError) as excinfo:
|
||||||
|
m.test_fn_cast_int(lambda: None)
|
||||||
|
|
||||||
|
assert str(excinfo.value).startswith(
|
||||||
|
"Unable to cast Python instance of type <class 'NoneType'> to C++ type"
|
||||||
|
)
|
||||||
|
@ -96,7 +96,7 @@ def test_init_factory_signature(msg):
|
|||||||
3. __init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.pointer_tag) -> None
|
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: handle, arg1: int, arg2: handle) -> None
|
||||||
""" # noqa: E501 line too long
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -148,10 +148,7 @@ ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK = ALL_BASIC_TESTS + (_intentional_dead
|
|||||||
|
|
||||||
|
|
||||||
def _run_in_process(target, *args, **kwargs):
|
def _run_in_process(target, *args, **kwargs):
|
||||||
if len(args) == 0:
|
test_fn = target if len(args) == 0 else args[0]
|
||||||
test_fn = target
|
|
||||||
else:
|
|
||||||
test_fn = args[0]
|
|
||||||
# Do not need to wait much, 10s should be more than enough.
|
# Do not need to wait much, 10s should be more than enough.
|
||||||
timeout = 0.1 if test_fn is _intentional_deadlock else 10
|
timeout = 0.1 if test_fn is _intentional_deadlock else 10
|
||||||
process = multiprocessing.Process(target=target, args=args, kwargs=kwargs)
|
process = multiprocessing.Process(target=target, args=args, kwargs=kwargs)
|
||||||
@ -178,7 +175,8 @@ def _run_in_process(target, *args, **kwargs):
|
|||||||
elif test_fn is _intentional_deadlock:
|
elif test_fn is _intentional_deadlock:
|
||||||
assert process.exitcode is None
|
assert process.exitcode is None
|
||||||
return 0
|
return 0
|
||||||
elif process.exitcode is None:
|
|
||||||
|
if process.exitcode is None:
|
||||||
assert t_delta > 0.9 * timeout
|
assert t_delta > 0.9 * timeout
|
||||||
msg = "DEADLOCK, most likely, exactly what this test is meant to detect."
|
msg = "DEADLOCK, most likely, exactly what this test is meant to detect."
|
||||||
if env.PYPY and env.WIN:
|
if env.PYPY and env.WIN:
|
||||||
|
@ -9,16 +9,16 @@ def test_captured(capsys):
|
|||||||
m.captured_output(msg)
|
m.captured_output(msg)
|
||||||
stdout, stderr = capsys.readouterr()
|
stdout, stderr = capsys.readouterr()
|
||||||
assert stdout == msg
|
assert stdout == msg
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
|
|
||||||
m.captured_output_default(msg)
|
m.captured_output_default(msg)
|
||||||
stdout, stderr = capsys.readouterr()
|
stdout, stderr = capsys.readouterr()
|
||||||
assert stdout == msg
|
assert stdout == msg
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
|
|
||||||
m.captured_err(msg)
|
m.captured_err(msg)
|
||||||
stdout, stderr = capsys.readouterr()
|
stdout, stderr = capsys.readouterr()
|
||||||
assert stdout == ""
|
assert not stdout
|
||||||
assert stderr == msg
|
assert stderr == msg
|
||||||
|
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ def test_captured_large_string(capsys):
|
|||||||
m.captured_output_default(msg)
|
m.captured_output_default(msg)
|
||||||
stdout, stderr = capsys.readouterr()
|
stdout, stderr = capsys.readouterr()
|
||||||
assert stdout == msg
|
assert stdout == msg
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
|
|
||||||
|
|
||||||
def test_captured_utf8_2byte_offset0(capsys):
|
def test_captured_utf8_2byte_offset0(capsys):
|
||||||
@ -40,7 +40,7 @@ def test_captured_utf8_2byte_offset0(capsys):
|
|||||||
m.captured_output_default(msg)
|
m.captured_output_default(msg)
|
||||||
stdout, stderr = capsys.readouterr()
|
stdout, stderr = capsys.readouterr()
|
||||||
assert stdout == msg
|
assert stdout == msg
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
|
|
||||||
|
|
||||||
def test_captured_utf8_2byte_offset1(capsys):
|
def test_captured_utf8_2byte_offset1(capsys):
|
||||||
@ -50,7 +50,7 @@ def test_captured_utf8_2byte_offset1(capsys):
|
|||||||
m.captured_output_default(msg)
|
m.captured_output_default(msg)
|
||||||
stdout, stderr = capsys.readouterr()
|
stdout, stderr = capsys.readouterr()
|
||||||
assert stdout == msg
|
assert stdout == msg
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
|
|
||||||
|
|
||||||
def test_captured_utf8_3byte_offset0(capsys):
|
def test_captured_utf8_3byte_offset0(capsys):
|
||||||
@ -60,7 +60,7 @@ def test_captured_utf8_3byte_offset0(capsys):
|
|||||||
m.captured_output_default(msg)
|
m.captured_output_default(msg)
|
||||||
stdout, stderr = capsys.readouterr()
|
stdout, stderr = capsys.readouterr()
|
||||||
assert stdout == msg
|
assert stdout == msg
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
|
|
||||||
|
|
||||||
def test_captured_utf8_3byte_offset1(capsys):
|
def test_captured_utf8_3byte_offset1(capsys):
|
||||||
@ -70,7 +70,7 @@ def test_captured_utf8_3byte_offset1(capsys):
|
|||||||
m.captured_output_default(msg)
|
m.captured_output_default(msg)
|
||||||
stdout, stderr = capsys.readouterr()
|
stdout, stderr = capsys.readouterr()
|
||||||
assert stdout == msg
|
assert stdout == msg
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
|
|
||||||
|
|
||||||
def test_captured_utf8_3byte_offset2(capsys):
|
def test_captured_utf8_3byte_offset2(capsys):
|
||||||
@ -80,7 +80,7 @@ def test_captured_utf8_3byte_offset2(capsys):
|
|||||||
m.captured_output_default(msg)
|
m.captured_output_default(msg)
|
||||||
stdout, stderr = capsys.readouterr()
|
stdout, stderr = capsys.readouterr()
|
||||||
assert stdout == msg
|
assert stdout == msg
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
|
|
||||||
|
|
||||||
def test_captured_utf8_4byte_offset0(capsys):
|
def test_captured_utf8_4byte_offset0(capsys):
|
||||||
@ -90,7 +90,7 @@ def test_captured_utf8_4byte_offset0(capsys):
|
|||||||
m.captured_output_default(msg)
|
m.captured_output_default(msg)
|
||||||
stdout, stderr = capsys.readouterr()
|
stdout, stderr = capsys.readouterr()
|
||||||
assert stdout == msg
|
assert stdout == msg
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
|
|
||||||
|
|
||||||
def test_captured_utf8_4byte_offset1(capsys):
|
def test_captured_utf8_4byte_offset1(capsys):
|
||||||
@ -100,7 +100,7 @@ def test_captured_utf8_4byte_offset1(capsys):
|
|||||||
m.captured_output_default(msg)
|
m.captured_output_default(msg)
|
||||||
stdout, stderr = capsys.readouterr()
|
stdout, stderr = capsys.readouterr()
|
||||||
assert stdout == msg
|
assert stdout == msg
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
|
|
||||||
|
|
||||||
def test_captured_utf8_4byte_offset2(capsys):
|
def test_captured_utf8_4byte_offset2(capsys):
|
||||||
@ -110,7 +110,7 @@ def test_captured_utf8_4byte_offset2(capsys):
|
|||||||
m.captured_output_default(msg)
|
m.captured_output_default(msg)
|
||||||
stdout, stderr = capsys.readouterr()
|
stdout, stderr = capsys.readouterr()
|
||||||
assert stdout == msg
|
assert stdout == msg
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
|
|
||||||
|
|
||||||
def test_captured_utf8_4byte_offset3(capsys):
|
def test_captured_utf8_4byte_offset3(capsys):
|
||||||
@ -120,7 +120,7 @@ def test_captured_utf8_4byte_offset3(capsys):
|
|||||||
m.captured_output_default(msg)
|
m.captured_output_default(msg)
|
||||||
stdout, stderr = capsys.readouterr()
|
stdout, stderr = capsys.readouterr()
|
||||||
assert stdout == msg
|
assert stdout == msg
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
|
|
||||||
|
|
||||||
def test_guard_capture(capsys):
|
def test_guard_capture(capsys):
|
||||||
@ -128,7 +128,7 @@ def test_guard_capture(capsys):
|
|||||||
m.guard_output(msg)
|
m.guard_output(msg)
|
||||||
stdout, stderr = capsys.readouterr()
|
stdout, stderr = capsys.readouterr()
|
||||||
assert stdout == msg
|
assert stdout == msg
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
|
|
||||||
|
|
||||||
def test_series_captured(capture):
|
def test_series_captured(capture):
|
||||||
@ -145,7 +145,7 @@ def test_flush(capfd):
|
|||||||
with m.ostream_redirect():
|
with m.ostream_redirect():
|
||||||
m.noisy_function(msg, flush=False)
|
m.noisy_function(msg, flush=False)
|
||||||
stdout, stderr = capfd.readouterr()
|
stdout, stderr = capfd.readouterr()
|
||||||
assert stdout == ""
|
assert not stdout
|
||||||
|
|
||||||
m.noisy_function(msg2, flush=True)
|
m.noisy_function(msg2, flush=True)
|
||||||
stdout, stderr = capfd.readouterr()
|
stdout, stderr = capfd.readouterr()
|
||||||
@ -164,15 +164,15 @@ def test_not_captured(capfd):
|
|||||||
m.raw_output(msg)
|
m.raw_output(msg)
|
||||||
stdout, stderr = capfd.readouterr()
|
stdout, stderr = capfd.readouterr()
|
||||||
assert stdout == msg
|
assert stdout == msg
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
assert stream.getvalue() == ""
|
assert not stream.getvalue()
|
||||||
|
|
||||||
stream = StringIO()
|
stream = StringIO()
|
||||||
with redirect_stdout(stream):
|
with redirect_stdout(stream):
|
||||||
m.captured_output(msg)
|
m.captured_output(msg)
|
||||||
stdout, stderr = capfd.readouterr()
|
stdout, stderr = capfd.readouterr()
|
||||||
assert stdout == ""
|
assert not stdout
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
assert stream.getvalue() == msg
|
assert stream.getvalue() == msg
|
||||||
|
|
||||||
|
|
||||||
@ -182,16 +182,16 @@ def test_err(capfd):
|
|||||||
with redirect_stderr(stream):
|
with redirect_stderr(stream):
|
||||||
m.raw_err(msg)
|
m.raw_err(msg)
|
||||||
stdout, stderr = capfd.readouterr()
|
stdout, stderr = capfd.readouterr()
|
||||||
assert stdout == ""
|
assert not stdout
|
||||||
assert stderr == msg
|
assert stderr == msg
|
||||||
assert stream.getvalue() == ""
|
assert not stream.getvalue()
|
||||||
|
|
||||||
stream = StringIO()
|
stream = StringIO()
|
||||||
with redirect_stderr(stream):
|
with redirect_stderr(stream):
|
||||||
m.captured_err(msg)
|
m.captured_err(msg)
|
||||||
stdout, stderr = capfd.readouterr()
|
stdout, stderr = capfd.readouterr()
|
||||||
assert stdout == ""
|
assert not stdout
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
assert stream.getvalue() == msg
|
assert stream.getvalue() == msg
|
||||||
|
|
||||||
|
|
||||||
@ -221,14 +221,13 @@ def test_redirect(capfd):
|
|||||||
m.raw_output(msg)
|
m.raw_output(msg)
|
||||||
stdout, stderr = capfd.readouterr()
|
stdout, stderr = capfd.readouterr()
|
||||||
assert stdout == msg
|
assert stdout == msg
|
||||||
assert stream.getvalue() == ""
|
assert not stream.getvalue()
|
||||||
|
|
||||||
stream = StringIO()
|
stream = StringIO()
|
||||||
with redirect_stdout(stream):
|
with redirect_stdout(stream), m.ostream_redirect():
|
||||||
with m.ostream_redirect():
|
m.raw_output(msg)
|
||||||
m.raw_output(msg)
|
|
||||||
stdout, stderr = capfd.readouterr()
|
stdout, stderr = capfd.readouterr()
|
||||||
assert stdout == ""
|
assert not stdout
|
||||||
assert stream.getvalue() == msg
|
assert stream.getvalue() == msg
|
||||||
|
|
||||||
stream = StringIO()
|
stream = StringIO()
|
||||||
@ -236,7 +235,7 @@ def test_redirect(capfd):
|
|||||||
m.raw_output(msg)
|
m.raw_output(msg)
|
||||||
stdout, stderr = capfd.readouterr()
|
stdout, stderr = capfd.readouterr()
|
||||||
assert stdout == msg
|
assert stdout == msg
|
||||||
assert stream.getvalue() == ""
|
assert not stream.getvalue()
|
||||||
|
|
||||||
|
|
||||||
def test_redirect_err(capfd):
|
def test_redirect_err(capfd):
|
||||||
@ -244,13 +243,12 @@ def test_redirect_err(capfd):
|
|||||||
msg2 = "StdErr"
|
msg2 = "StdErr"
|
||||||
|
|
||||||
stream = StringIO()
|
stream = StringIO()
|
||||||
with redirect_stderr(stream):
|
with redirect_stderr(stream), m.ostream_redirect(stdout=False):
|
||||||
with m.ostream_redirect(stdout=False):
|
m.raw_output(msg)
|
||||||
m.raw_output(msg)
|
m.raw_err(msg2)
|
||||||
m.raw_err(msg2)
|
|
||||||
stdout, stderr = capfd.readouterr()
|
stdout, stderr = capfd.readouterr()
|
||||||
assert stdout == msg
|
assert stdout == msg
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
assert stream.getvalue() == msg2
|
assert stream.getvalue() == msg2
|
||||||
|
|
||||||
|
|
||||||
@ -260,14 +258,12 @@ def test_redirect_both(capfd):
|
|||||||
|
|
||||||
stream = StringIO()
|
stream = StringIO()
|
||||||
stream2 = StringIO()
|
stream2 = StringIO()
|
||||||
with redirect_stdout(stream):
|
with redirect_stdout(stream), redirect_stderr(stream2), m.ostream_redirect():
|
||||||
with redirect_stderr(stream2):
|
m.raw_output(msg)
|
||||||
with m.ostream_redirect():
|
m.raw_err(msg2)
|
||||||
m.raw_output(msg)
|
|
||||||
m.raw_err(msg2)
|
|
||||||
stdout, stderr = capfd.readouterr()
|
stdout, stderr = capfd.readouterr()
|
||||||
assert stdout == ""
|
assert not stdout
|
||||||
assert stderr == ""
|
assert not stderr
|
||||||
assert stream.getvalue() == msg
|
assert stream.getvalue() == msg
|
||||||
assert stream2.getvalue() == msg2
|
assert stream2.getvalue() == msg2
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ def test_function_signatures(doc):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_named_arguments(msg):
|
def test_named_arguments():
|
||||||
assert m.kw_func0(5, 10) == "x=5, y=10"
|
assert m.kw_func0(5, 10) == "x=5, y=10"
|
||||||
|
|
||||||
assert m.kw_func1(5, 10) == "x=5, y=10"
|
assert m.kw_func1(5, 10) == "x=5, y=10"
|
||||||
@ -43,8 +43,7 @@ def test_named_arguments(msg):
|
|||||||
# noinspection PyArgumentList
|
# noinspection PyArgumentList
|
||||||
m.kw_func2(x=5, y=10, z=12)
|
m.kw_func2(x=5, y=10, z=12)
|
||||||
assert excinfo.match(
|
assert excinfo.match(
|
||||||
r"(?s)^kw_func2\(\): incompatible.*Invoked with: kwargs: ((x=5|y=10|z=12)(, |$))"
|
r"(?s)^kw_func2\(\): incompatible.*Invoked with: kwargs: ((x=5|y=10|z=12)(, |$)){3}$"
|
||||||
+ "{3}$"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
assert m.kw_func4() == "{13 17}"
|
assert m.kw_func4() == "{13 17}"
|
||||||
@ -59,7 +58,7 @@ def test_arg_and_kwargs():
|
|||||||
assert m.args_function(*args) == args
|
assert m.args_function(*args) == args
|
||||||
|
|
||||||
args = "a1", "a2"
|
args = "a1", "a2"
|
||||||
kwargs = dict(arg3="a3", arg4=4)
|
kwargs = {"arg3": "a3", "arg4": 4}
|
||||||
assert m.args_kwargs_function(*args, **kwargs) == (args, kwargs)
|
assert m.args_kwargs_function(*args, **kwargs) == (args, kwargs)
|
||||||
|
|
||||||
|
|
||||||
@ -177,7 +176,7 @@ def test_mixed_args_and_kwargs(msg):
|
|||||||
|
|
||||||
assert (
|
assert (
|
||||||
m.args_kwonly_kwargs_defaults.__doc__
|
m.args_kwonly_kwargs_defaults.__doc__
|
||||||
== "args_kwonly_kwargs_defaults(i: int = 1, j: float = 3.14159, *args, z: int = 42, **kwargs) -> tuple\n" # noqa: E501 line too long
|
== "args_kwonly_kwargs_defaults(i: int = 1, j: float = 3.14159, *args, z: int = 42, **kwargs) -> tuple\n"
|
||||||
)
|
)
|
||||||
assert m.args_kwonly_kwargs_defaults() == (1, 3.14159, (), 42, {})
|
assert m.args_kwonly_kwargs_defaults() == (1, 3.14159, (), 42, {})
|
||||||
assert m.args_kwonly_kwargs_defaults(2) == (2, 3.14159, (), 42, {})
|
assert m.args_kwonly_kwargs_defaults(2) == (2, 3.14159, (), 42, {})
|
||||||
@ -233,15 +232,15 @@ def test_keyword_only_args(msg):
|
|||||||
x.method(i=1, j=2)
|
x.method(i=1, j=2)
|
||||||
assert (
|
assert (
|
||||||
m.first_arg_kw_only.__init__.__doc__
|
m.first_arg_kw_only.__init__.__doc__
|
||||||
== "__init__(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, *, i: int = 0) -> None\n" # noqa: E501 line too long
|
== "__init__(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, *, i: int = 0) -> None\n"
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
m.first_arg_kw_only.method.__doc__
|
m.first_arg_kw_only.method.__doc__
|
||||||
== "method(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, *, i: int = 1, j: int = 2) -> None\n" # noqa: E501 line too long
|
== "method(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, *, i: int = 1, j: int = 2) -> None\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_positional_only_args(msg):
|
def test_positional_only_args():
|
||||||
assert m.pos_only_all(1, 2) == (1, 2)
|
assert m.pos_only_all(1, 2) == (1, 2)
|
||||||
assert m.pos_only_all(2, 1) == (2, 1)
|
assert m.pos_only_all(2, 1) == (2, 1)
|
||||||
|
|
||||||
@ -283,7 +282,7 @@ def test_positional_only_args(msg):
|
|||||||
# Mix it with args and kwargs:
|
# Mix it with args and kwargs:
|
||||||
assert (
|
assert (
|
||||||
m.args_kwonly_full_monty.__doc__
|
m.args_kwonly_full_monty.__doc__
|
||||||
== "args_kwonly_full_monty(arg0: int = 1, arg1: int = 2, /, j: float = 3.14159, *args, z: int = 42, **kwargs) -> tuple\n" # noqa: E501 line too long
|
== "args_kwonly_full_monty(arg0: int = 1, arg1: int = 2, /, j: float = 3.14159, *args, z: int = 42, **kwargs) -> tuple\n"
|
||||||
)
|
)
|
||||||
assert m.args_kwonly_full_monty() == (1, 2, 3.14159, (), 42, {})
|
assert m.args_kwonly_full_monty() == (1, 2, 3.14159, (), 42, {})
|
||||||
assert m.args_kwonly_full_monty(8) == (8, 2, 3.14159, (), 42, {})
|
assert m.args_kwonly_full_monty(8) == (8, 2, 3.14159, (), 42, {})
|
||||||
@ -326,18 +325,18 @@ def test_positional_only_args(msg):
|
|||||||
# https://github.com/pybind/pybind11/pull/3402#issuecomment-963341987
|
# https://github.com/pybind/pybind11/pull/3402#issuecomment-963341987
|
||||||
assert (
|
assert (
|
||||||
m.first_arg_kw_only.pos_only.__doc__
|
m.first_arg_kw_only.pos_only.__doc__
|
||||||
== "pos_only(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, /, i: int, j: int) -> None\n" # noqa: E501 line too long
|
== "pos_only(self: pybind11_tests.kwargs_and_defaults.first_arg_kw_only, /, i: int, j: int) -> None\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_signatures():
|
def test_signatures():
|
||||||
assert "kw_only_all(*, i: int, j: int) -> tuple\n" == m.kw_only_all.__doc__
|
assert m.kw_only_all.__doc__ == "kw_only_all(*, i: int, j: int) -> tuple\n"
|
||||||
assert "kw_only_mixed(i: int, *, j: int) -> tuple\n" == m.kw_only_mixed.__doc__
|
assert m.kw_only_mixed.__doc__ == "kw_only_mixed(i: int, *, j: int) -> tuple\n"
|
||||||
assert "pos_only_all(i: int, j: int, /) -> tuple\n" == m.pos_only_all.__doc__
|
assert m.pos_only_all.__doc__ == "pos_only_all(i: int, j: int, /) -> tuple\n"
|
||||||
assert "pos_only_mix(i: int, /, j: int) -> tuple\n" == m.pos_only_mix.__doc__
|
assert m.pos_only_mix.__doc__ == "pos_only_mix(i: int, /, j: int) -> tuple\n"
|
||||||
assert (
|
assert (
|
||||||
"pos_kw_only_mix(i: int, /, j: int, *, k: int) -> tuple\n"
|
m.pos_kw_only_mix.__doc__
|
||||||
== m.pos_kw_only_mix.__doc__
|
== "pos_kw_only_mix(i: int, /, j: int, *, k: int) -> tuple\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -177,6 +177,38 @@ struct RValueRefParam {
|
|||||||
std::size_t func4(std::string &&s) const & { return s.size(); }
|
std::size_t func4(std::string &&s) const & { return s.size(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace pybind11_tests {
|
||||||
|
namespace exercise_is_setter {
|
||||||
|
|
||||||
|
struct FieldBase {
|
||||||
|
int int_value() const { return int_value_; }
|
||||||
|
|
||||||
|
FieldBase &SetIntValue(int int_value) {
|
||||||
|
int_value_ = int_value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int int_value_ = -99;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Field : FieldBase {};
|
||||||
|
|
||||||
|
void add_bindings(py::module &m) {
|
||||||
|
py::module sm = m.def_submodule("exercise_is_setter");
|
||||||
|
// NOTE: FieldBase is not wrapped, therefore ...
|
||||||
|
py::class_<Field>(sm, "Field")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def_property(
|
||||||
|
"int_value",
|
||||||
|
&Field::int_value,
|
||||||
|
&Field::SetIntValue // ... the `FieldBase &` return value here cannot be converted.
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace exercise_is_setter
|
||||||
|
} // namespace pybind11_tests
|
||||||
|
|
||||||
TEST_SUBMODULE(methods_and_attributes, m) {
|
TEST_SUBMODULE(methods_and_attributes, m) {
|
||||||
// test_methods_and_attributes
|
// test_methods_and_attributes
|
||||||
py::class_<ExampleMandA> emna(m, "ExampleMandA");
|
py::class_<ExampleMandA> emna(m, "ExampleMandA");
|
||||||
@ -456,4 +488,6 @@ TEST_SUBMODULE(methods_and_attributes, m) {
|
|||||||
.def("func2", &RValueRefParam::func2)
|
.def("func2", &RValueRefParam::func2)
|
||||||
.def("func3", &RValueRefParam::func3)
|
.def("func3", &RValueRefParam::func3)
|
||||||
.def("func4", &RValueRefParam::func4);
|
.def("func4", &RValueRefParam::func4);
|
||||||
|
|
||||||
|
pybind11_tests::exercise_is_setter::add_bindings(m);
|
||||||
}
|
}
|
||||||
|
@ -183,9 +183,9 @@ def test_static_properties():
|
|||||||
|
|
||||||
# Only static attributes can be deleted
|
# Only static attributes can be deleted
|
||||||
del m.TestPropertiesOverride.def_readonly_static
|
del m.TestPropertiesOverride.def_readonly_static
|
||||||
|
assert hasattr(m.TestPropertiesOverride, "def_readonly_static")
|
||||||
assert (
|
assert (
|
||||||
hasattr(m.TestPropertiesOverride, "def_readonly_static")
|
m.TestPropertiesOverride.def_readonly_static
|
||||||
and m.TestPropertiesOverride.def_readonly_static
|
|
||||||
is m.TestProperties.def_readonly_static
|
is m.TestProperties.def_readonly_static
|
||||||
)
|
)
|
||||||
assert "def_readonly_static" not in m.TestPropertiesOverride.__dict__
|
assert "def_readonly_static" not in m.TestPropertiesOverride.__dict__
|
||||||
@ -256,10 +256,7 @@ def test_no_mixed_overloads():
|
|||||||
|
|
||||||
@pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"])
|
@pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"])
|
||||||
def test_property_return_value_policies(access):
|
def test_property_return_value_policies(access):
|
||||||
if not access.startswith("static"):
|
obj = m.TestPropRVP() if not access.startswith("static") else m.TestPropRVP
|
||||||
obj = m.TestPropRVP()
|
|
||||||
else:
|
|
||||||
obj = m.TestPropRVP
|
|
||||||
|
|
||||||
ref = getattr(obj, access + "_ref")
|
ref = getattr(obj, access + "_ref")
|
||||||
assert ref.value == 1
|
assert ref.value == 1
|
||||||
@ -525,3 +522,12 @@ def test_rvalue_ref_param():
|
|||||||
assert r.func2("1234") == 4
|
assert r.func2("1234") == 4
|
||||||
assert r.func3("12345") == 5
|
assert r.func3("12345") == 5
|
||||||
assert r.func4("123456") == 6
|
assert r.func4("123456") == 6
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_setter():
|
||||||
|
fld = m.exercise_is_setter.Field()
|
||||||
|
assert fld.int_value == -99
|
||||||
|
setter_return = fld.int_value = 100
|
||||||
|
assert isinstance(setter_return, int)
|
||||||
|
assert setter_return == 100
|
||||||
|
assert fld.int_value == 100
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import builtins
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import env
|
import env
|
||||||
@ -61,7 +63,6 @@ def test_importing():
|
|||||||
from pybind11_tests.modules import OD
|
from pybind11_tests.modules import OD
|
||||||
|
|
||||||
assert OD is OrderedDict
|
assert OD is OrderedDict
|
||||||
assert str(OD([(1, "a"), (2, "b")])) == "OrderedDict([(1, 'a'), (2, 'b')])"
|
|
||||||
|
|
||||||
|
|
||||||
def test_pydoc():
|
def test_pydoc():
|
||||||
@ -86,12 +87,7 @@ def test_builtin_key_type():
|
|||||||
|
|
||||||
Previous versions of pybind11 would add a unicode key in python 2.
|
Previous versions of pybind11 would add a unicode key in python 2.
|
||||||
"""
|
"""
|
||||||
if hasattr(__builtins__, "keys"):
|
assert all(type(k) == str for k in dir(builtins))
|
||||||
keys = __builtins__.keys()
|
|
||||||
else: # this is to make pypy happy since builtins is different there.
|
|
||||||
keys = __builtins__.__dict__.keys()
|
|
||||||
|
|
||||||
assert {type(k) for k in keys} == {str}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail("env.PYPY", reason="PyModule_GetName()")
|
@pytest.mark.xfail("env.PYPY", reason="PyModule_GetName()")
|
||||||
|
@ -523,4 +523,30 @@ TEST_SUBMODULE(numpy_array, sm) {
|
|||||||
sm.def("test_fmt_desc_const_double", [](const py::array_t<const double> &) {});
|
sm.def("test_fmt_desc_const_double", [](const py::array_t<const double> &) {});
|
||||||
|
|
||||||
sm.def("round_trip_float", [](double d) { return d; });
|
sm.def("round_trip_float", [](double d) { return d; });
|
||||||
|
|
||||||
|
sm.def("pass_array_pyobject_ptr_return_sum_str_values",
|
||||||
|
[](const py::array_t<PyObject *> &objs) {
|
||||||
|
std::string sum_str_values;
|
||||||
|
for (const auto &obj : objs) {
|
||||||
|
sum_str_values += py::str(obj.attr("value"));
|
||||||
|
}
|
||||||
|
return sum_str_values;
|
||||||
|
});
|
||||||
|
|
||||||
|
sm.def("pass_array_pyobject_ptr_return_as_list",
|
||||||
|
[](const py::array_t<PyObject *> &objs) -> py::list { return objs; });
|
||||||
|
|
||||||
|
sm.def("return_array_pyobject_ptr_cpp_loop", [](const py::list &objs) {
|
||||||
|
py::size_t arr_size = py::len(objs);
|
||||||
|
py::array_t<PyObject *> arr_from_list(static_cast<py::ssize_t>(arr_size));
|
||||||
|
PyObject **data = arr_from_list.mutable_data();
|
||||||
|
for (py::size_t i = 0; i < arr_size; i++) {
|
||||||
|
assert(data[i] == nullptr);
|
||||||
|
data[i] = py::cast<PyObject *>(objs[i].attr("value"));
|
||||||
|
}
|
||||||
|
return arr_from_list;
|
||||||
|
});
|
||||||
|
|
||||||
|
sm.def("return_array_pyobject_ptr_from_list",
|
||||||
|
[](const py::list &objs) -> py::array_t<PyObject *> { return objs; });
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ def test_dtypes():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture()
|
||||||
def arr():
|
def arr():
|
||||||
return np.array([[1, 2, 3], [4, 5, 6]], "=u2")
|
return np.array([[1, 2, 3], [4, 5, 6]], "=u2")
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ def test_array_attributes():
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"args, ret", [([], 0), ([0], 0), ([1], 3), ([0, 1], 1), ([1, 2], 5)]
|
("args", "ret"), [([], 0), ([0], 0), ([1], 3), ([0, 1], 1), ([1, 2], 5)]
|
||||||
)
|
)
|
||||||
def test_index_offset(arr, args, ret):
|
def test_index_offset(arr, args, ret):
|
||||||
assert m.index_at(arr, *args) == ret
|
assert m.index_at(arr, *args) == ret
|
||||||
@ -93,7 +93,7 @@ def test_dim_check_fail(arr):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"args, ret",
|
("args", "ret"),
|
||||||
[
|
[
|
||||||
([], [1, 2, 3, 4, 5, 6]),
|
([], [1, 2, 3, 4, 5, 6]),
|
||||||
([1], [4, 5, 6]),
|
([1], [4, 5, 6]),
|
||||||
@ -211,12 +211,14 @@ def test_wrap():
|
|||||||
assert b[0, 0] == 1234
|
assert b[0, 0] == 1234
|
||||||
|
|
||||||
a1 = np.array([1, 2], dtype=np.int16)
|
a1 = np.array([1, 2], dtype=np.int16)
|
||||||
assert a1.flags.owndata and a1.base is None
|
assert a1.flags.owndata
|
||||||
|
assert a1.base is None
|
||||||
a2 = m.wrap(a1)
|
a2 = m.wrap(a1)
|
||||||
assert_references(a1, a2)
|
assert_references(a1, a2)
|
||||||
|
|
||||||
a1 = np.array([[1, 2], [3, 4]], dtype=np.float32, order="F")
|
a1 = np.array([[1, 2], [3, 4]], dtype=np.float32, order="F")
|
||||||
assert a1.flags.owndata and a1.base is None
|
assert a1.flags.owndata
|
||||||
|
assert a1.base is None
|
||||||
a2 = m.wrap(a1)
|
a2 = m.wrap(a1)
|
||||||
assert_references(a1, a2)
|
assert_references(a1, a2)
|
||||||
|
|
||||||
@ -451,13 +453,15 @@ def test_array_resize():
|
|||||||
try:
|
try:
|
||||||
m.array_resize3(a, 3, True)
|
m.array_resize3(a, 3, True)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
assert str(e).startswith("cannot resize an array")
|
assert str(e).startswith("cannot resize an array") # noqa: PT017
|
||||||
# transposed array doesn't own data
|
# transposed array doesn't own data
|
||||||
b = a.transpose()
|
b = a.transpose()
|
||||||
try:
|
try:
|
||||||
m.array_resize3(b, 3, False)
|
m.array_resize3(b, 3, False)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
assert str(e).startswith("cannot resize this array: it does not own its data")
|
assert str(e).startswith( # noqa: PT017
|
||||||
|
"cannot resize this array: it does not own its data"
|
||||||
|
)
|
||||||
# ... but reshape should be fine
|
# ... but reshape should be fine
|
||||||
m.array_reshape2(b)
|
m.array_reshape2(b)
|
||||||
assert b.shape == (8, 8)
|
assert b.shape == (8, 8)
|
||||||
@ -591,3 +595,74 @@ def test_round_trip_float():
|
|||||||
arr = np.zeros((), np.float64)
|
arr = np.zeros((), np.float64)
|
||||||
arr[()] = 37.2
|
arr[()] = 37.2
|
||||||
assert m.round_trip_float(arr) == 37.2
|
assert m.round_trip_float(arr) == 37.2
|
||||||
|
|
||||||
|
|
||||||
|
# HINT: An easy and robust way (although only manual unfortunately) to check for
|
||||||
|
# ref-count leaks in the test_.*pyobject_ptr.* functions below is to
|
||||||
|
# * temporarily insert `while True:` (one-by-one),
|
||||||
|
# * run this test, and
|
||||||
|
# * run the Linux `top` command in another shell to visually monitor
|
||||||
|
# `RES` for a minute or two.
|
||||||
|
# If there is a leak, it is usually evident in seconds because the `RES`
|
||||||
|
# value increases without bounds. (Don't forget to Ctrl-C the test!)
|
||||||
|
|
||||||
|
|
||||||
|
# For use as a temporary user-defined object, to maximize sensitivity of the tests below:
|
||||||
|
# * Ref-count leaks will be immediately evident.
|
||||||
|
# * Sanitizers are much more likely to detect heap-use-after-free due to
|
||||||
|
# other ref-count bugs.
|
||||||
|
class PyValueHolder:
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
|
||||||
|
def WrapWithPyValueHolder(*values):
|
||||||
|
return [PyValueHolder(v) for v in values]
|
||||||
|
|
||||||
|
|
||||||
|
def UnwrapPyValueHolder(vhs):
|
||||||
|
return [vh.value for vh in vhs]
|
||||||
|
|
||||||
|
|
||||||
|
def test_pass_array_pyobject_ptr_return_sum_str_values_ndarray():
|
||||||
|
# Intentionally all temporaries, do not change.
|
||||||
|
assert (
|
||||||
|
m.pass_array_pyobject_ptr_return_sum_str_values(
|
||||||
|
np.array(WrapWithPyValueHolder(-3, "four", 5.0), dtype=object)
|
||||||
|
)
|
||||||
|
== "-3four5.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_pass_array_pyobject_ptr_return_sum_str_values_list():
|
||||||
|
# Intentionally all temporaries, do not change.
|
||||||
|
assert (
|
||||||
|
m.pass_array_pyobject_ptr_return_sum_str_values(
|
||||||
|
WrapWithPyValueHolder(2, "three", -4.0)
|
||||||
|
)
|
||||||
|
== "2three-4.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_pass_array_pyobject_ptr_return_as_list():
|
||||||
|
# Intentionally all temporaries, do not change.
|
||||||
|
assert UnwrapPyValueHolder(
|
||||||
|
m.pass_array_pyobject_ptr_return_as_list(
|
||||||
|
np.array(WrapWithPyValueHolder(-1, "two", 3.0), dtype=object)
|
||||||
|
)
|
||||||
|
) == [-1, "two", 3.0]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("return_array_pyobject_ptr", "unwrap"),
|
||||||
|
[
|
||||||
|
(m.return_array_pyobject_ptr_cpp_loop, list),
|
||||||
|
(m.return_array_pyobject_ptr_from_list, UnwrapPyValueHolder),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_return_array_pyobject_ptr_cpp_loop(return_array_pyobject_ptr, unwrap):
|
||||||
|
# Intentionally all temporaries, do not change.
|
||||||
|
arr_from_list = return_array_pyobject_ptr(WrapWithPyValueHolder(6, "seven", -8.0))
|
||||||
|
assert isinstance(arr_from_list, np.ndarray)
|
||||||
|
assert arr_from_list.dtype == np.dtype("O")
|
||||||
|
assert unwrap(arr_from_list) == [6, "seven", -8.0]
|
||||||
|
@ -130,14 +130,10 @@ def test_dtype(simple_dtype):
|
|||||||
partial_nested_fmt(),
|
partial_nested_fmt(),
|
||||||
"[('a','S3'),('b','S3')]",
|
"[('a','S3'),('b','S3')]",
|
||||||
(
|
(
|
||||||
"{{'names':['a','b','c','d'],"
|
"{'names':['a','b','c','d'],"
|
||||||
+ "'formats':[('S4',(3,)),('"
|
f"'formats':[('S4',(3,)),('{e}i4',(2,)),('u1',(3,)),('{e}f4',(4,2))],"
|
||||||
+ e
|
"'offsets':[0,12,20,24],'itemsize':56}"
|
||||||
+ "i4',(2,)),('u1',(3,)),('"
|
),
|
||||||
+ e
|
|
||||||
+ "f4',(4,2))],"
|
|
||||||
+ "'offsets':[0,12,20,24],'itemsize':56}}"
|
|
||||||
).format(e=e),
|
|
||||||
"[('e1','" + e + "i8'),('e2','u1')]",
|
"[('e1','" + e + "i8'),('e2','u1')]",
|
||||||
"[('x','i1'),('y','" + e + "u8')]",
|
"[('x','i1'),('y','" + e + "u8')]",
|
||||||
"[('cflt','" + e + "c8'),('cdbl','" + e + "c16')]",
|
"[('cflt','" + e + "c8'),('cdbl','" + e + "c16')]",
|
||||||
@ -291,19 +287,17 @@ def test_array_array():
|
|||||||
|
|
||||||
arr = m.create_array_array(3)
|
arr = m.create_array_array(3)
|
||||||
assert str(arr.dtype).replace(" ", "") == (
|
assert str(arr.dtype).replace(" ", "") == (
|
||||||
"{{'names':['a','b','c','d'],"
|
"{'names':['a','b','c','d'],"
|
||||||
+ "'formats':[('S4',(3,)),('"
|
f"'formats':[('S4',(3,)),('{e}i4',(2,)),('u1',(3,)),('{e}f4',(4,2))],"
|
||||||
+ e
|
"'offsets':[0,12,20,24],'itemsize':56}"
|
||||||
+ "i4',(2,)),('u1',(3,)),('{e}f4',(4,2))],"
|
)
|
||||||
+ "'offsets':[0,12,20,24],'itemsize':56}}"
|
|
||||||
).format(e=e)
|
|
||||||
assert m.print_array_array(arr) == [
|
assert m.print_array_array(arr) == [
|
||||||
"a={{A,B,C,D},{K,L,M,N},{U,V,W,X}},b={0,1},"
|
"a={{A,B,C,D},{K,L,M,N},{U,V,W,X}},b={0,1},"
|
||||||
+ "c={0,1,2},d={{0,1},{10,11},{20,21},{30,31}}",
|
"c={0,1,2},d={{0,1},{10,11},{20,21},{30,31}}",
|
||||||
"a={{W,X,Y,Z},{G,H,I,J},{Q,R,S,T}},b={1000,1001},"
|
"a={{W,X,Y,Z},{G,H,I,J},{Q,R,S,T}},b={1000,1001},"
|
||||||
+ "c={10,11,12},d={{100,101},{110,111},{120,121},{130,131}}",
|
"c={10,11,12},d={{100,101},{110,111},{120,121},{130,131}}",
|
||||||
"a={{S,T,U,V},{C,D,E,F},{M,N,O,P}},b={2000,2001},"
|
"a={{S,T,U,V},{C,D,E,F},{M,N,O,P}},b={2000,2001},"
|
||||||
+ "c={20,21,22},d={{200,201},{210,211},{220,221},{230,231}}",
|
"c={20,21,22},d={{200,201},{210,211},{220,221},{230,231}}",
|
||||||
]
|
]
|
||||||
assert arr["a"].tolist() == [
|
assert arr["a"].tolist() == [
|
||||||
[b"ABCD", b"KLMN", b"UVWX"],
|
[b"ABCD", b"KLMN", b"UVWX"],
|
||||||
|
@ -149,7 +149,7 @@ def test_docs(doc):
|
|||||||
doc(m.vectorized_func)
|
doc(m.vectorized_func)
|
||||||
== """
|
== """
|
||||||
vectorized_func(arg0: numpy.ndarray[numpy.int32], arg1: numpy.ndarray[numpy.float32], arg2: numpy.ndarray[numpy.float64]) -> object
|
vectorized_func(arg0: numpy.ndarray[numpy.int32], arg1: numpy.ndarray[numpy.float32], arg2: numpy.ndarray[numpy.float64]) -> object
|
||||||
""" # noqa: E501 line too long
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -260,6 +260,15 @@ TEST_SUBMODULE(pytypes, m) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
m.def("return_capsule_with_destructor_3", []() {
|
||||||
|
py::print("creating capsule");
|
||||||
|
auto cap = py::capsule((void *) 1233, "oname", [](void *ptr) {
|
||||||
|
py::print("destructing capsule: {}"_s.format((size_t) ptr));
|
||||||
|
});
|
||||||
|
py::print("original name: {}"_s.format(cap.name()));
|
||||||
|
return cap;
|
||||||
|
});
|
||||||
|
|
||||||
m.def("return_renamed_capsule_with_destructor_2", []() {
|
m.def("return_renamed_capsule_with_destructor_2", []() {
|
||||||
py::print("creating capsule");
|
py::print("creating capsule");
|
||||||
auto cap = py::capsule((void *) 1234, [](void *ptr) {
|
auto cap = py::capsule((void *) 1234, [](void *ptr) {
|
||||||
|
@ -15,7 +15,7 @@ def test_obj_class_name():
|
|||||||
assert m.obj_class_name([]) == "list"
|
assert m.obj_class_name([]) == "list"
|
||||||
|
|
||||||
|
|
||||||
def test_handle_from_move_only_type_with_operator_PyObject(): # noqa: N802
|
def test_handle_from_move_only_type_with_operator_PyObject():
|
||||||
assert m.handle_from_move_only_type_with_operator_PyObject_ncnst()
|
assert m.handle_from_move_only_type_with_operator_PyObject_ncnst()
|
||||||
assert m.handle_from_move_only_type_with_operator_PyObject_const()
|
assert m.handle_from_move_only_type_with_operator_PyObject_const()
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ def test_iterator(doc):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"pytype, from_iter_func",
|
("pytype", "from_iter_func"),
|
||||||
[
|
[
|
||||||
(frozenset, m.get_frozenset_from_iterable),
|
(frozenset, m.get_frozenset_from_iterable),
|
||||||
(list, m.get_list_from_iterable),
|
(list, m.get_list_from_iterable),
|
||||||
@ -87,7 +87,7 @@ def test_list(capture, doc):
|
|||||||
assert doc(m.print_list) == "print_list(arg0: list) -> None"
|
assert doc(m.print_list) == "print_list(arg0: list) -> None"
|
||||||
|
|
||||||
|
|
||||||
def test_none(capture, doc):
|
def test_none(doc):
|
||||||
assert doc(m.get_none) == "get_none() -> None"
|
assert doc(m.get_none) == "get_none() -> None"
|
||||||
assert doc(m.print_none) == "print_none(arg0: None) -> None"
|
assert doc(m.print_none) == "print_none(arg0: None) -> None"
|
||||||
|
|
||||||
@ -182,10 +182,10 @@ class CustomContains:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"arg,func",
|
("arg", "func"),
|
||||||
[
|
[
|
||||||
(set(), m.anyset_contains),
|
(set(), m.anyset_contains),
|
||||||
(dict(), m.dict_contains),
|
({}, m.dict_contains),
|
||||||
(CustomContains(), m.obj_contains),
|
(CustomContains(), m.obj_contains),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -273,7 +273,7 @@ def test_bytes(doc):
|
|||||||
assert doc(m.bytes_from_str) == "bytes_from_str() -> bytes"
|
assert doc(m.bytes_from_str) == "bytes_from_str() -> bytes"
|
||||||
|
|
||||||
|
|
||||||
def test_bytearray(doc):
|
def test_bytearray():
|
||||||
assert m.bytearray_from_char_ssize_t().decode() == "$%"
|
assert m.bytearray_from_char_ssize_t().decode() == "$%"
|
||||||
assert m.bytearray_from_char_size_t().decode() == "@$!"
|
assert m.bytearray_from_char_size_t().decode() == "@$!"
|
||||||
assert m.bytearray_from_string().decode() == "foo"
|
assert m.bytearray_from_string().decode() == "foo"
|
||||||
@ -319,6 +319,19 @@ def test_capsule(capture):
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
with capture:
|
||||||
|
a = m.return_capsule_with_destructor_3()
|
||||||
|
del a
|
||||||
|
pytest.gc_collect()
|
||||||
|
assert (
|
||||||
|
capture.unordered
|
||||||
|
== """
|
||||||
|
creating capsule
|
||||||
|
destructing capsule: 1233
|
||||||
|
original name: oname
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
with capture:
|
with capture:
|
||||||
a = m.return_renamed_capsule_with_destructor_2()
|
a = m.return_renamed_capsule_with_destructor_2()
|
||||||
del a
|
del a
|
||||||
@ -385,7 +398,7 @@ def test_accessors():
|
|||||||
assert d["implicit_list"] == [1, 2, 3]
|
assert d["implicit_list"] == [1, 2, 3]
|
||||||
assert all(x in TestObject.__dict__ for x in d["implicit_dict"])
|
assert all(x in TestObject.__dict__ for x in d["implicit_dict"])
|
||||||
|
|
||||||
assert m.tuple_accessor(tuple()) == (0, 1, 2)
|
assert m.tuple_accessor(()) == (0, 1, 2)
|
||||||
|
|
||||||
d = m.accessor_assignment()
|
d = m.accessor_assignment()
|
||||||
assert d["get"] == 0
|
assert d["get"] == 0
|
||||||
@ -475,7 +488,7 @@ def test_pybind11_str_raw_str():
|
|||||||
assert cvt({}) == "{}"
|
assert cvt({}) == "{}"
|
||||||
assert cvt({3: 4}) == "{3: 4}"
|
assert cvt({3: 4}) == "{3: 4}"
|
||||||
assert cvt(set()) == "set()"
|
assert cvt(set()) == "set()"
|
||||||
assert cvt({3, 3}) == "{3}"
|
assert cvt({3}) == "{3}"
|
||||||
|
|
||||||
valid_orig = "DZ"
|
valid_orig = "DZ"
|
||||||
valid_utf8 = valid_orig.encode("utf-8")
|
valid_utf8 = valid_orig.encode("utf-8")
|
||||||
@ -536,7 +549,7 @@ def test_print(capture):
|
|||||||
assert str(excinfo.value) == "Unable to convert call argument " + (
|
assert str(excinfo.value) == "Unable to convert call argument " + (
|
||||||
"'1' of type 'UnregisteredType' to Python object"
|
"'1' of type 'UnregisteredType' to Python object"
|
||||||
if detailed_error_messages_enabled
|
if detailed_error_messages_enabled
|
||||||
else "to Python object (#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)"
|
else "'1' to Python object (#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -593,7 +606,7 @@ def test_issue2361():
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"method, args, fmt, expected_view",
|
("method", "args", "fmt", "expected_view"),
|
||||||
[
|
[
|
||||||
(m.test_memoryview_object, (b"red",), "B", b"red"),
|
(m.test_memoryview_object, (b"red",), "B", b"red"),
|
||||||
(m.test_memoryview_buffer_info, (b"green",), "B", b"green"),
|
(m.test_memoryview_buffer_info, (b"green",), "B", b"green"),
|
||||||
@ -651,7 +664,7 @@ def test_memoryview_from_memory():
|
|||||||
|
|
||||||
|
|
||||||
def test_builtin_functions():
|
def test_builtin_functions():
|
||||||
assert m.get_len([i for i in range(42)]) == 42
|
assert m.get_len(list(range(42))) == 42
|
||||||
with pytest.raises(TypeError) as exc_info:
|
with pytest.raises(TypeError) as exc_info:
|
||||||
m.get_len(i for i in range(42))
|
m.get_len(i for i in range(42))
|
||||||
assert str(exc_info.value) in [
|
assert str(exc_info.value) in [
|
||||||
@ -695,7 +708,7 @@ def test_pass_bytes_or_unicode_to_string_types():
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"create_weakref, create_weakref_with_callback",
|
("create_weakref", "create_weakref_with_callback"),
|
||||||
[
|
[
|
||||||
(m.weakref_from_handle, m.weakref_from_handle_and_function),
|
(m.weakref_from_handle, m.weakref_from_handle_and_function),
|
||||||
(m.weakref_from_object, m.weakref_from_object_and_function),
|
(m.weakref_from_object, m.weakref_from_object_and_function),
|
||||||
@ -710,7 +723,7 @@ def test_weakref(create_weakref, create_weakref_with_callback):
|
|||||||
|
|
||||||
callback_called = False
|
callback_called = False
|
||||||
|
|
||||||
def callback(wr):
|
def callback(_):
|
||||||
nonlocal callback_called
|
nonlocal callback_called
|
||||||
callback_called = True
|
callback_called = True
|
||||||
|
|
||||||
@ -730,7 +743,7 @@ def test_weakref(create_weakref, create_weakref_with_callback):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"create_weakref, has_callback",
|
("create_weakref", "has_callback"),
|
||||||
[
|
[
|
||||||
(m.weakref_from_handle, False),
|
(m.weakref_from_handle, False),
|
||||||
(m.weakref_from_object, False),
|
(m.weakref_from_object, False),
|
||||||
@ -748,10 +761,7 @@ def test_weakref_err(create_weakref, has_callback):
|
|||||||
ob = C()
|
ob = C()
|
||||||
# Should raise TypeError on CPython
|
# Should raise TypeError on CPython
|
||||||
with pytest.raises(TypeError) if not env.PYPY else contextlib.nullcontext():
|
with pytest.raises(TypeError) if not env.PYPY else contextlib.nullcontext():
|
||||||
if has_callback:
|
_ = create_weakref(ob, callback) if has_callback else create_weakref(ob)
|
||||||
_ = create_weakref(ob, callback)
|
|
||||||
else:
|
|
||||||
_ = create_weakref(ob)
|
|
||||||
|
|
||||||
|
|
||||||
def test_cpp_iterators():
|
def test_cpp_iterators():
|
||||||
@ -814,33 +824,36 @@ def test_populate_obj_str_attrs():
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"a,b", [("foo", "bar"), (1, 2), (1.0, 2.0), (list(range(3)), list(range(3, 6)))]
|
("a", "b"),
|
||||||
|
[("foo", "bar"), (1, 2), (1.0, 2.0), (list(range(3)), list(range(3, 6)))],
|
||||||
)
|
)
|
||||||
def test_inplace_append(a, b):
|
def test_inplace_append(a, b):
|
||||||
expected = a + b
|
expected = a + b
|
||||||
assert m.inplace_append(a, b) == expected
|
assert m.inplace_append(a, b) == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("a,b", [(3, 2), (3.0, 2.0), (set(range(3)), set(range(2)))])
|
@pytest.mark.parametrize(
|
||||||
|
("a", "b"), [(3, 2), (3.0, 2.0), (set(range(3)), set(range(2)))]
|
||||||
|
)
|
||||||
def test_inplace_subtract(a, b):
|
def test_inplace_subtract(a, b):
|
||||||
expected = a - b
|
expected = a - b
|
||||||
assert m.inplace_subtract(a, b) == expected
|
assert m.inplace_subtract(a, b) == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("a,b", [(3, 2), (3.0, 2.0), ([1], 3)])
|
@pytest.mark.parametrize(("a", "b"), [(3, 2), (3.0, 2.0), ([1], 3)])
|
||||||
def test_inplace_multiply(a, b):
|
def test_inplace_multiply(a, b):
|
||||||
expected = a * b
|
expected = a * b
|
||||||
assert m.inplace_multiply(a, b) == expected
|
assert m.inplace_multiply(a, b) == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("a,b", [(6, 3), (6.0, 3.0)])
|
@pytest.mark.parametrize(("a", "b"), [(6, 3), (6.0, 3.0)])
|
||||||
def test_inplace_divide(a, b):
|
def test_inplace_divide(a, b):
|
||||||
expected = a / b
|
expected = a / b
|
||||||
assert m.inplace_divide(a, b) == expected
|
assert m.inplace_divide(a, b) == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"a,b",
|
("a", "b"),
|
||||||
[
|
[
|
||||||
(False, True),
|
(False, True),
|
||||||
(
|
(
|
||||||
@ -857,7 +870,7 @@ def test_inplace_or(a, b):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"a,b",
|
("a", "b"),
|
||||||
[
|
[
|
||||||
(True, False),
|
(True, False),
|
||||||
(
|
(
|
||||||
@ -873,13 +886,13 @@ def test_inplace_and(a, b):
|
|||||||
assert m.inplace_and(a, b) == expected
|
assert m.inplace_and(a, b) == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("a,b", [(8, 1), (-3, 2)])
|
@pytest.mark.parametrize(("a", "b"), [(8, 1), (-3, 2)])
|
||||||
def test_inplace_lshift(a, b):
|
def test_inplace_lshift(a, b):
|
||||||
expected = a << b
|
expected = a << b
|
||||||
assert m.inplace_lshift(a, b) == expected
|
assert m.inplace_lshift(a, b) == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("a,b", [(8, 1), (-2, 2)])
|
@pytest.mark.parametrize(("a", "b"), [(8, 1), (-2, 2)])
|
||||||
def test_inplace_rshift(a, b):
|
def test_inplace_rshift(a, b):
|
||||||
expected = a >> b
|
expected = a >> b
|
||||||
assert m.inplace_rshift(a, b) == expected
|
assert m.inplace_rshift(a, b) == expected
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from pytest import approx
|
from pytest import approx # noqa: PT013
|
||||||
|
|
||||||
from pybind11_tests import ConstructorStats
|
from pybind11_tests import ConstructorStats
|
||||||
from pybind11_tests import sequences_and_iterators as m
|
from pybind11_tests import sequences_and_iterators as m
|
||||||
@ -103,7 +103,8 @@ def test_sequence():
|
|||||||
|
|
||||||
assert "Sequence" in repr(s)
|
assert "Sequence" in repr(s)
|
||||||
assert len(s) == 5
|
assert len(s) == 5
|
||||||
assert s[0] == 0 and s[3] == 0
|
assert s[0] == 0
|
||||||
|
assert s[3] == 0
|
||||||
assert 12.34 not in s
|
assert 12.34 not in s
|
||||||
s[0], s[3] = 12.34, 56.78
|
s[0], s[3] = 12.34, 56.78
|
||||||
assert 12.34 in s
|
assert 12.34 in s
|
||||||
@ -245,7 +246,7 @@ def test_iterator_rvp():
|
|||||||
|
|
||||||
def test_carray_iterator():
|
def test_carray_iterator():
|
||||||
"""#4100: Check for proper iterator overload with C-Arrays"""
|
"""#4100: Check for proper iterator overload with C-Arrays"""
|
||||||
args_gt = list(float(i) for i in range(3))
|
args_gt = [float(i) for i in range(3)]
|
||||||
arr_h = m.CArrayHolder(*args_gt)
|
arr_h = m.CArrayHolder(*args_gt)
|
||||||
args = list(arr_h)
|
args = list(arr_h)
|
||||||
assert args_gt == args
|
assert args_gt == args
|
||||||
|
@ -14,7 +14,7 @@ def test_vector(doc):
|
|||||||
|
|
||||||
assert m.cast_bool_vector() == [True, False]
|
assert m.cast_bool_vector() == [True, False]
|
||||||
assert m.load_bool_vector([True, False])
|
assert m.load_bool_vector([True, False])
|
||||||
assert m.load_bool_vector(tuple([True, False]))
|
assert m.load_bool_vector((True, False))
|
||||||
|
|
||||||
assert doc(m.cast_vector) == "cast_vector() -> List[int]"
|
assert doc(m.cast_vector) == "cast_vector() -> List[int]"
|
||||||
assert doc(m.load_vector) == "load_vector(arg0: List[int]) -> bool"
|
assert doc(m.load_vector) == "load_vector(arg0: List[int]) -> bool"
|
||||||
@ -23,7 +23,7 @@ def test_vector(doc):
|
|||||||
assert m.cast_ptr_vector() == ["lvalue", "lvalue"]
|
assert m.cast_ptr_vector() == ["lvalue", "lvalue"]
|
||||||
|
|
||||||
|
|
||||||
def test_deque(doc):
|
def test_deque():
|
||||||
"""std::deque <-> list"""
|
"""std::deque <-> list"""
|
||||||
lst = m.cast_deque()
|
lst = m.cast_deque()
|
||||||
assert lst == [1]
|
assert lst == [1]
|
||||||
@ -39,8 +39,11 @@ def test_array(doc):
|
|||||||
assert m.load_array(lst)
|
assert m.load_array(lst)
|
||||||
assert m.load_array(tuple(lst))
|
assert m.load_array(tuple(lst))
|
||||||
|
|
||||||
assert doc(m.cast_array) == "cast_array() -> List[int[2]]"
|
assert doc(m.cast_array) == "cast_array() -> Annotated[List[int], FixedSize(2)]"
|
||||||
assert doc(m.load_array) == "load_array(arg0: List[int[2]]) -> bool"
|
assert (
|
||||||
|
doc(m.load_array)
|
||||||
|
== "load_array(arg0: Annotated[List[int], FixedSize(2)]) -> bool"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_valarray(doc):
|
def test_valarray(doc):
|
||||||
@ -95,7 +98,8 @@ def test_recursive_casting():
|
|||||||
|
|
||||||
# Issue #853 test case:
|
# Issue #853 test case:
|
||||||
z = m.cast_unique_ptr_vector()
|
z = m.cast_unique_ptr_vector()
|
||||||
assert z[0].value == 7 and z[1].value == 42
|
assert z[0].value == 7
|
||||||
|
assert z[1].value == 42
|
||||||
|
|
||||||
|
|
||||||
def test_move_out_container():
|
def test_move_out_container():
|
||||||
@ -366,7 +370,7 @@ def test_issue_1561():
|
|||||||
"""check fix for issue #1561"""
|
"""check fix for issue #1561"""
|
||||||
bar = m.Issue1561Outer()
|
bar = m.Issue1561Outer()
|
||||||
bar.list = [m.Issue1561Inner("bar")]
|
bar.list = [m.Issue1561Inner("bar")]
|
||||||
bar.list
|
assert bar.list
|
||||||
assert bar.list[0].data == "bar"
|
assert bar.list[0].data == "bar"
|
||||||
|
|
||||||
|
|
||||||
|
@ -70,6 +70,44 @@ NestMap *times_hundred(int n) {
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Recursive data structures as test for issue #4623
|
||||||
|
*/
|
||||||
|
struct RecursiveVector : std::vector<RecursiveVector> {
|
||||||
|
using Parent = std::vector<RecursiveVector>;
|
||||||
|
using Parent::Parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RecursiveMap : std::map<int, RecursiveMap> {
|
||||||
|
using Parent = std::map<int, RecursiveMap>;
|
||||||
|
using Parent::Parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pybind11 does not catch more complicated recursion schemes, such as mutual
|
||||||
|
* recursion.
|
||||||
|
* In that case custom recursive_container_traits specializations need to be added,
|
||||||
|
* thus manually telling pybind11 about the recursion.
|
||||||
|
*/
|
||||||
|
struct MutuallyRecursiveContainerPairMV;
|
||||||
|
struct MutuallyRecursiveContainerPairVM;
|
||||||
|
|
||||||
|
struct MutuallyRecursiveContainerPairMV : std::map<int, MutuallyRecursiveContainerPairVM> {};
|
||||||
|
struct MutuallyRecursiveContainerPairVM : std::vector<MutuallyRecursiveContainerPairMV> {};
|
||||||
|
|
||||||
|
namespace pybind11 {
|
||||||
|
namespace detail {
|
||||||
|
template <typename SFINAE>
|
||||||
|
struct recursive_container_traits<MutuallyRecursiveContainerPairMV, SFINAE> {
|
||||||
|
using type_to_check_recursively = recursive_bottom;
|
||||||
|
};
|
||||||
|
template <typename SFINAE>
|
||||||
|
struct recursive_container_traits<MutuallyRecursiveContainerPairVM, SFINAE> {
|
||||||
|
using type_to_check_recursively = recursive_bottom;
|
||||||
|
};
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace pybind11
|
||||||
|
|
||||||
TEST_SUBMODULE(stl_binders, m) {
|
TEST_SUBMODULE(stl_binders, m) {
|
||||||
// test_vector_int
|
// test_vector_int
|
||||||
py::bind_vector<std::vector<unsigned int>>(m, "VectorInt", py::buffer_protocol());
|
py::bind_vector<std::vector<unsigned int>>(m, "VectorInt", py::buffer_protocol());
|
||||||
@ -129,6 +167,12 @@ TEST_SUBMODULE(stl_binders, m) {
|
|||||||
m, "VectorUndeclStruct", py::buffer_protocol());
|
m, "VectorUndeclStruct", py::buffer_protocol());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Bind recursive container types
|
||||||
|
py::bind_vector<RecursiveVector>(m, "RecursiveVector");
|
||||||
|
py::bind_map<RecursiveMap>(m, "RecursiveMap");
|
||||||
|
py::bind_map<MutuallyRecursiveContainerPairMV>(m, "MutuallyRecursiveContainerPairMV");
|
||||||
|
py::bind_vector<MutuallyRecursiveContainerPairVM>(m, "MutuallyRecursiveContainerPairVM");
|
||||||
|
|
||||||
// The rest depends on numpy:
|
// The rest depends on numpy:
|
||||||
try {
|
try {
|
||||||
py::module_::import("numpy");
|
py::module_::import("numpy");
|
||||||
|
@ -186,9 +186,9 @@ def test_map_string_double():
|
|||||||
um["ua"] = 1.1
|
um["ua"] = 1.1
|
||||||
um["ub"] = 2.6
|
um["ub"] = 2.6
|
||||||
|
|
||||||
assert sorted(list(um)) == ["ua", "ub"]
|
assert sorted(um) == ["ua", "ub"]
|
||||||
assert list(um.keys()) == list(um)
|
assert list(um.keys()) == list(um)
|
||||||
assert sorted(list(um.items())) == [("ua", 1.1), ("ub", 2.6)]
|
assert sorted(um.items()) == [("ua", 1.1), ("ub", 2.6)]
|
||||||
assert list(zip(um.keys(), um.values())) == list(um.items())
|
assert list(zip(um.keys(), um.values())) == list(um.items())
|
||||||
assert "UnorderedMapStringDouble" in str(um)
|
assert "UnorderedMapStringDouble" in str(um)
|
||||||
|
|
||||||
@ -304,11 +304,11 @@ def test_map_delitem():
|
|||||||
um["ua"] = 1.1
|
um["ua"] = 1.1
|
||||||
um["ub"] = 2.6
|
um["ub"] = 2.6
|
||||||
|
|
||||||
assert sorted(list(um)) == ["ua", "ub"]
|
assert sorted(um) == ["ua", "ub"]
|
||||||
assert sorted(list(um.items())) == [("ua", 1.1), ("ub", 2.6)]
|
assert sorted(um.items()) == [("ua", 1.1), ("ub", 2.6)]
|
||||||
del um["ua"]
|
del um["ua"]
|
||||||
assert sorted(list(um)) == ["ub"]
|
assert sorted(um) == ["ub"]
|
||||||
assert sorted(list(um.items())) == [("ub", 2.6)]
|
assert sorted(um.items()) == [("ub", 2.6)]
|
||||||
|
|
||||||
|
|
||||||
def test_map_view_types():
|
def test_map_view_types():
|
||||||
@ -335,3 +335,21 @@ def test_map_view_types():
|
|||||||
assert type(unordered_map_string_double.items()) is items_type
|
assert type(unordered_map_string_double.items()) is items_type
|
||||||
assert type(map_string_double_const.items()) is items_type
|
assert type(map_string_double_const.items()) is items_type
|
||||||
assert type(unordered_map_string_double_const.items()) is items_type
|
assert type(unordered_map_string_double_const.items()) is items_type
|
||||||
|
|
||||||
|
|
||||||
|
def test_recursive_vector():
|
||||||
|
recursive_vector = m.RecursiveVector()
|
||||||
|
recursive_vector.append(m.RecursiveVector())
|
||||||
|
recursive_vector[0].append(m.RecursiveVector())
|
||||||
|
recursive_vector[0].append(m.RecursiveVector())
|
||||||
|
# Can't use len() since test_stl_binders.cpp does not include stl.h,
|
||||||
|
# so the necessary conversion is missing
|
||||||
|
assert recursive_vector[0].count(m.RecursiveVector()) == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_recursive_map():
|
||||||
|
recursive_map = m.RecursiveMap()
|
||||||
|
recursive_map[100] = m.RecursiveMap()
|
||||||
|
recursive_map[100][101] = m.RecursiveMap()
|
||||||
|
recursive_map[100][102] = m.RecursiveMap()
|
||||||
|
assert list(recursive_map[100].keys()) == [101, 102]
|
||||||
|
130
tests/test_type_caster_pyobject_ptr.cpp
Normal file
130
tests/test_type_caster_pyobject_ptr.cpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#include <pybind11/functional.h>
|
||||||
|
#include <pybind11/stl.h>
|
||||||
|
#include <pybind11/type_caster_pyobject_ptr.h>
|
||||||
|
|
||||||
|
#include "pybind11_tests.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::vector<PyObject *> make_vector_pyobject_ptr(const py::object &ValueHolder) {
|
||||||
|
std::vector<PyObject *> vec_obj;
|
||||||
|
for (int i = 1; i < 3; i++) {
|
||||||
|
vec_obj.push_back(ValueHolder(i * 93).release().ptr());
|
||||||
|
}
|
||||||
|
// This vector now owns the refcounts.
|
||||||
|
return vec_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_SUBMODULE(type_caster_pyobject_ptr, m) {
|
||||||
|
m.def("cast_from_pyobject_ptr", []() {
|
||||||
|
PyObject *ptr = PyLong_FromLongLong(6758L);
|
||||||
|
return py::cast(ptr, py::return_value_policy::take_ownership);
|
||||||
|
});
|
||||||
|
m.def("cast_handle_to_pyobject_ptr", [](py::handle obj) {
|
||||||
|
auto rc1 = obj.ref_count();
|
||||||
|
auto *ptr = py::cast<PyObject *>(obj);
|
||||||
|
auto rc2 = obj.ref_count();
|
||||||
|
if (rc2 != rc1 + 1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 100 - py::reinterpret_steal<py::object>(ptr).attr("value").cast<int>();
|
||||||
|
});
|
||||||
|
m.def("cast_object_to_pyobject_ptr", [](py::object obj) {
|
||||||
|
py::handle hdl = obj;
|
||||||
|
auto rc1 = hdl.ref_count();
|
||||||
|
auto *ptr = py::cast<PyObject *>(std::move(obj));
|
||||||
|
auto rc2 = hdl.ref_count();
|
||||||
|
if (rc2 != rc1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 300 - py::reinterpret_steal<py::object>(ptr).attr("value").cast<int>();
|
||||||
|
});
|
||||||
|
m.def("cast_list_to_pyobject_ptr", [](py::list lst) {
|
||||||
|
// This is to cover types implicitly convertible to object.
|
||||||
|
py::handle hdl = lst;
|
||||||
|
auto rc1 = hdl.ref_count();
|
||||||
|
auto *ptr = py::cast<PyObject *>(std::move(lst));
|
||||||
|
auto rc2 = hdl.ref_count();
|
||||||
|
if (rc2 != rc1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 400 - static_cast<int>(py::len(py::reinterpret_steal<py::list>(ptr)));
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def(
|
||||||
|
"return_pyobject_ptr",
|
||||||
|
[]() { return PyLong_FromLongLong(2314L); },
|
||||||
|
py::return_value_policy::take_ownership);
|
||||||
|
m.def("pass_pyobject_ptr", [](PyObject *ptr) {
|
||||||
|
return 200 - py::reinterpret_borrow<py::object>(ptr).attr("value").cast<int>();
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("call_callback_with_object_return",
|
||||||
|
[](const std::function<py::object(int)> &cb, int value) { return cb(value); });
|
||||||
|
m.def(
|
||||||
|
"call_callback_with_pyobject_ptr_return",
|
||||||
|
[](const std::function<PyObject *(int)> &cb, int value) { return cb(value); },
|
||||||
|
py::return_value_policy::take_ownership);
|
||||||
|
m.def(
|
||||||
|
"call_callback_with_pyobject_ptr_arg",
|
||||||
|
[](const std::function<int(PyObject *)> &cb, py::handle obj) { return cb(obj.ptr()); },
|
||||||
|
py::arg("cb"), // This triggers return_value_policy::automatic_reference
|
||||||
|
py::arg("obj"));
|
||||||
|
|
||||||
|
m.def("cast_to_pyobject_ptr_nullptr", [](bool set_error) {
|
||||||
|
if (set_error) {
|
||||||
|
PyErr_SetString(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::cast(Py_None);
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("pass_list_pyobject_ptr", [](const std::vector<PyObject *> &vec_obj) {
|
||||||
|
int acc = 0;
|
||||||
|
for (const auto &ptr : vec_obj) {
|
||||||
|
acc = acc * 1000 + py::reinterpret_borrow<py::object>(ptr).attr("value").cast<int>();
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("return_list_pyobject_ptr_take_ownership",
|
||||||
|
make_vector_pyobject_ptr,
|
||||||
|
// Ownership is transferred one-by-one when the vector is converted to a Python list.
|
||||||
|
py::return_value_policy::take_ownership);
|
||||||
|
|
||||||
|
m.def("return_list_pyobject_ptr_reference",
|
||||||
|
make_vector_pyobject_ptr,
|
||||||
|
// Ownership is not transferred.
|
||||||
|
py::return_value_policy::reference);
|
||||||
|
|
||||||
|
m.def("dec_ref_each_pyobject_ptr", [](const std::vector<PyObject *> &vec_obj) {
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (; i < vec_obj.size(); i++) {
|
||||||
|
py::handle h(vec_obj[i]);
|
||||||
|
if (static_cast<std::size_t>(h.ref_count()) < 2) {
|
||||||
|
break; // Something is badly wrong.
|
||||||
|
}
|
||||||
|
h.dec_ref();
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("pass_pyobject_ptr_and_int", [](PyObject *, int) {});
|
||||||
|
|
||||||
|
#ifdef PYBIND11_NO_COMPILE_SECTION // Change to ifndef for manual testing.
|
||||||
|
{
|
||||||
|
PyObject *ptr = nullptr;
|
||||||
|
(void) py::cast(*ptr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
104
tests/test_type_caster_pyobject_ptr.py
Normal file
104
tests/test_type_caster_pyobject_ptr.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from pybind11_tests import type_caster_pyobject_ptr as m
|
||||||
|
|
||||||
|
|
||||||
|
# For use as a temporary user-defined object, to maximize sensitivity of the tests below.
|
||||||
|
class ValueHolder:
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
|
||||||
|
def test_cast_from_pyobject_ptr():
|
||||||
|
assert m.cast_from_pyobject_ptr() == 6758
|
||||||
|
|
||||||
|
|
||||||
|
def test_cast_handle_to_pyobject_ptr():
|
||||||
|
assert m.cast_handle_to_pyobject_ptr(ValueHolder(24)) == 76
|
||||||
|
|
||||||
|
|
||||||
|
def test_cast_object_to_pyobject_ptr():
|
||||||
|
assert m.cast_object_to_pyobject_ptr(ValueHolder(43)) == 257
|
||||||
|
|
||||||
|
|
||||||
|
def test_cast_list_to_pyobject_ptr():
|
||||||
|
assert m.cast_list_to_pyobject_ptr([1, 2, 3, 4, 5]) == 395
|
||||||
|
|
||||||
|
|
||||||
|
def test_return_pyobject_ptr():
|
||||||
|
assert m.return_pyobject_ptr() == 2314
|
||||||
|
|
||||||
|
|
||||||
|
def test_pass_pyobject_ptr():
|
||||||
|
assert m.pass_pyobject_ptr(ValueHolder(82)) == 118
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"call_callback",
|
||||||
|
[
|
||||||
|
m.call_callback_with_object_return,
|
||||||
|
m.call_callback_with_pyobject_ptr_return,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_call_callback_with_object_return(call_callback):
|
||||||
|
def cb(value):
|
||||||
|
if value < 0:
|
||||||
|
raise ValueError("Raised from cb")
|
||||||
|
return ValueHolder(1000 - value)
|
||||||
|
|
||||||
|
assert call_callback(cb, 287).value == 713
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="^Raised from cb$"):
|
||||||
|
call_callback(cb, -1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_call_callback_with_pyobject_ptr_arg():
|
||||||
|
def cb(obj):
|
||||||
|
return 300 - obj.value
|
||||||
|
|
||||||
|
assert m.call_callback_with_pyobject_ptr_arg(cb, ValueHolder(39)) == 261
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("set_error", [True, False])
|
||||||
|
def test_cast_to_python_nullptr(set_error):
|
||||||
|
expected = {
|
||||||
|
True: r"^Reflective of healthy error handling\.$",
|
||||||
|
False: (
|
||||||
|
r"^Internal error: pybind11::error_already_set called "
|
||||||
|
r"while Python error indicator not set\.$"
|
||||||
|
),
|
||||||
|
}[set_error]
|
||||||
|
with pytest.raises(RuntimeError, match=expected):
|
||||||
|
m.cast_to_pyobject_ptr_nullptr(set_error)
|
||||||
|
|
||||||
|
|
||||||
|
def test_cast_to_python_non_nullptr_with_error_set():
|
||||||
|
with pytest.raises(SystemError) as excinfo:
|
||||||
|
m.cast_to_pyobject_ptr_non_nullptr_with_error_set()
|
||||||
|
assert str(excinfo.value) == "src != nullptr but PyErr_Occurred()"
|
||||||
|
assert str(excinfo.value.__cause__) == "Reflective of unhealthy error handling."
|
||||||
|
|
||||||
|
|
||||||
|
def test_pass_list_pyobject_ptr():
|
||||||
|
acc = m.pass_list_pyobject_ptr([ValueHolder(842), ValueHolder(452)])
|
||||||
|
assert acc == 842452
|
||||||
|
|
||||||
|
|
||||||
|
def test_return_list_pyobject_ptr_take_ownership():
|
||||||
|
vec_obj = m.return_list_pyobject_ptr_take_ownership(ValueHolder)
|
||||||
|
assert [e.value for e in vec_obj] == [93, 186]
|
||||||
|
|
||||||
|
|
||||||
|
def test_return_list_pyobject_ptr_reference():
|
||||||
|
vec_obj = m.return_list_pyobject_ptr_reference(ValueHolder)
|
||||||
|
assert [e.value for e in vec_obj] == [93, 186]
|
||||||
|
# Commenting out the next `assert` will leak the Python references.
|
||||||
|
# An easy way to see evidence of the leaks:
|
||||||
|
# Insert `while True:` as the first line of this function and monitor the
|
||||||
|
# process RES (Resident Memory Size) with the Unix top command.
|
||||||
|
assert m.dec_ref_each_pyobject_ptr(vec_obj) == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_type_caster_name_via_incompatible_function_arguments_type_error():
|
||||||
|
with pytest.raises(TypeError, match=r"1\. \(arg0: object, arg1: int\) -> None"):
|
||||||
|
m.pass_pyobject_ptr_and_int(ValueHolder(101), ValueHolder(202))
|
38
tests/test_unnamed_namespace_a.cpp
Normal file
38
tests/test_unnamed_namespace_a.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include "pybind11_tests.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct any_struct {};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_SUBMODULE(unnamed_namespace_a, m) {
|
||||||
|
if (py::detail::get_type_info(typeid(any_struct)) == nullptr) {
|
||||||
|
py::class_<any_struct>(m, "unnamed_namespace_a_any_struct");
|
||||||
|
} else {
|
||||||
|
m.attr("unnamed_namespace_a_any_struct") = py::none();
|
||||||
|
}
|
||||||
|
m.attr("PYBIND11_INTERNALS_VERSION") = PYBIND11_INTERNALS_VERSION;
|
||||||
|
m.attr("defined_WIN32_or__WIN32") =
|
||||||
|
#if defined(WIN32) || defined(_WIN32)
|
||||||
|
true;
|
||||||
|
#else
|
||||||
|
false;
|
||||||
|
#endif
|
||||||
|
m.attr("defined___clang__") =
|
||||||
|
#if defined(__clang__)
|
||||||
|
true;
|
||||||
|
#else
|
||||||
|
false;
|
||||||
|
#endif
|
||||||
|
m.attr("defined__LIBCPP_VERSION") =
|
||||||
|
#if defined(_LIBCPP_VERSION)
|
||||||
|
true;
|
||||||
|
#else
|
||||||
|
false;
|
||||||
|
#endif
|
||||||
|
m.attr("defined___GLIBCXX__") =
|
||||||
|
#if defined(__GLIBCXX__)
|
||||||
|
true;
|
||||||
|
#else
|
||||||
|
false;
|
||||||
|
#endif
|
||||||
|
}
|
34
tests/test_unnamed_namespace_a.py
Normal file
34
tests/test_unnamed_namespace_a.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from pybind11_tests import unnamed_namespace_a as m
|
||||||
|
from pybind11_tests import unnamed_namespace_b as mb
|
||||||
|
|
||||||
|
XFAIL_CONDITION = (
|
||||||
|
"(m.PYBIND11_INTERNALS_VERSION <= 4 and (m.defined___clang__ or not m.defined___GLIBCXX__))"
|
||||||
|
" or "
|
||||||
|
"(m.PYBIND11_INTERNALS_VERSION >= 5 and not m.defined_WIN32_or__WIN32"
|
||||||
|
" and "
|
||||||
|
"(m.defined___clang__ or m.defined__LIBCPP_VERSION))"
|
||||||
|
)
|
||||||
|
XFAIL_REASON = "Known issues: https://github.com/pybind/pybind11/pull/4319"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(XFAIL_CONDITION, reason=XFAIL_REASON, strict=False)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"any_struct", [m.unnamed_namespace_a_any_struct, mb.unnamed_namespace_b_any_struct]
|
||||||
|
)
|
||||||
|
def test_have_class_any_struct(any_struct):
|
||||||
|
assert any_struct is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_have_at_least_one_class_any_struct():
|
||||||
|
assert (
|
||||||
|
m.unnamed_namespace_a_any_struct is not None
|
||||||
|
or mb.unnamed_namespace_b_any_struct is not None
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(XFAIL_CONDITION, reason=XFAIL_REASON, strict=True)
|
||||||
|
def test_have_both_class_any_struct():
|
||||||
|
assert m.unnamed_namespace_a_any_struct is not None
|
||||||
|
assert mb.unnamed_namespace_b_any_struct is not None
|
13
tests/test_unnamed_namespace_b.cpp
Normal file
13
tests/test_unnamed_namespace_b.cpp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include "pybind11_tests.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct any_struct {};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_SUBMODULE(unnamed_namespace_b, m) {
|
||||||
|
if (py::detail::get_type_info(typeid(any_struct)) == nullptr) {
|
||||||
|
py::class_<any_struct>(m, "unnamed_namespace_b_any_struct");
|
||||||
|
} else {
|
||||||
|
m.attr("unnamed_namespace_b_any_struct") = py::none();
|
||||||
|
}
|
||||||
|
}
|
5
tests/test_unnamed_namespace_b.py
Normal file
5
tests/test_unnamed_namespace_b.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from pybind11_tests import unnamed_namespace_b as m
|
||||||
|
|
||||||
|
|
||||||
|
def test_have_attr_any_struct():
|
||||||
|
assert hasattr(m, "unnamed_namespace_b_any_struct")
|
54
tests/test_vector_unique_ptr_member.cpp
Normal file
54
tests/test_vector_unique_ptr_member.cpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#include "pybind11_tests.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace pybind11_tests {
|
||||||
|
namespace vector_unique_ptr_member {
|
||||||
|
|
||||||
|
struct DataType {};
|
||||||
|
|
||||||
|
// Reduced from a use case in the wild.
|
||||||
|
struct VectorOwner {
|
||||||
|
static std::unique_ptr<VectorOwner> Create(std::size_t num_elems) {
|
||||||
|
return std::unique_ptr<VectorOwner>(
|
||||||
|
new VectorOwner(std::vector<std::unique_ptr<DataType>>(num_elems)));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t data_size() const { return data_.size(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit VectorOwner(std::vector<std::unique_ptr<DataType>> data) : data_(std::move(data)) {}
|
||||||
|
|
||||||
|
const std::vector<std::unique_ptr<DataType>> data_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace vector_unique_ptr_member
|
||||||
|
} // namespace pybind11_tests
|
||||||
|
|
||||||
|
namespace pybind11 {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct is_copy_constructible<pybind11_tests::vector_unique_ptr_member::VectorOwner>
|
||||||
|
: std::false_type {};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct is_move_constructible<pybind11_tests::vector_unique_ptr_member::VectorOwner>
|
||||||
|
: std::false_type {};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace pybind11
|
||||||
|
|
||||||
|
using namespace pybind11_tests::vector_unique_ptr_member;
|
||||||
|
|
||||||
|
py::object py_cast_VectorOwner_ptr(VectorOwner *ptr) { return py::cast(ptr); }
|
||||||
|
|
||||||
|
TEST_SUBMODULE(vector_unique_ptr_member, m) {
|
||||||
|
py::class_<VectorOwner>(m, "VectorOwner")
|
||||||
|
.def_static("Create", &VectorOwner::Create)
|
||||||
|
.def("data_size", &VectorOwner::data_size);
|
||||||
|
|
||||||
|
m.def("py_cast_VectorOwner_ptr", py_cast_VectorOwner_ptr);
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user