diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index f5a08e2d7..f2d8007df 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -81,7 +81,7 @@ nox -s build ### Full setup To setup an ideal development environment, run the following commands on a -system with CMake 3.14+: +system with CMake 3.15+: ```bash python3 -m venv venv @@ -96,8 +96,8 @@ Tips: * You can use `virtualenv` (faster, from PyPI) instead of `venv`. * You can select any name for your environment folder; if it contains "env" it will be ignored by git. -* If you don't have CMake 3.14+, just add "cmake" to the pip install command. -* You can use `-DPYBIND11_FINDPYTHON=ON` to use FindPython on CMake 3.12+ +* If you don't have CMake 3.15+, just add "cmake" to the pip install command. +* You can use `-DPYBIND11_FINDPYTHON=ON` to use FindPython. * In classic mode, you may need to set `-DPYTHON_EXECUTABLE=/path/to/python`. FindPython uses `-DPython_ROOT_DIR=/path/to` or `-DPython_EXECUTABLE=/path/to/python`. @@ -149,8 +149,8 @@ To run the tests, you can "build" the check target: cmake --build build --target check ``` -`--target` can be spelled `-t` in CMake 3.15+. You can also run individual -tests with these targets: +`--target` can be spelled `-t`. You can also run individual tests with these +targets: * `pytest`: Python tests only, using the [pytest](https://docs.pytest.org/en/stable/) framework diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9b4851951..d77071aba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,7 +68,18 @@ jobs: # Extra ubuntu latest job - runs-on: ubuntu-latest python: '3.11' - + - runs-on: ubuntu-latest + python: '3.12' + args: > + -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" + - runs-on: macos-13 + python: '3.12' + args: > + -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" + - runs-on: windows-2022 + python: '3.12' + args: > + -DCMAKE_CXX_FLAGS="/DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT /GR /EHsc" name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • x64 ${{ matrix.args }}" runs-on: ${{ matrix.runs-on }} diff --git a/.github/workflows/ci_sh_def.yml b/.github/workflows/ci_sh_def.yml deleted file mode 100644 index 68a739850..000000000 --- a/.github/workflows/ci_sh_def.yml +++ /dev/null @@ -1,1262 +0,0 @@ -# PLEASE KEEP THIS GROUP OF FILES IN SYNC AT ALL TIMES: -# ci.yml -# ci_sh_def.yml -# ci_sh_def.yml.patch -# To import changes made to ci.yml *** ESPECIALLY AFTER `git merge master` ***: -# patch -i ci_sh_def.yml.patch -o ci_sh_def.yml -# To update the patch file after making changes to ci_sh.yml: -# diff -u ci.yml ci_sh_def.yml > ci_sh_def.yml.patch -# git commit -a -m 'Tracking ci.yml changes from master.' -# -# Thanks a lot to @rhaschke for PR #2930! - -name: "CI-SH-DEF" - -on: - workflow_dispatch: - pull_request: - push: - branches: - - master - - stable - - smart_holder - - v* - -permissions: read-all - -concurrency: - group: test-sh-def-${{ github.ref }} - cancel-in-progress: true - -env: - PIP_BREAK_SYSTEM_PACKAGES: 1 - PIP_ONLY_BINARY: numpy - FORCE_COLOR: 3 - PYTEST_TIMEOUT: 300 - # For cmake: - VERBOSE: 1 - -jobs: - # This is the "main" test suite, which tests a large number of different - # versions of default compilers and Python versions in GitHub Actions. - standard: - strategy: - fail-fast: false - matrix: - runs-on: [ubuntu-20.04, windows-2022, macos-13] - python: - - '3.8' - - '3.9' - - '3.12' - - '3.13' - - 'pypy-3.8' - - 'pypy-3.9' - - 'pypy-3.10' - - # 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 - # existing keys match. - # - # We support an optional key: args, for cmake args - include: - # Just add a key - - runs-on: ubuntu-20.04 - python: '3.8' - args: > - -DPYBIND11_FINDPYTHON=ON - -DCMAKE_CXX_FLAGS="-D_=1" - exercise_D_: 1 - - runs-on: ubuntu-20.04 - python: 'pypy-3.8' - args: > - -DPYBIND11_FINDPYTHON=ON - - runs-on: windows-2019 - python: '3.8' - args: > - -DPYBIND11_FINDPYTHON=ON - # Inject a couple Windows 2019 runs - - runs-on: windows-2019 - python: '3.9' - # Extra ubuntu latest job - - runs-on: ubuntu-latest - python: '3.11' - - - name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • x64 ${{ matrix.args }}" - runs-on: ${{ matrix.runs-on }} - - steps: - - uses: actions/checkout@v4 - - - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python }} - allow-prereleases: true - - - name: Setup Boost (Linux) - # Can't use boost + define _ - if: runner.os == 'Linux' && matrix.exercise_D_ != 1 - run: sudo apt-get install libboost-dev - - - name: Setup Boost (macOS) - if: runner.os == 'macOS' - run: brew install boost - - - name: Update CMake - uses: jwlawson/actions-setup-cmake@v2.0 - - - name: Cache wheels - if: runner.os == 'macOS' - uses: actions/cache@v4 - with: - # This path is specific to macOS - we really only need it for PyPy NumPy wheels - # See https://github.com/actions/cache/blob/master/examples.md#python---pip - # for ways to do this more generally - path: ~/Library/Caches/pip - # Look to see if there is a cache hit for the corresponding requirements file - key: ${{ runner.os }}-pip-${{ matrix.python }}-x64-${{ hashFiles('tests/requirements.txt') }} - - - name: Prepare env - run: | - python -m pip install -r tests/requirements.txt - - - name: Setup annotations on Linux - if: runner.os == 'Linux' - run: python -m pip install pytest-github-actions-annotate-failures - - # First build - C++11 mode and inplace - # More-or-less randomly adding -DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON here - # (same for PYBIND11_NUMPY_1_ONLY, but requires a NumPy 1.x at runtime). - - name: Configure C++11 ${{ matrix.args }} - run: > - cmake -S . -B . - -DPYBIND11_WERROR=ON - -DPYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION=ON - -DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON - -DPYBIND11_NUMPY_1_ONLY=ON - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_STANDARD=11 - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT ${{runner.os == 'Windows' && '/GR /EHsc' || ''}}" - ${{ matrix.args }} - - - name: Build C++11 - run: cmake --build . -j 2 - - - name: Python tests C++11 - run: cmake --build . --target pytest -j 2 - - - name: C++11 tests - run: cmake --build . --target cpptest -j 2 - - - name: Interface test C++11 - run: cmake --build . --target test_cmake_build - - - name: Clean directory - run: git clean -fdx - - # Second build - C++17 mode and in a build directory - # More-or-less randomly adding -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF here. - # (same for PYBIND11_NUMPY_1_ONLY, but requires a NumPy 1.x at runtime). - - name: Configure C++17 - run: > - cmake -S . -B build2 - -DPYBIND11_WERROR=ON - -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF - -DPYBIND11_NUMPY_1_ONLY=ON - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_STANDARD=17 - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT ${{runner.os == 'Windows' && '/GR /EHsc' || ''}}" - ${{ matrix.args }} - - - name: Build - run: cmake --build build2 -j 2 - - - name: Python tests - run: cmake --build build2 --target pytest - - - name: C++ tests - run: cmake --build build2 --target cpptest - - # Third build - C++17 mode with unstable ABI - - name: Configure (unstable ABI) - run: > - cmake -S . -B build3 - -DPYBIND11_WERROR=ON - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_STANDARD=17 - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT ${{runner.os == 'Windows' && '/GR /EHsc' || ''}}" - -DPYBIND11_INTERNALS_VERSION=10000000 - ${{ matrix.args }} - - - name: Build (unstable ABI) - run: cmake --build build3 -j 2 - - - name: Python tests (unstable ABI) - run: cmake --build build3 --target pytest - - - name: Interface test - run: cmake --build build2 --target test_cmake_build - - # This makes sure the setup_helpers module can build packages using - # setuptools - - name: Setuptools helpers test - run: | - pip install setuptools - pytest tests/extra_setuptools - if: "!(matrix.runs-on == 'windows-2022')" - - manylinux: - name: Manylinux on 🐍 3.13t • GIL - runs-on: ubuntu-latest - timeout-minutes: 40 - container: quay.io/pypa/musllinux_1_2_x86_64:latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Prepare venv - run: python3.13t -m venv .venv - - - name: Install Python deps - run: .venv/bin/pip install -r tests/requirements.txt - - - name: Configure C++11 - run: > - cmake -S. -Bbuild - -DPYBIND11_WERROR=ON - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DPython_ROOT_DIR=.venv - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - - - name: Build C++11 - run: cmake --build build -j2 - - - name: Python tests C++11 - run: cmake --build build --target pytest -j2 - - deadsnakes: - strategy: - fail-fast: false - matrix: - include: - # TODO: Fails on 3.10, investigate - # JOB DISABLED (NEEDS WORK): https://github.com/pybind/pybind11/issues/4889 - # - python-version: "3.9" - # python-debug: true - # valgrind: true - - python-version: "3.11" - python-debug: false - - name: "🐍 ${{ matrix.python-version }}${{ matrix.python-debug && '-dbg' || '' }} (deadsnakes)${{ matrix.valgrind && ' • Valgrind' || '' }} • x64" - runs-on: ubuntu-20.04 - - steps: - - uses: actions/checkout@v4 - - - name: Setup Python ${{ matrix.python-version }} (deadsnakes) - uses: deadsnakes/action@v3.1.0 - with: - python-version: ${{ matrix.python-version }} - debug: ${{ matrix.python-debug }} - - - name: Update CMake - uses: jwlawson/actions-setup-cmake@v2.0 - - - name: Valgrind cache - if: matrix.valgrind - uses: actions/cache@v4 - id: cache-valgrind - with: - path: valgrind - key: 3.16.1 # Valgrind version - - - name: Compile Valgrind - if: matrix.valgrind && steps.cache-valgrind.outputs.cache-hit != 'true' - run: | - VALGRIND_VERSION=3.16.1 - curl https://sourceware.org/pub/valgrind/valgrind-$VALGRIND_VERSION.tar.bz2 -o - | tar xj - mv valgrind-$VALGRIND_VERSION valgrind - cd valgrind - ./configure - make -j 2 > /dev/null - - - name: Install Valgrind - if: matrix.valgrind - working-directory: valgrind - run: | - sudo make install - sudo apt-get update - sudo apt-get install libc6-dbg # Needed by Valgrind - - - name: Prepare env - run: | - python -m pip install -r tests/requirements.txt - - - name: Configure - run: > - cmake -S . -B build - -DCMAKE_BUILD_TYPE=Debug - -DPYBIND11_WERROR=ON - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_STANDARD=17 - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - - - name: Build - run: cmake --build build -j 2 - - - name: Python tests - run: cmake --build build --target pytest - - - name: C++ tests - run: cmake --build build --target cpptest - - - name: Run Valgrind on Python tests - if: matrix.valgrind - run: cmake --build build --target memcheck - - - # Testing on clang using the excellent silkeh clang docker images - clang: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - clang: - - dev - std: - - 11 - container_suffix: - - "" - include: - - clang: 5 - std: 14 - - clang: 11 - std: 20 - - clang: 12 - std: 20 - - clang: 13 - std: 20 - - clang: 14 - std: 20 - - clang: 15 - std: 20 - container_suffix: "-bullseye" - - clang: 16 - std: 20 - container_suffix: "-bullseye" - - clang: 17 - std: 20 - container_suffix: "-bookworm" - - clang: 18 - std: 20 - container_suffix: "-bookworm" - - name: "🐍 3 • Clang ${{ matrix.clang }} • C++${{ matrix.std }} • x64" - container: "silkeh/clang:${{ matrix.clang }}${{ matrix.container_suffix }}" - - steps: - - uses: actions/checkout@v4 - - - name: Add wget and python3 - run: apt-get update && apt-get install -y python3-dev python3-numpy python3-pytest libeigen3-dev - - - name: Configure - shell: bash - run: > - cmake -S . -B build - -DPYBIND11_WERROR=ON - -DDOWNLOAD_CATCH=ON - -DCMAKE_CXX_STANDARD=${{ matrix.std }} - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - - - name: Build - run: cmake --build build -j 2 - - - name: Python tests - run: cmake --build build --target pytest - - - name: C++ tests - run: cmake --build build --target cpptest - - - name: Interface test - run: cmake --build build --target test_cmake_build - - - # Testing NVCC; forces sources to behave like .cu files - cuda: - runs-on: ubuntu-latest - name: "🐍 3.10 • CUDA 12.2 • Ubuntu 22.04" - container: nvidia/cuda:12.2.0-devel-ubuntu22.04 - - steps: - - uses: actions/checkout@v4 - - # tzdata will try to ask for the timezone, so set the DEBIAN_FRONTEND - - name: Install 🐍 3 - run: apt-get update && DEBIAN_FRONTEND="noninteractive" apt-get install -y cmake git python3-dev python3-pytest python3-numpy - - - name: Configure - run: cmake -S . -B build -DPYBIND11_CUDA_TESTS=ON -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - - - name: Build - run: cmake --build build -j2 --verbose - - - name: Python tests - run: cmake --build build --target pytest - - -# TODO: Internal compiler error - report to NVidia -# # Testing CentOS 8 + PGI compilers -# centos-nvhpc8: -# runs-on: ubuntu-latest -# name: "🐍 3 • CentOS8 / PGI 20.11 • x64" -# container: centos:8 -# -# steps: -# - uses: actions/checkout@v4 -# -# - name: Add Python 3 and a few requirements -# run: yum update -y && yum install -y git python3-devel python3-numpy python3-pytest make environment-modules -# -# - name: Install CMake with pip -# run: | -# python3 -m pip install --upgrade pip -# python3 -m pip install cmake --prefer-binary -# -# - name: Install NVidia HPC SDK -# run: > -# yum -y install -# https://developer.download.nvidia.com/hpc-sdk/20.11/nvhpc-20-11-20.11-1.x86_64.rpm -# https://developer.download.nvidia.com/hpc-sdk/20.11/nvhpc-2020-20.11-1.x86_64.rpm -# -# - name: Configure -# shell: bash -# run: | -# source /etc/profile.d/modules.sh -# module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/20.11 -# cmake -S . -B build -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_STANDARD=14 -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") -# -# - name: Build -# run: cmake --build build -j 2 --verbose -# -# - name: Python tests -# run: cmake --build build --target pytest -# -# - name: C++ tests -# run: cmake --build build --target cpptest -# -# - name: Interface test -# run: cmake --build build --target test_cmake_build - - - # Testing on Ubuntu + NVHPC (previous PGI) compilers, which seems to require more workarounds - ubuntu-nvhpc7: - runs-on: ubuntu-20.04 - name: "🐍 3 • NVHPC 23.5 • C++17 • x64" - - env: - # tzdata will try to ask for the timezone, so set the DEBIAN_FRONTEND - DEBIAN_FRONTEND: 'noninteractive' - steps: - - uses: actions/checkout@v4 - - - name: Add NVHPC Repo - run: | - echo 'deb [trusted=yes] https://developer.download.nvidia.com/hpc-sdk/ubuntu/amd64 /' | \ - sudo tee /etc/apt/sources.list.d/nvhpc.list - - - name: Install 🐍 3 & NVHPC - run: | - sudo apt-get update -y && \ - sudo apt-get install -y cmake environment-modules git python3-dev python3-pip python3-numpy && \ - sudo apt-get install -y --no-install-recommends nvhpc-23-5 && \ - sudo rm -rf /var/lib/apt/lists/* - python3 -m pip install --upgrade pip - python3 -m pip install --upgrade pytest - - # On some systems, you many need further workarounds: - # https://github.com/pybind/pybind11/pull/2475 - - name: Configure - shell: bash - run: | - source /etc/profile.d/modules.sh - module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/23.5 - cmake -S . -B build -DDOWNLOAD_CATCH=ON \ - -DCMAKE_CXX_STANDARD=17 \ - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") \ - -DCMAKE_CXX_FLAGS="-Wc,--pending_instantiations=0 -DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" \ - -DPYBIND11_TEST_FILTER="test_smart_ptr.cpp" - - - name: Build - run: cmake --build build -j 2 --verbose - - - name: Python tests - run: cmake --build build --target pytest - - - name: C++ tests - run: cmake --build build --target cpptest - - - name: Interface test - run: cmake --build build --target test_cmake_build - - - # Testing on GCC using the GCC docker images (only recent images supported) - gcc: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - { gcc: 9, std: 20 } - - { gcc: 10, std: 17 } - - { gcc: 10, std: 20 } - - { gcc: 11, std: 20 } - - { gcc: 12, std: 20 } - - { gcc: 13, std: 20 } - - name: "🐍 3 • GCC ${{ matrix.gcc }} • C++${{ matrix.std }}• x64" - container: "gcc:${{ matrix.gcc }}" - - steps: - - uses: actions/checkout@v4 - - - name: Add Python 3 - run: apt-get update; apt-get install -y python3-dev python3-numpy python3-pytest python3-pip libeigen3-dev - - - name: Update pip - run: python3 -m pip install --upgrade pip - - - name: Update CMake - uses: jwlawson/actions-setup-cmake@v2.0 - - - name: Configure - shell: bash - run: > - cmake -S . -B build - -DPYBIND11_WERROR=ON - -DDOWNLOAD_CATCH=ON - -DCMAKE_CXX_STANDARD=${{ matrix.std }} - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - - - name: Build - run: cmake --build build -j 2 - - - name: Python tests - run: cmake --build build --target pytest - - - name: C++ tests - run: cmake --build build --target cpptest - - - name: Interface test - 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 }} - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -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 - icc: - runs-on: ubuntu-20.04 - - name: "🐍 3 • ICC latest • x64" - - steps: - - uses: actions/checkout@v4 - - - name: Add apt repo - run: | - sudo apt-get update - sudo apt-get install -y wget build-essential pkg-config cmake ca-certificates gnupg - wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB - sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB - echo "deb https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list - - - name: Add ICC & Python 3 - run: sudo apt-get update; sudo apt-get install -y intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic cmake python3-dev python3-numpy python3-pytest python3-pip - - - name: Update pip - run: | - set +e; source /opt/intel/oneapi/setvars.sh; set -e - python3 -m pip install --upgrade pip - - - name: Install dependencies - run: | - set +e; source /opt/intel/oneapi/setvars.sh; set -e - python3 -m pip install -r tests/requirements.txt - - - name: Configure C++11 - run: | - set +e; source /opt/intel/oneapi/setvars.sh; set -e - cmake -S . -B build-11 \ - -DPYBIND11_WERROR=ON \ - -DDOWNLOAD_CATCH=ON \ - -DDOWNLOAD_EIGEN=OFF \ - -DCMAKE_CXX_STANDARD=11 \ - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" \ - -DCMAKE_CXX_COMPILER=$(which icpc) \ - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - - - name: Build C++11 - run: | - set +e; source /opt/intel/oneapi/setvars.sh; set -e - cmake --build build-11 -j 2 -v - - - name: Python tests C++11 - run: | - set +e; source /opt/intel/oneapi/setvars.sh; set -e - sudo service apport stop - cmake --build build-11 --target check - - - name: C++ tests C++11 - run: | - set +e; source /opt/intel/oneapi/setvars.sh; set -e - cmake --build build-11 --target cpptest - - - name: Interface test C++11 - run: | - set +e; source /opt/intel/oneapi/setvars.sh; set -e - cmake --build build-11 --target test_cmake_build - - - name: Configure C++17 - run: | - set +e; source /opt/intel/oneapi/setvars.sh; set -e - cmake -S . -B build-17 \ - -DPYBIND11_WERROR=ON \ - -DDOWNLOAD_CATCH=ON \ - -DDOWNLOAD_EIGEN=OFF \ - -DCMAKE_CXX_STANDARD=17 \ - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" \ - -DCMAKE_CXX_COMPILER=$(which icpc) \ - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - - - name: Build C++17 - run: | - set +e; source /opt/intel/oneapi/setvars.sh; set -e - cmake --build build-17 -j 2 -v - - - name: Python tests C++17 - run: | - set +e; source /opt/intel/oneapi/setvars.sh; set -e - sudo service apport stop - cmake --build build-17 --target check - - - name: C++ tests C++17 - run: | - set +e; source /opt/intel/oneapi/setvars.sh; set -e - cmake --build build-17 --target cpptest - - - name: Interface test C++17 - run: | - set +e; source /opt/intel/oneapi/setvars.sh; set -e - cmake --build build-17 --target test_cmake_build - - - # Testing on CentOS (manylinux uses a centos base). - centos: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - container: - - "almalinux:8" - - "almalinux:9" - - name: "🐍 3 • ${{ matrix.container }} • x64" - container: "${{ matrix.container }}" - - steps: - - name: Latest actions/checkout - uses: actions/checkout@v4 - - - name: Add Python 3.8 - if: matrix.container == 'almalinux:8' - run: dnf update -y && dnf install -y python38-devel gcc-c++ make git - - - name: Add Python 3 (default) - if: matrix.container != 'almalinux:8' - run: dnf update -y && dnf install -y python3-devel gcc-c++ make git - - - name: Update pip - run: python3 -m pip install --upgrade pip - - - name: Install dependencies - run: | - python3 -m pip install cmake -r tests/requirements.txt - - - name: Ensure NumPy 2 is used (required Python >= 3.9) - if: matrix.container == 'almalinux:9' - run: | - python3 -m pip install 'numpy>=2.0.0b1' 'scipy>=1.13.0rc1' - - - name: Configure - shell: bash - run: > - cmake -S . -B build - -DCMAKE_BUILD_TYPE=MinSizeRel - -DPYBIND11_WERROR=ON - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_STANDARD=11 - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - - - name: Build - run: cmake --build build -j 2 - - - name: Python tests - run: cmake --build build --target pytest - - - name: C++ tests - run: cmake --build build --target cpptest - - - name: Interface test - run: cmake --build build --target test_cmake_build - - - # This tests an "install" with the CMake tools - install-classic: - name: "🐍 3.9 • Debian • x86 • Install" - runs-on: ubuntu-latest - container: i386/debian:bullseye - - steps: - - uses: actions/checkout@v1 # v1 is required to run inside docker - - - name: Install requirements - run: | - apt-get update - apt-get install -y git make cmake g++ libeigen3-dev python3-dev python3-pip - pip3 install "pytest==6.*" - - - name: Configure for install - run: > - cmake . - -DPYBIND11_INSTALL=1 -DPYBIND11_TEST=0 - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - - - name: Make and install - run: make install - - - name: Copy tests to new directory - run: cp -a tests /pybind11-tests - - - name: Make a new test directory - run: mkdir /build-tests - - - name: Configure tests - run: > - cmake ../pybind11-tests - -DDOWNLOAD_CATCH=ON - -DPYBIND11_WERROR=ON - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - working-directory: /build-tests - - - name: Python tests - run: make pytest -j 2 - working-directory: /build-tests - - - # This verifies that the documentation is not horribly broken, and does a - # basic validation check on the SDist. - doxygen: - name: "Documentation build test" - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-python@v5 - with: - python-version: "3.x" - - - name: Install Doxygen - run: sudo apt-get install -y doxygen librsvg2-bin # Changed to rsvg-convert in 20.04 - - - name: Build docs - run: pipx run nox -s docs - - - name: Make SDist - run: pipx run nox -s build -- --sdist - - - run: git status --ignored - - - name: Check local include dir - run: > - ls pybind11; - python3 -c "import pybind11, pathlib; assert (a := pybind11.get_include()) == (b := str(pathlib.Path('include').resolve())), f'{a} != {b}'" - - - name: Compare Dists (headers only) - working-directory: include - run: | - python3 -m pip install --user -U ../dist/*.tar.gz - installed=$(python3 -c "import pybind11; print(pybind11.get_include() + '/pybind11')") - diff -rq $installed ./pybind11 - - win32: - strategy: - fail-fast: false - matrix: - python: - - '3.8' - - '3.9' - - '3.10' - - '3.11' - - '3.12' - - include: - - python: '3.12' - args: -DCMAKE_CXX_STANDARD=20 - - python: '3.11' - args: -DCMAKE_CXX_STANDARD=20 - - python: '3.10' - args: -DCMAKE_CXX_STANDARD=20 - - python: '3.9' - args: -DCMAKE_CXX_STANDARD=20 - - python: '3.8' - args: -DCMAKE_CXX_STANDARD=17 - - - name: "🐍 ${{ matrix.python }} • MSVC 2019 • x86 ${{ matrix.args }}" - runs-on: windows-2019 - - steps: - - uses: actions/checkout@v4 - - - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python }} - architecture: x86 - - - name: Update CMake - uses: jwlawson/actions-setup-cmake@v2.0 - - - name: Prepare MSVC - uses: ilammy/msvc-dev-cmd@v1.13.0 - with: - arch: x86 - - - name: Prepare env - run: | - python -m pip install -r tests/requirements.txt - - # First build - C++11 mode and inplace - - name: Configure ${{ matrix.args }} - run: > - cmake -S . -B build - -G "Visual Studio 16 2019" -A Win32 - -DPYBIND11_WERROR=ON - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_FLAGS="/GR /EHsc /DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - ${{ matrix.args }} - - name: Build C++11 - run: cmake --build build -j 2 - - - name: Python tests - run: cmake --build build -t pytest - - win32-debug: - strategy: - fail-fast: false - matrix: - python: - - 3.8 - - 3.9 - - include: - - python: 3.9 - args: -DCMAKE_CXX_STANDARD=20 - - python: 3.8 - args: -DCMAKE_CXX_STANDARD=17 - - name: "🐍 ${{ matrix.python }} • MSVC 2019 (Debug) • x86 ${{ matrix.args }}" - runs-on: windows-2019 - - steps: - - uses: actions/checkout@v4 - - - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python }} - architecture: x86 - - - name: Update CMake - uses: jwlawson/actions-setup-cmake@v2.0 - - - name: Prepare MSVC - uses: ilammy/msvc-dev-cmd@v1.13.0 - with: - arch: x86 - - - name: Prepare env - run: | - python -m pip install -r tests/requirements.txt - - # First build - C++11 mode and inplace - - name: Configure ${{ matrix.args }} - run: > - cmake -S . -B build - -G "Visual Studio 16 2019" -A Win32 - -DCMAKE_BUILD_TYPE=Debug - -DPYBIND11_WERROR=ON - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_FLAGS="/GR /EHsc /DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - ${{ matrix.args }} - - name: Build C++11 - run: cmake --build build --config Debug -j 2 - - - name: Python tests - run: cmake --build build --config Debug -t pytest - - - windows-2022: - strategy: - fail-fast: false - matrix: - python: - - 3.9 - - name: "🐍 ${{ matrix.python }} • MSVC 2022 C++20 • x64" - runs-on: windows-2022 - - steps: - - uses: actions/checkout@v4 - - - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python }} - - - name: Prepare env - # Ensure use of NumPy 2 (via NumPy nightlies but can be changed soon) - run: | - python3 -m pip install -r tests/requirements.txt - python3 -m pip install 'numpy>=2.0.0b1' 'scipy>=1.13.0rc1' - - - name: Update CMake - uses: jwlawson/actions-setup-cmake@v2.0 - - - name: Configure C++20 - run: > - cmake -S . -B build - -DPYBIND11_WERROR=ON - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_STANDARD=20 - -DCMAKE_CXX_FLAGS="/GR /EHsc /DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - - - name: Build C++20 - run: cmake --build build -j 2 - - - name: Python tests - run: cmake --build build --target pytest - - - name: C++20 tests - run: cmake --build build --target cpptest -j 2 - - - name: Interface test C++20 - 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 - -DCMAKE_CXX_FLAGS="/GR /EHsc /DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - "-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: - name: "🐍 3 • windows-latest • ${{ matrix.sys }}" - runs-on: windows-latest - defaults: - run: - shell: msys2 {0} - strategy: - fail-fast: false - matrix: - include: - - { sys: mingw64, env: x86_64 } - - { sys: mingw32, env: i686 } - steps: - - uses: msys2/setup-msys2@v2 - with: - msystem: ${{matrix.sys}} - install: >- - git - mingw-w64-${{matrix.env}}-gcc - mingw-w64-${{matrix.env}}-python-pip - mingw-w64-${{matrix.env}}-python-numpy - mingw-w64-${{matrix.env}}-cmake - mingw-w64-${{matrix.env}}-make - mingw-w64-${{matrix.env}}-python-pytest - mingw-w64-${{matrix.env}}-boost - mingw-w64-${{matrix.env}}-catch - - - uses: msys2/setup-msys2@v2 - if: matrix.sys == 'mingw64' - with: - msystem: ${{matrix.sys}} - install: >- - git - mingw-w64-${{matrix.env}}-python-scipy - mingw-w64-${{matrix.env}}-eigen3 - - - uses: actions/checkout@v4 - - - name: Configure C++11 - # LTO leads to many undefined reference like - # `pybind11::detail::function_call::function_call(pybind11::detail::function_call&&) - run: >- - cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=11 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON - -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)") - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -S . -B build - - - name: Build C++11 - run: cmake --build build -j 2 - - - name: Python tests C++11 - run: cmake --build build --target pytest -j 2 - - - name: C++11 tests - run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build --target cpptest -j 2 - - - name: Interface test C++11 - run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build --target test_cmake_build - - - name: Clean directory - run: git clean -fdx - - - name: Configure C++14 - run: >- - cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=14 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON - -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)") - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -S . -B build2 - - - name: Build C++14 - run: cmake --build build2 -j 2 - - - name: Python tests C++14 - run: cmake --build build2 --target pytest -j 2 - - - name: C++14 tests - run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build2 --target cpptest -j 2 - - - name: Interface test C++14 - run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build2 --target test_cmake_build - - - name: Clean directory - run: git clean -fdx - - - name: Configure C++17 - run: >- - cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=17 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON - -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)") - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -S . -B build3 - - - name: Build C++17 - run: cmake --build build3 -j 2 - - - name: Python tests C++17 - run: cmake --build build3 --target pytest -j 2 - - - name: C++17 tests - run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build3 --target cpptest -j 2 - - - name: Interface test C++17 - run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build3 --target test_cmake_build - - windows_clang: - - strategy: - matrix: - os: [windows-latest] - python: ['3.10'] - - runs-on: "${{ matrix.os }}" - - name: "🐍 ${{ matrix.python }} • ${{ matrix.os }} • clang-latest" - - steps: - - name: Show env - run: env - - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up Clang - uses: egor-tensin/setup-clang@v1 - - - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python }} - - - name: Update CMake - uses: jwlawson/actions-setup-cmake@v2.0 - - - name: Install ninja-build tool - uses: seanmiddleditch/gha-setup-ninja@v5 - - - name: Run pip installs - run: | - python -m pip install --upgrade pip - python -m pip install -r tests/requirements.txt - - - name: Show Clang++ version - run: clang++ --version - - - name: Show CMake version - run: cmake --version - - # TODO: WERROR=ON - - name: Configure Clang - run: > - cmake -G Ninja -S . -B . - -DPYBIND11_WERROR=OFF - -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_COMPILER=clang++ - -DCMAKE_CXX_STANDARD=17 - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - - - name: Build - run: cmake --build . -j 2 - - - name: Python tests - run: cmake --build . --target pytest -j 2 - - - name: C++ tests - run: cmake --build . --target cpptest -j 2 - - - name: Interface test - run: cmake --build . --target test_cmake_build -j 2 - - - name: Clean directory - run: git clean -fdx - - macos_brew_install_llvm: - name: "macos-13 • brew install llvm" - runs-on: macos-13 - - env: - # https://apple.stackexchange.com/questions/227026/how-to-install-recent-clang-with-homebrew - LDFLAGS: '-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib' - - steps: - - name: Update PATH - run: echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH - - - name: Show env - run: env - - - name: Checkout - uses: actions/checkout@v4 - - - name: Show Clang++ version before brew install llvm - run: clang++ --version - - - name: brew install llvm - run: brew install llvm - - - name: Show Clang++ version after brew install llvm - run: clang++ --version - - - name: Update CMake - uses: jwlawson/actions-setup-cmake@v2.0 - - - name: Run pip installs - run: | - python3 -m pip install --upgrade pip - python3 -m pip install -r tests/requirements.txt - python3 -m pip install numpy - python3 -m pip install scipy - - - name: Show CMake version - run: cmake --version - - - name: CMake Configure - run: > - cmake -S . -B . - -DPYBIND11_WERROR=ON - -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_COMPILER=clang++ - -DCMAKE_CXX_STANDARD=17 - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - - - name: Build - run: cmake --build . -j 2 - - - name: Python tests - run: cmake --build . --target pytest -j 2 - - - name: C++ tests - run: cmake --build . --target cpptest -j 2 - - - name: Interface test - 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 - -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -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 - run: git clean -fdx diff --git a/.github/workflows/ci_sh_def.yml.patch b/.github/workflows/ci_sh_def.yml.patch deleted file mode 100644 index ecfd7639c..000000000 --- a/.github/workflows/ci_sh_def.yml.patch +++ /dev/null @@ -1,223 +0,0 @@ ---- ci.yml 2024-07-30 11:20:28.997003056 -0700 -+++ ci_sh_def.yml 2024-07-30 11:21:39.724969167 -0700 -@@ -1,4 +1,16 @@ --name: CI -+# PLEASE KEEP THIS GROUP OF FILES IN SYNC AT ALL TIMES: -+# ci.yml -+# ci_sh_def.yml -+# ci_sh_def.yml.patch -+# To import changes made to ci.yml *** ESPECIALLY AFTER `git merge master` ***: -+# patch -i ci_sh_def.yml.patch -o ci_sh_def.yml -+# To update the patch file after making changes to ci_sh.yml: -+# diff -u ci.yml ci_sh_def.yml > ci_sh_def.yml.patch -+# git commit -a -m 'Tracking ci.yml changes from master.' -+# -+# Thanks a lot to @rhaschke for PR #2930! -+ -+name: "CI-SH-DEF" - - on: - workflow_dispatch: -@@ -13,7 +25,7 @@ - permissions: read-all - - concurrency: -- group: test-sh-avl${{ github.ref }} -+ group: test-sh-def-${{ github.ref }} - cancel-in-progress: true - - env: -@@ -126,6 +138,7 @@ - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_STANDARD=11 -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT ${{runner.os == 'Windows' && '/GR /EHsc' || ''}}" - ${{ matrix.args }} - - - name: Build C++11 -@@ -155,6 +168,7 @@ - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_STANDARD=17 -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT ${{runner.os == 'Windows' && '/GR /EHsc' || ''}}" - ${{ matrix.args }} - - - name: Build -@@ -174,6 +188,7 @@ - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_STANDARD=17 -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT ${{runner.os == 'Windows' && '/GR /EHsc' || ''}}" - -DPYBIND11_INTERNALS_VERSION=10000000 - ${{ matrix.args }} - -@@ -217,6 +232,7 @@ - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DPython_ROOT_DIR=.venv -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - - - name: Build C++11 - run: cmake --build build -j2 -@@ -290,6 +306,7 @@ - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_STANDARD=17 -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - - - name: Build - run: cmake --build build -j 2 -@@ -357,6 +374,7 @@ - -DPYBIND11_WERROR=ON - -DDOWNLOAD_CATCH=ON - -DCMAKE_CXX_STANDARD=${{ matrix.std }} -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - - - name: Build -@@ -386,7 +404,7 @@ - run: apt-get update && DEBIAN_FRONTEND="noninteractive" apt-get install -y cmake git python3-dev python3-pytest python3-numpy - - - name: Configure -- run: cmake -S . -B build -DPYBIND11_CUDA_TESTS=ON -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -+ run: cmake -S . -B build -DPYBIND11_CUDA_TESTS=ON -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - - - name: Build - run: cmake --build build -j2 --verbose -@@ -474,7 +492,7 @@ - cmake -S . -B build -DDOWNLOAD_CATCH=ON \ - -DCMAKE_CXX_STANDARD=17 \ - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") \ -- -DCMAKE_CXX_FLAGS="-Wc,--pending_instantiations=0" \ -+ -DCMAKE_CXX_FLAGS="-Wc,--pending_instantiations=0 -DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" \ - -DPYBIND11_TEST_FILTER="test_smart_ptr.cpp" - - - name: Build -@@ -526,6 +544,7 @@ - -DPYBIND11_WERROR=ON - -DDOWNLOAD_CATCH=ON - -DCMAKE_CXX_STANDARD=${{ matrix.std }} -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - - - name: Build -@@ -548,6 +567,7 @@ - -DPYBIND11_WERROR=ON - -DDOWNLOAD_CATCH=ON - -DCMAKE_CXX_STANDARD=${{ matrix.std }} -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - "-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp" - -@@ -597,6 +617,7 @@ - -DDOWNLOAD_CATCH=ON \ - -DDOWNLOAD_EIGEN=OFF \ - -DCMAKE_CXX_STANDARD=11 \ -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" \ - -DCMAKE_CXX_COMPILER=$(which icpc) \ - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - -@@ -629,6 +650,7 @@ - -DDOWNLOAD_CATCH=ON \ - -DDOWNLOAD_EIGEN=OFF \ - -DCMAKE_CXX_STANDARD=17 \ -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" \ - -DCMAKE_CXX_COMPILER=$(which icpc) \ - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - -@@ -700,6 +722,7 @@ - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_STANDARD=11 -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - - - name: Build -@@ -750,6 +773,7 @@ - cmake ../pybind11-tests - -DDOWNLOAD_CATCH=ON - -DPYBIND11_WERROR=ON -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - working-directory: /build-tests - -@@ -850,6 +874,7 @@ - -DPYBIND11_WERROR=ON - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON -+ -DCMAKE_CXX_FLAGS="/GR /EHsc /DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - ${{ matrix.args }} - - name: Build C++11 - run: cmake --build build -j 2 -@@ -904,6 +929,7 @@ - -DPYBIND11_WERROR=ON - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON -+ -DCMAKE_CXX_FLAGS="/GR /EHsc /DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - ${{ matrix.args }} - - name: Build C++11 - run: cmake --build build --config Debug -j 2 -@@ -946,6 +972,7 @@ - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_STANDARD=20 -+ -DCMAKE_CXX_FLAGS="/GR /EHsc /DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - - - name: Build C++20 - run: cmake --build build -j 2 -@@ -966,6 +993,7 @@ - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_STANDARD=20 -+ -DCMAKE_CXX_FLAGS="/GR /EHsc /DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - "-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp" - - - name: Build C++20 - Exercise cmake -DPYBIND11_TEST_OVERRIDE -@@ -1018,6 +1046,7 @@ - run: >- - cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=11 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON - -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)") -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -S . -B build - - - name: Build C++11 -@@ -1039,6 +1068,7 @@ - run: >- - cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=14 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON - -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)") -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -S . -B build2 - - - name: Build C++14 -@@ -1060,6 +1090,7 @@ - run: >- - cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=17 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON - -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)") -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -S . -B build3 - - - name: Build C++17 -@@ -1127,6 +1158,7 @@ - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_COMPILER=clang++ - -DCMAKE_CXX_STANDARD=17 -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - - - name: Build - run: cmake --build . -j 2 -@@ -1192,6 +1224,7 @@ - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_COMPILER=clang++ - -DCMAKE_CXX_STANDARD=17 -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - - - name: Build -@@ -1215,6 +1248,7 @@ - -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_COMPILER=clang++ - -DCMAKE_CXX_STANDARD=17 -+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" - -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - "-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp" - diff --git a/.github/workflows/configure.yml b/.github/workflows/configure.yml index 8242db7b1..8e89e6f39 100644 --- a/.github/workflows/configure.yml +++ b/.github/workflows/configure.yml @@ -32,7 +32,7 @@ jobs: include: - runs-on: ubuntu-20.04 arch: x64 - cmake: "3.5" + cmake: "3.15" - runs-on: ubuntu-20.04 arch: x64 @@ -40,7 +40,7 @@ jobs: - runs-on: macos-13 arch: x64 - cmake: "3.8" + cmake: "3.15" - runs-on: windows-2019 arch: x64 # x86 compilers seem to be missing on 2019 image diff --git a/.github/workflows/emscripten.yaml b/.github/workflows/emscripten.yaml index cbd7f5d54..fe4cd821c 100644 --- a/.github/workflows/emscripten.yaml +++ b/.github/workflows/emscripten.yaml @@ -5,6 +5,9 @@ on: pull_request: branches: - master + - stable + - smart_holder + - v* concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -23,8 +26,6 @@ jobs: - uses: pypa/cibuildwheel@v2.20 env: PYODIDE_BUILD_EXPORTS: whole_archive - CFLAGS: -fexceptions - LDFLAGS: -fexceptions with: package-dir: tests only: cp312-pyodide_wasm32 diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml index e98bea3aa..f45adf4ca 100644 --- a/.github/workflows/pip.yml +++ b/.github/workflows/pip.yml @@ -103,7 +103,7 @@ jobs: - uses: actions/download-artifact@v4 - name: Generate artifact attestation for sdist and wheel - uses: actions/attest-build-provenance@210c1913531870065f03ce1f9440dd87bc0938cd # v1.4.0 + uses: actions/attest-build-provenance@310b0a4a3b0b78ef57ecda988ee04b132db73ef8 # v1.4.1 with: subject-path: "*/pybind11*" diff --git a/CMakeLists.txt b/CMakeLists.txt index 82eca8923..681953900 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,16 +10,7 @@ if(NOT CMAKE_VERSION VERSION_LESS "3.27") cmake_policy(GET CMP0148 _pybind11_cmp0148) endif() -cmake_minimum_required(VERSION 3.5) - -# The `cmake_minimum_required(VERSION 3.5...3.29)` syntax does not work with -# some versions of VS that have a patched CMake 3.11. This forces us to emulate -# the behavior using the following workaround: -if(${CMAKE_VERSION} VERSION_LESS 3.29) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -else() - cmake_policy(VERSION 3.29) -endif() +cmake_minimum_required(VERSION 3.15...3.30) if(_pybind11_cmp0148) cmake_policy(SET CMP0148 ${_pybind11_cmp0148}) @@ -27,9 +18,7 @@ if(_pybind11_cmp0148) endif() # Avoid infinite recursion if tests include this as a subdirectory -if(DEFINED PYBIND11_MASTER_PROJECT) - return() -endif() +include_guard(GLOBAL) # Extract project version from source file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/pybind11/detail/common.h" @@ -74,14 +63,6 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) set(PYBIND11_MASTER_PROJECT ON) - if(OSX AND CMAKE_VERSION VERSION_LESS 3.7) - # Bug in macOS CMake < 3.7 is unable to download catch - message(WARNING "CMAKE 3.7+ needed on macOS to download catch, and newer HIGHLY recommended") - elseif(WINDOWS AND CMAKE_VERSION VERSION_LESS 3.8) - # Only tested with 3.8+ in CI. - message(WARNING "CMAKE 3.8+ tested on Windows, previous versions untested") - endif() - message(STATUS "CMake ${CMAKE_VERSION}") if(CMAKE_CXX_STANDARD) @@ -133,8 +114,7 @@ cmake_dependent_option( "Install pybind11 headers in Python include directory instead of default installation prefix" OFF "PYBIND11_INSTALL" OFF) -cmake_dependent_option(PYBIND11_FINDPYTHON "Force new FindPython" ${_pybind11_findpython_default} - "NOT CMAKE_VERSION VERSION_LESS 3.12" OFF) +option(PYBIND11_FINDPYTHON "Force new FindPython" ${_pybind11_findpython_default}) # Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests # (makes transition easier while we support both modes). @@ -154,7 +134,7 @@ set(PYBIND11_HEADERS include/pybind11/detail/init.h include/pybind11/detail/internals.h include/pybind11/detail/native_enum_data.h - include/pybind11/detail/smart_holder_poc.h + include/pybind11/detail/struct_smart_holder.h include/pybind11/detail/type_caster_base.h include/pybind11/detail/typeid.h include/pybind11/detail/using_smart_holder.h @@ -190,7 +170,7 @@ set(PYBIND11_HEADERS include/pybind11/typing.h) # Compare with grep and warn if mismatched -if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12) +if(PYBIND11_MASTER_PROJECT) file( GLOB_RECURSE _pybind11_header_check LIST_DIRECTORIES false @@ -208,10 +188,7 @@ if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12) endif() endif() -# CMake 3.12 added list(TRANSFORM PREPEND -# But we can't use it yet -string(REPLACE "include/" "${CMAKE_CURRENT_SOURCE_DIR}/include/" PYBIND11_HEADERS - "${PYBIND11_HEADERS}") +list(TRANSFORM PYBIND11_HEADERS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") # Cache variable so this can be used in parent projects set(pybind11_INCLUDE_DIR @@ -281,25 +258,11 @@ if(PYBIND11_INSTALL) tools/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) - if(CMAKE_VERSION VERSION_LESS 3.14) - # Remove CMAKE_SIZEOF_VOID_P from ConfigVersion.cmake since the library does - # not depend on architecture specific settings or libraries. - set(_PYBIND11_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P}) - unset(CMAKE_SIZEOF_VOID_P) - - write_basic_package_version_file( - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake - VERSION ${PROJECT_VERSION} - COMPATIBILITY AnyNewerVersion) - - set(CMAKE_SIZEOF_VOID_P ${_PYBIND11_CMAKE_SIZEOF_VOID_P}) - else() - # CMake 3.14+ natively supports header-only libraries - write_basic_package_version_file( - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake - VERSION ${PROJECT_VERSION} - COMPATIBILITY AnyNewerVersion ARCH_INDEPENDENT) - endif() + # CMake natively supports header-only libraries + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion ARCH_INDEPENDENT) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake diff --git a/docs/advanced/cast/eigen.rst b/docs/advanced/cast/eigen.rst index a5c11a3f1..894ce97f3 100644 --- a/docs/advanced/cast/eigen.rst +++ b/docs/advanced/cast/eigen.rst @@ -259,7 +259,7 @@ copying to take place: "small"_a // <- This one can be copied if needed ); -With the above binding code, attempting to call the the ``some_method(m)`` +With the above binding code, attempting to call the ``some_method(m)`` method on a ``MyClass`` object, or attempting to call ``some_function(m, m2)`` will raise a ``RuntimeError`` rather than making a temporary copy of the array. It will, however, allow the ``m2`` argument to be copied into a temporary if diff --git a/docs/advanced/cast/stl.rst b/docs/advanced/cast/stl.rst index 03d49b295..42b85532d 100644 --- a/docs/advanced/cast/stl.rst +++ b/docs/advanced/cast/stl.rst @@ -162,7 +162,7 @@ the declaration .. code-block:: cpp - PYBIND11_MAKE_OPAQUE(std::vector); + PYBIND11_MAKE_OPAQUE(std::vector) before any binding code (e.g. invocations to ``class_::def()``, etc.). This macro must be specified at the top level (and outside of any namespaces), since @@ -207,8 +207,8 @@ The following example showcases usage of :file:`pybind11/stl_bind.h`: // Don't forget this #include - PYBIND11_MAKE_OPAQUE(std::vector); - PYBIND11_MAKE_OPAQUE(std::map); + PYBIND11_MAKE_OPAQUE(std::vector) + PYBIND11_MAKE_OPAQUE(std::map) // ... diff --git a/docs/advanced/embedding.rst b/docs/advanced/embedding.rst index cbed82158..e78a8f4ce 100644 --- a/docs/advanced/embedding.rst +++ b/docs/advanced/embedding.rst @@ -18,7 +18,7 @@ information, see :doc:`/compiling`. .. code-block:: cmake - cmake_minimum_required(VERSION 3.5...3.29) + cmake_minimum_required(VERSION 3.15...3.30) project(example) find_package(pybind11 REQUIRED) # or `add_subdirectory(pybind11)` diff --git a/docs/advanced/smart_ptrs.rst b/docs/advanced/smart_ptrs.rst index 6f004a597..e2e9f3f37 100644 --- a/docs/advanced/smart_ptrs.rst +++ b/docs/advanced/smart_ptrs.rst @@ -131,7 +131,7 @@ top namespace level before any binding code: .. code-block:: cpp - PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr); + PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr) The first argument of :func:`PYBIND11_DECLARE_HOLDER_TYPE` should be a placeholder name that is used as a template parameter of the second argument. @@ -143,7 +143,7 @@ by default. Specify .. code-block:: cpp - PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr, true); + PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr, true) if ``SmartPtr`` can always be initialized from a ``T*`` pointer without the risk of inconsistencies (such as multiple independent ``SmartPtr`` instances @@ -161,7 +161,7 @@ specialized: .. code-block:: cpp // Always needed for custom holder types - PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr); + PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr) // Only needed if the type's `.get()` goes by another name namespace PYBIND11_NAMESPACE { namespace detail { diff --git a/docs/basics.rst b/docs/basics.rst index e9b24c7fa..cd97c100e 100644 --- a/docs/basics.rst +++ b/docs/basics.rst @@ -78,6 +78,13 @@ For brevity, all code examples assume that the following two lines are present: namespace py = pybind11; +.. note:: + + ``pybind11/pybind11.h`` includes ``Python.h``, as such it must be the first file + included in any source file or header for `the same reasons as Python.h`_. + +.. _`the same reasons as Python.h`: https://docs.python.org/3/extending/extending.html#a-simple-example + Some features may require additional headers, but those will be specified as needed. .. _simple_example: diff --git a/docs/changelog.rst b/docs/changelog.rst index ab6c713c1..014531774 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -15,6 +15,115 @@ IN DEVELOPMENT Changes will be summarized here periodically. +New Features: + +* Support for Python 3.7 was removed. (Official end-of-life: 2023-06-27). + `#5191 `_ + +* stl.h ``list|set|map_caster`` were made more user friendly: it is no longer + necessary to explicitly convert Python iterables to ``tuple()``, ``set()``, + or ``map()`` in many common situations. + `#4686 `_ + +* Support for CMake older than 3.15 removed. CMake 3.15-3.30 supported. + `#5304 `_ + +* The ``array_caster`` in pybind11/stl.h was enhanced to support value types that are not default-constructible. + `#5305 `_ + +Version 2.13.5 (August 22, 2024) +-------------------------------- + +Bug fixes: + +* Fix includes when using Windows long paths (``\\?\`` prefix). + `#5321 `_ + +* Support ``-Wpedantic`` in C++20 mode. + `#5322 `_ + +* Fix and test ```` support for ``py::tuple`` and ``py::list``. + `#5314 `_ + +Version 2.13.4 (August 14, 2024) +-------------------------------- + +Bug fixes: + +* Fix paths with spaces, including on Windows. + (Replaces regression from `#5302 `_) + `#4874 `_ + +Documentation: + +* Remove repetitive words. + `#5308 `_ + + +Version 2.13.3 (August 13, 2024) +-------------------------------- + +Bug fixes: + +* Quote paths from pybind11-config + `#5302 `_ + + +* Fix typo in Emscripten support when in config mode (CMake) + `#5301 `_ + + +Version 2.13.2 (August 13, 2024) +-------------------------------- + +New Features: + +* A ``pybind11::detail::type_caster_std_function_specializations`` feature was added, to support specializations for + ``std::function``'s with return types that require custom to-Python conversion behavior (to primary use case is to catch and + convert exceptions). + `#4597 `_ + + +Changes: + + +* Use ``PyMutex`` instead of ``std::mutex`` for internal locking in the free-threaded build. + `#5219 `_ + +* Add a special type annotation for C++ empty tuple. + `#5214 `_ + +* When compiling for WebAssembly, add the required exception flags (CMake 3.13+). + `#5298 `_ + +Bug fixes: + +* Make ``gil_safe_call_once_and_store`` thread-safe in free-threaded CPython. + `#5246 `_ + +* A missing ``#include `` in pybind11/typing.h was added to fix build errors (in case user code does not already depend + on that include). + `#5208 `_ + +* Fix regression introduced in #5201 for GCC<10.3 in C++20 mode. + `#5205 `_ + + +.. fix(cmake) + +* Remove extra = when assigning flto value in the case for Clang in CMake. + `#5207 `_ + + +Tests: + +* Adding WASM testing to our CI (Pyodide / Emscripten via scikit-build-core). + `#4745 `_ + +* clang-tidy (in GitHub Actions) was updated from clang 15 to clang 18. + `#5272 `_ + + Version 2.13.1 (June 26, 2024) ------------------------------ diff --git a/docs/compiling.rst b/docs/compiling.rst index 0c788335d..94042c3e5 100644 --- a/docs/compiling.rst +++ b/docs/compiling.rst @@ -18,7 +18,7 @@ A Python extension module can be created with just a few lines of code: .. code-block:: cmake - cmake_minimum_required(VERSION 3.15...3.29) + cmake_minimum_required(VERSION 3.15...3.30) project(example LANGUAGES CXX) set(PYBIND11_FINDPYTHON ON) @@ -319,11 +319,11 @@ Building with CMake For C++ codebases that have an existing CMake-based build system, a Python extension module can be created with just a few lines of code, as seen above in -the module section. Pybind11 currently supports a lower minimum if you don't -use the modern FindPython, though be aware that CMake 3.27 removed the old -mechanism, so pybind11 will automatically switch if the old mechanism is not -available. Please opt into the new mechanism if at all possible. Our default -may change in future versions. This is the minimum required: +the module section. Pybind11 currently defaults to the old mechanism, though be +aware that CMake 3.27 removed the old mechanism, so pybind11 will automatically +switch if the old mechanism is not available. Please opt into the new mechanism +if at all possible. Our default may change in future versions. This is the +minimum required: @@ -333,6 +333,9 @@ may change in future versions. This is the minimum required: .. versionchanged:: 2.11 CMake 3.5+ is required. +.. versionchanged:: 2.14 + CMake 3.15+ is required. + Further information can be found at :doc:`cmake/index`. @@ -388,7 +391,7 @@ that will be respected instead of the built-in flag search. The ``OPT_SIZE`` flag enables size-based optimization equivalent to the standard ``/Os`` or ``-Os`` compiler flags and the ``MinSizeRel`` build type, -which avoid optimizations that that can substantially increase the size of the +which avoid optimizations that can substantially increase the size of the resulting binary. This flag is particularly useful in projects that are split into performance-critical parts and associated bindings. In this case, we can compile the project in release mode (and hence, optimize performance globally), @@ -444,7 +447,7 @@ See the `Config file`_ docstring for details of relevant CMake variables. .. code-block:: cmake - cmake_minimum_required(VERSION 3.4...3.18) + cmake_minimum_required(VERSION 3.15...3.30) project(example LANGUAGES CXX) find_package(pybind11 REQUIRED) @@ -483,14 +486,13 @@ can refer to the same [cmake_example]_ repository for a full sample project FindPython mode --------------- -CMake 3.12+ (3.15+ recommended, 3.18.2+ ideal) added a new module called -FindPython that had a highly improved search algorithm and modern targets -and tools. If you use FindPython, pybind11 will detect this and use the -existing targets instead: +Modern CMake (3.18.2+ ideal) added a new module called FindPython that had a +highly improved search algorithm and modern targets and tools. If you use +FindPython, pybind11 will detect this and use the existing targets instead: .. code-block:: cmake - cmake_minimum_required(VERSION 3.15...3.22) + cmake_minimum_required(VERSION 3.15...3.30) project(example LANGUAGES CXX) find_package(Python 3.8 COMPONENTS Interpreter Development REQUIRED) @@ -541,7 +543,7 @@ available in all modes. The targets provided are: Just the "linking" part of pybind11:module ``pybind11::module`` - Everything for extension modules - ``pybind11::pybind11`` + ``Python::Module`` (FindPython CMake 3.15+) or ``pybind11::python_link_helper`` + Everything for extension modules - ``pybind11::pybind11`` + ``Python::Module`` (FindPython) or ``pybind11::python_link_helper`` ``pybind11::embed`` Everything for embedding the Python interpreter - ``pybind11::pybind11`` + ``Python::Python`` (FindPython) or Python libs @@ -568,7 +570,7 @@ You can use these targets to build complex applications. For example, the .. code-block:: cmake - cmake_minimum_required(VERSION 3.5...3.29) + cmake_minimum_required(VERSION 3.15...3.30) project(example LANGUAGES CXX) find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11) @@ -626,7 +628,7 @@ information about usage in C++, see :doc:`/advanced/embedding`. .. code-block:: cmake - cmake_minimum_required(VERSION 3.5...3.29) + cmake_minimum_required(VERSION 3.15...3.30) project(example LANGUAGES CXX) find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11) @@ -719,7 +721,7 @@ customizable pybind11-based wrappers by parsing C++ header files. [litgen]_ is an automatic python bindings generator with a focus on generating documented and discoverable bindings: bindings will nicely reproduce the documentation -found in headers. It is is based on srcML (srcml.org), a highly scalable, multi-language +found in headers. It is based on srcML (srcml.org), a highly scalable, multi-language parsing tool with a developer centric approach. The API that you want to expose to python must be C++14 compatible (but your implementation can use more modern constructs). diff --git a/docs/faq.rst b/docs/faq.rst index 1eb00efad..92777b5b3 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -258,9 +258,9 @@ CMake configure line. (Replace ``$(which python)`` with a path to python if your prefer.) You can alternatively try ``-DPYBIND11_FINDPYTHON=ON``, which will activate the -new CMake FindPython support instead of pybind11's custom search. Requires -CMake 3.12+, and 3.15+ or 3.18.2+ are even better. You can set this in your -``CMakeLists.txt`` before adding or finding pybind11, as well. +new CMake FindPython support instead of pybind11's custom search. Newer CMake, +like, 3.18.2+, is recommended. You can set this in your ``CMakeLists.txt`` +before adding or finding pybind11, as well. Inconsistent detection of Python version in CMake and pybind11 ============================================================== @@ -281,11 +281,11 @@ There are three possible solutions: from CMake and rely on pybind11 in detecting Python version. If this is not possible, the CMake machinery should be called *before* including pybind11. 2. Set ``PYBIND11_FINDPYTHON`` to ``True`` or use ``find_package(Python - 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 - of the old, deprecated search tools, and these modules are much better at - finding the correct Python. If FindPythonLibs/Interp are not available - (CMake 3.27+), then this will be ignored and FindPython will be used. + COMPONENTS Interpreter Development)`` on modern CMake ( 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 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. 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 diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 4c397c7d1..049d995d0 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -957,8 +957,12 @@ public: using base::value; bool load(handle src, bool convert) { - return base::template load_impl>>( - src, convert); + if (base::template load_impl>>( + src, convert)) { + sh_load_helper.maybe_set_python_instance_is_alias(src); + return true; + } + return false; } explicit operator std::shared_ptr *() { @@ -966,14 +970,14 @@ public: pybind11_fail("Passing `std::shared_ptr *` from Python to C++ is not supported " "(inherently unsafe)."); } - return std::addressof(shared_ptr_holder); + return std::addressof(shared_ptr_storage); } explicit operator std::shared_ptr &() { if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { - shared_ptr_holder = sh_load_helper.load_as_shared_ptr(value); + shared_ptr_storage = sh_load_helper.load_as_shared_ptr(value); } - return shared_ptr_holder; + return shared_ptr_storage; } static handle @@ -1013,12 +1017,13 @@ protected: void load_value(value_and_holder &&v_h) { if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { sh_load_helper.loaded_v_h = v_h; + sh_load_helper.was_populated = true; value = sh_load_helper.get_void_ptr_or_nullptr(); return; } if (v_h.holder_constructed()) { value = v_h.value_ptr(); - shared_ptr_holder = v_h.template holder>(); + shared_ptr_storage = v_h.template holder>(); return; } throw cast_error("Unable to cast from non-held to held instance (T& to Holder) " @@ -1047,8 +1052,8 @@ protected: if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { sh_load_helper.loaded_v_h = sub_caster.sh_load_helper.loaded_v_h; } else { - shared_ptr_holder - = std::shared_ptr(sub_caster.shared_ptr_holder, (type *) value); + shared_ptr_storage + = std::shared_ptr(sub_caster.shared_ptr_storage, (type *) value); } return true; } @@ -1058,8 +1063,8 @@ protected: static bool try_direct_conversions(handle) { return false; } - std::shared_ptr shared_ptr_holder; smart_holder_type_caster_support::load_helper> sh_load_helper; // Const2Mutbl + std::shared_ptr shared_ptr_storage; }; #endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT @@ -1134,20 +1139,25 @@ public: policy = return_value_policy::reference_internal; } if (policy != return_value_policy::reference_internal) { - throw cast_error("Invalid return_value_policy for unique_ptr&"); + throw cast_error("Invalid return_value_policy for const unique_ptr&"); } return type_caster_base::cast(src.get(), policy, parent); } bool load(handle src, bool convert) { - return base::template load_impl< - move_only_holder_caster>>(src, convert); + if (base::template load_impl< + move_only_holder_caster>>(src, convert)) { + sh_load_helper.maybe_set_python_instance_is_alias(src); + return true; + } + return false; } void load_value(value_and_holder &&v_h) { if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { sh_load_helper.loaded_v_h = v_h; sh_load_helper.loaded_v_h.type = typeinfo; + sh_load_helper.was_populated = true; value = sh_load_helper.get_void_ptr_or_nullptr(); return; } @@ -1156,8 +1166,14 @@ public: + clean_type_id(typeinfo->cpptype->name()) + ")"); } - template - using cast_op_type = std::unique_ptr; + template + using cast_op_type + = conditional_t::type, + const std::unique_ptr &>::value + || std::is_same::type, + const std::unique_ptr &>::value, + const std::unique_ptr &, + std::unique_ptr>; explicit operator std::unique_ptr() { if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { @@ -1166,6 +1182,28 @@ public: pybind11_fail("Expected to be UNREACHABLE: " __FILE__ ":" PYBIND11_TOSTRING(__LINE__)); } + explicit operator const std::unique_ptr &() { + if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { + // Get shared_ptr to ensure that the Python object is not disowned elsewhere. + shared_ptr_storage = sh_load_helper.load_as_shared_ptr(value); + // Build a temporary unique_ptr that is meant to never expire. + unique_ptr_storage = std::shared_ptr>( + new std::unique_ptr{ + sh_load_helper.template load_as_const_unique_ptr( + shared_ptr_storage.get())}, + [](std::unique_ptr *ptr) { + if (!ptr) { + pybind11_fail("FATAL: `const std::unique_ptr &` was disowned " + "(EXPECT UNDEFINED BEHAVIOR)."); + } + (void) ptr->release(); + delete ptr; + }); + return *unique_ptr_storage; + } + pybind11_fail("Expected to be UNREACHABLE: " __FILE__ ":" PYBIND11_TOSTRING(__LINE__)); + } + bool try_implicit_casts(handle src, bool convert) { for (auto &cast : typeinfo->implicit_casts) { move_only_holder_caster sub_caster(*cast.first); @@ -1186,6 +1224,8 @@ public: static bool try_direct_conversions(handle) { return false; } smart_holder_type_caster_support::load_helper> sh_load_helper; // Const2Mutbl + std::shared_ptr shared_ptr_storage; // Serves as a pseudo lock. + std::shared_ptr> unique_ptr_storage; }; #endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT diff --git a/include/pybind11/detail/class.h b/include/pybind11/detail/class.h index b79c701c0..191315c0e 100644 --- a/include/pybind11/detail/class.h +++ b/include/pybind11/detail/class.h @@ -9,8 +9,8 @@ #pragma once -#include "../attr.h" -#include "../options.h" +#include +#include PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index b18dc8501..c75e8ab21 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -479,6 +479,8 @@ PYBIND11_WARNING_POP } \endrst */ +PYBIND11_WARNING_PUSH +PYBIND11_WARNING_DISABLE_CLANG("-Wgnu-zero-variadic-macro-arguments") #define PYBIND11_MODULE(name, variable, ...) \ static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name) \ PYBIND11_MAYBE_UNUSED; \ @@ -499,6 +501,7 @@ PYBIND11_WARNING_POP PYBIND11_CATCH_INIT_EXCEPTIONS \ } \ void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ & (variable)) +PYBIND11_WARNING_POP PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) @@ -554,7 +557,7 @@ enum class return_value_policy : uint8_t { object without taking ownership similar to the above return_value_policy::reference policy. In contrast to that policy, the function or property's implicit this argument (called the parent) is - considered to be the the owner of the return value (the child). + considered to be the owner of the return value (the child). pybind11 then couples the lifetime of the parent to the child via a reference relationship that ensures that the parent cannot be garbage collected while Python is still using the child. More advanced @@ -637,6 +640,11 @@ struct instance { bool simple_instance_registered : 1; /// If true, get_internals().patients has an entry for this object bool has_patients : 1; +// Cannot use PYBIND11_INTERNALS_VERSION >= 6 here without refactoring. +#if PYBIND11_VERSION_MAJOR >= 3 + /// If true, this Python object needs to be kept alive for the lifetime of the C++ value. + bool is_alias : 1; +#endif /// Initializes all of the above type/values/holders data (but not the instance values /// themselves) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index cf36bf498..a41c9a97b 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -12,10 +12,10 @@ #include "common.h" #if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT) -# include "../gil.h" +# include #endif -#include "../pytypes.h" +#include #include #include diff --git a/include/pybind11/detail/smart_holder_poc.h b/include/pybind11/detail/struct_smart_holder.h similarity index 84% rename from include/pybind11/detail/smart_holder_poc.h rename to include/pybind11/detail/struct_smart_holder.h index 89742ab27..b1e24d7bb 100644 --- a/include/pybind11/detail/smart_holder_poc.h +++ b/include/pybind11/detail/struct_smart_holder.h @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2021 The Pybind Development Team. +// Copyright (c) 2020-2024 The Pybind Development Team. // All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. @@ -44,16 +44,6 @@ Details: * The `void_cast_raw_ptr` option is needed to make the `smart_holder` `vptr` member invisible to the `shared_from_this` mechanism, in case the lifetime of a `PyObject` is tied to the pointee. - -* Regarding `PYBIND11_TESTS_PURE_CPP_SMART_HOLDER_POC_TEST_CPP` below: - This define serves as a marker for code that is NOT used - from smart_holder_type_casters.h, but is exercised only from - tests/pure_cpp/smart_holder_poc_test.cpp. The marked code was useful - mainly for bootstrapping the smart_holder work. At this stage, with - smart_holder_type_casters.h in production use (at Google) since around - February 2021, it could be moved from here to tests/pure_cpp/ (help welcome). - It will probably be best in most cases to add tests for new functionality - under test/test_class_sh_*. */ #pragma once @@ -146,7 +136,6 @@ struct smart_holder { bool vptr_is_external_shared_ptr : 1; bool is_populated : 1; bool is_disowned : 1; - bool pointee_depends_on_holder_owner : 1; // SMART_HOLDER_WIP: See PR #2839. // Design choice: smart_holder is movable but not copyable. smart_holder(smart_holder &&) = default; @@ -156,8 +145,7 @@ struct smart_holder { smart_holder() : vptr_is_using_noop_deleter{false}, vptr_is_using_builtin_delete{false}, - vptr_is_external_shared_ptr{false}, is_populated{false}, is_disowned{false}, - pointee_depends_on_holder_owner{false} {} + vptr_is_external_shared_ptr{false}, is_populated{false}, is_disowned{false} {} bool has_pointee() const { return vptr != nullptr; } @@ -243,6 +231,24 @@ struct smart_holder { vptr_del_ptr->armed_flag = armed_flag; } + // Caller is responsible for precondition: ensure_compatible_rtti_uqp_del() must succeed. + template + std::unique_ptr extract_deleter(const char *context) const { + const auto *gd = std::get_deleter(vptr); + if (gd && gd->use_del_fun) { + const auto &custom_deleter_ptr = gd->del_fun.template target>(); + if (custom_deleter_ptr == nullptr) { + throw std::runtime_error( + std::string("smart_holder::extract_deleter() precondition failure (") + context + + ")."); + } + static_assert(std::is_copy_constructible::value, + "Required for compatibility with smart_holder functionality."); + return std::unique_ptr(new D(custom_deleter_ptr->deleter)); + } + return nullptr; + } + static smart_holder from_raw_ptr_unowned(void *raw_ptr) { smart_holder hld; hld.vptr.reset(raw_ptr, [](void *) {}); @@ -256,26 +262,6 @@ struct smart_holder { return static_cast(vptr.get()); } -#ifdef PYBIND11_TESTS_PURE_CPP_SMART_HOLDER_POC_TEST_CPP // See comment near top. - template - T &as_lvalue_ref() const { - static const char *context = "as_lvalue_ref"; - ensure_is_populated(context); - ensure_has_pointee(context); - return *as_raw_ptr_unowned(); - } -#endif - -#ifdef PYBIND11_TESTS_PURE_CPP_SMART_HOLDER_POC_TEST_CPP // See comment near top. - template - T &&as_rvalue_ref() const { - static const char *context = "as_rvalue_ref"; - ensure_is_populated(context); - ensure_has_pointee(context); - return std::move(*as_raw_ptr_unowned()); - } -#endif - template static smart_holder from_raw_ptr_take_ownership(T *raw_ptr, bool void_cast_raw_ptr = false) { ensure_pointee_is_destructible("from_raw_ptr_take_ownership"); @@ -319,16 +305,6 @@ struct smart_holder { release_disowned(); } -#ifdef PYBIND11_TESTS_PURE_CPP_SMART_HOLDER_POC_TEST_CPP // See comment near top. - template - T *as_raw_ptr_release_ownership(const char *context = "as_raw_ptr_release_ownership") { - ensure_can_release_ownership(context); - T *raw_ptr = as_raw_ptr_unowned(); - release_ownership(); - return raw_ptr; - } -#endif - template static smart_holder from_unique_ptr(std::unique_ptr &&unq_ptr, void *void_ptr = nullptr) { @@ -351,19 +327,6 @@ struct smart_holder { return hld; } -#ifdef PYBIND11_TESTS_PURE_CPP_SMART_HOLDER_POC_TEST_CPP // See comment near top. - template > - std::unique_ptr as_unique_ptr() { - static const char *context = "as_unique_ptr"; - ensure_compatible_rtti_uqp_del(context); - ensure_use_count_1(context); - T *raw_ptr = as_raw_ptr_unowned(); - release_ownership(); - // KNOWN DEFECT (see PR #4850): Does not copy the deleter. - return std::unique_ptr(raw_ptr); - } -#endif - template static smart_holder from_shared_ptr(std::shared_ptr shd_ptr) { smart_holder hld; diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index 92415718f..f1a5d0d8d 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -9,9 +9,10 @@ #pragma once -#include "../gil.h" -#include "../pytypes.h" -#include "../trampoline_self_life_support.h" +#include +#include +#include + #include "common.h" #include "descr.h" #include "dynamic_raw_ptr_cast_if_possible.h" @@ -704,6 +705,15 @@ inline std::unique_ptr unique_with_deleter(T *raw_ptr, std::unique_ptr template struct load_helper : value_and_holder_helper { + bool was_populated = false; + bool python_instance_is_alias = false; + + void maybe_set_python_instance_is_alias(handle src) { + if (was_populated) { + python_instance_is_alias = reinterpret_cast(src.ptr())->is_alias; + } + } + static std::shared_ptr make_shared_ptr_with_responsible_parent(T *raw_ptr, handle parent) { return std::shared_ptr(raw_ptr, shared_ptr_parent_life_support(parent.ptr())); } @@ -724,7 +734,7 @@ struct load_helper : value_and_holder_helper { throw std::runtime_error("Non-owning holder (load_as_shared_ptr)."); } auto *type_raw_ptr = static_cast(void_raw_ptr); - if (hld.pointee_depends_on_holder_owner) { + if (python_instance_is_alias) { auto *vptr_gd_ptr = std::get_deleter(hld.vptr); if (vptr_gd_ptr != nullptr) { std::shared_ptr released_ptr = vptr_gd_ptr->released_ptr.lock(); @@ -778,28 +788,13 @@ struct load_helper : value_and_holder_helper { auto *self_life_support = dynamic_raw_ptr_cast_if_possible(raw_type_ptr); - if (self_life_support == nullptr && holder().pointee_depends_on_holder_owner) { + if (self_life_support == nullptr && python_instance_is_alias) { throw value_error("Alias class (also known as trampoline) does not inherit from " "py::trampoline_self_life_support, therefore the ownership of this " "instance cannot safely be transferred to C++."); } - // Temporary variable to store the extracted deleter in. - std::unique_ptr extracted_deleter; - - auto *gd = std::get_deleter(holder().vptr); - if (gd && gd->use_del_fun) { // Note the ensure_compatible_rtti_uqp_del() call above. - // In smart_holder_poc, a custom deleter is always stored in a guarded delete. - // The guarded delete's std::function actually points at the - // custom_deleter type, so we can verify it is of the custom deleter type and - // finally extract its deleter. - using custom_deleter_D = pybindit::memory::custom_deleter; - const auto &custom_deleter_ptr = gd->del_fun.template target(); - assert(custom_deleter_ptr != nullptr); - // Now that we have confirmed the type of the deleter matches the desired return - // value we can extract the function. - extracted_deleter = std::unique_ptr(new D(std::move(custom_deleter_ptr->deleter))); - } + std::unique_ptr extracted_deleter = holder().template extract_deleter(context); // Critical transfer-of-ownership section. This must stay together. if (self_life_support != nullptr) { @@ -819,6 +814,19 @@ struct load_helper : value_and_holder_helper { return result; } + + // This assumes load_as_shared_ptr succeeded(), and the returned shared_ptr is still alive. + // The returned unique_ptr is meant to never expire (the behavior is undefined otherwise). + template + std::unique_ptr + load_as_const_unique_ptr(T *raw_type_ptr, const char *context = "load_as_const_unique_ptr") { + if (!have_holder()) { + return unique_with_deleter(nullptr, std::unique_ptr()); + } + holder().template ensure_compatible_rtti_uqp_del(context); + return unique_with_deleter( + raw_type_ptr, std::move(holder().template extract_deleter(context))); + } }; PYBIND11_NAMESPACE_END(smart_holder_type_caster_support) diff --git a/include/pybind11/detail/using_smart_holder.h b/include/pybind11/detail/using_smart_holder.h index 3d0463209..826e6b2c6 100644 --- a/include/pybind11/detail/using_smart_holder.h +++ b/include/pybind11/detail/using_smart_holder.h @@ -10,7 +10,7 @@ #include #ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT -# include "smart_holder_poc.h" +# include "struct_smart_holder.h" #endif PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) diff --git a/include/pybind11/eigen/matrix.h b/include/pybind11/eigen/matrix.h index 8d4342f81..5cf1f0a2a 100644 --- a/include/pybind11/eigen/matrix.h +++ b/include/pybind11/eigen/matrix.h @@ -9,7 +9,8 @@ #pragma once -#include "../numpy.h" +#include + #include "common.h" /* HINT: To suppress warnings originating from the Eigen headers, use -isystem. diff --git a/include/pybind11/eigen/tensor.h b/include/pybind11/eigen/tensor.h index 14bb91c40..0a9d7c252 100644 --- a/include/pybind11/eigen/tensor.h +++ b/include/pybind11/eigen/tensor.h @@ -7,7 +7,8 @@ #pragma once -#include "../numpy.h" +#include + #include "common.h" #if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) diff --git a/include/pybind11/functional.h b/include/pybind11/functional.h index 6856119cd..4b3610117 100644 --- a/include/pybind11/functional.h +++ b/include/pybind11/functional.h @@ -9,12 +9,55 @@ #pragma once +#define PYBIND11_HAS_TYPE_CASTER_STD_FUNCTION_SPECIALIZATIONS + #include "pybind11.h" #include PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(type_caster_std_function_specializations) + +// ensure GIL is held during functor destruction +struct func_handle { + function f; +#if !(defined(_MSC_VER) && _MSC_VER == 1916 && defined(PYBIND11_CPP17)) + // This triggers a syntax error under very special conditions (very weird indeed). + explicit +#endif + func_handle(function &&f_) noexcept + : f(std::move(f_)) { + } + func_handle(const func_handle &f_) { operator=(f_); } + func_handle &operator=(const func_handle &f_) { + gil_scoped_acquire acq; + f = f_.f; + return *this; + } + ~func_handle() { + gil_scoped_acquire acq; + function kill_f(std::move(f)); + } +}; + +// to emulate 'move initialization capture' in C++11 +struct func_wrapper_base { + func_handle hfunc; + explicit func_wrapper_base(func_handle &&hf) noexcept : hfunc(hf) {} +}; + +template +struct func_wrapper : func_wrapper_base { + using func_wrapper_base::func_wrapper_base; + Return operator()(Args... args) const { + gil_scoped_acquire acq; + // casts the returned object as a rvalue to the return type + return hfunc.f(std::forward(args)...).template cast(); + } +}; + +PYBIND11_NAMESPACE_END(type_caster_std_function_specializations) template struct type_caster> { @@ -77,40 +120,8 @@ public: // See PR #1413 for full details } - // ensure GIL is held during functor destruction - struct func_handle { - function f; -#if !(defined(_MSC_VER) && _MSC_VER == 1916 && defined(PYBIND11_CPP17)) - // This triggers a syntax error under very special conditions (very weird indeed). - explicit -#endif - func_handle(function &&f_) noexcept - : f(std::move(f_)) { - } - func_handle(const func_handle &f_) { operator=(f_); } - func_handle &operator=(const func_handle &f_) { - gil_scoped_acquire acq; - f = f_.f; - return *this; - } - ~func_handle() { - gil_scoped_acquire acq; - function kill_f(std::move(f)); - } - }; - - // to emulate 'move initialization capture' in C++11 - struct func_wrapper { - func_handle hfunc; - explicit func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {} - Return operator()(Args... args) const { - gil_scoped_acquire acq; - // casts the returned object as a rvalue to the return type - return hfunc.f(std::forward(args)...).template cast(); - } - }; - - value = func_wrapper(func_handle(std::move(func))); + value = type_caster_std_function_specializations::func_wrapper( + type_caster_std_function_specializations::func_handle(std::move(func))); return true; } diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 0bdc72c21..f2534e78d 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -2278,7 +2278,8 @@ private: } auto *uninitialized_location = std::addressof(v_h.holder()); auto *value_ptr_w_t = v_h.value_ptr(); - bool pointee_depends_on_holder_owner + // Try downcast from `type` to `type_alias`: + inst->is_alias = detail::dynamic_raw_ptr_cast_if_possible(value_ptr_w_t) != nullptr; if (holder_void_ptr) { // Note: inst->owned ignored. @@ -2288,14 +2289,12 @@ private: uninitialized_location, value_ptr_w_t, value_ptr_w_t)) { if (inst->owned) { new (uninitialized_location) holder_type(holder_type::from_raw_ptr_take_ownership( - value_ptr_w_t, /*void_cast_raw_ptr*/ pointee_depends_on_holder_owner)); + value_ptr_w_t, /*void_cast_raw_ptr*/ inst->is_alias)); } else { new (uninitialized_location) holder_type(holder_type::from_raw_ptr_unowned(value_ptr_w_t)); } } - v_h.holder().pointee_depends_on_holder_owner - = pointee_depends_on_holder_owner; v_h.set_holder_constructed(); } diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index a5d9e5aa9..719bf8aa7 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1263,6 +1263,7 @@ protected: using pointer = arrow_proxy; sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) {} + sequence_fast_readonly() = default; // NOLINTNEXTLINE(readability-const-return-type) // PR #3263 reference dereference() const { return *ptr; } @@ -1285,6 +1286,7 @@ protected: using pointer = arrow_proxy; sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) {} + sequence_slow_readwrite() = default; reference dereference() const { return {obj, static_cast(index)}; } void increment() { ++index; } diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index 71bc5902e..6a148e740 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -11,10 +11,14 @@ #include "pybind11.h" #include "detail/common.h" +#include "detail/descr.h" +#include "detail/type_caster_base.h" #include +#include #include #include +#include #include #include #include @@ -35,6 +39,89 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) +// +// Begin: Equivalent of +// https://github.com/google/clif/blob/ae4eee1de07cdf115c0c9bf9fec9ff28efce6f6c/clif/python/runtime.cc#L388-L438 +/* +The three `PyObjectTypeIsConvertibleTo*()` functions below are +the result of converging the behaviors of pybind11 and PyCLIF +(http://github.com/google/clif). + +Originally PyCLIF was extremely far on the permissive side of the spectrum, +while pybind11 was very far on the strict side. Originally PyCLIF accepted any +Python iterable as input for a C++ `vector`/`set`/`map` argument, as long as +the elements were convertible. The obvious (in hindsight) problem was that +any empty Python iterable could be passed to any of these C++ types, e.g. `{}` +was accepted for C++ `vector`/`set` arguments, or `[]` for C++ `map` arguments. + +The functions below strike a practical permissive-vs-strict compromise, +informed by tens of thousands of use cases in the wild. A main objective is +to prevent accidents and improve readability: + +- Python literals must match the C++ types. + +- For C++ `set`: The potentially reducing conversion from a Python sequence + (e.g. Python `list` or `tuple`) to a C++ `set` must be explicit, by going + through a Python `set`. + +- However, a Python `set` can still be passed to a C++ `vector`. The rationale + is that this conversion is not reducing. Implicit conversions of this kind + are also fairly commonly used, therefore enforcing explicit conversions + would have an unfavorable cost : benefit ratio; more sloppily speaking, + such an enforcement would be more annoying than helpful. +*/ + +inline bool PyObjectIsInstanceWithOneOfTpNames(PyObject *obj, + std::initializer_list tp_names) { + if (PyType_Check(obj)) { + return false; + } + const char *obj_tp_name = Py_TYPE(obj)->tp_name; + for (const auto *tp_name : tp_names) { + if (std::strcmp(obj_tp_name, tp_name) == 0) { + return true; + } + } + return false; +} + +inline bool PyObjectTypeIsConvertibleToStdVector(PyObject *obj) { + if (PySequence_Check(obj) != 0) { + return !PyUnicode_Check(obj) && !PyBytes_Check(obj); + } + return (PyGen_Check(obj) != 0) || (PyAnySet_Check(obj) != 0) + || PyObjectIsInstanceWithOneOfTpNames( + obj, {"dict_keys", "dict_values", "dict_items", "map", "zip"}); +} + +inline bool PyObjectTypeIsConvertibleToStdSet(PyObject *obj) { + return (PyAnySet_Check(obj) != 0) || PyObjectIsInstanceWithOneOfTpNames(obj, {"dict_keys"}); +} + +inline bool PyObjectTypeIsConvertibleToStdMap(PyObject *obj) { + if (PyDict_Check(obj)) { + return true; + } + // Implicit requirement in the conditions below: + // A type with `.__getitem__()` & `.items()` methods must implement these + // to be compatible with https://docs.python.org/3/c-api/mapping.html + if (PyMapping_Check(obj) == 0) { + return false; + } + PyObject *items = PyObject_GetAttrString(obj, "items"); + if (items == nullptr) { + PyErr_Clear(); + return false; + } + bool is_convertible = (PyCallable_Check(items) != 0); + Py_DECREF(items); + return is_convertible; +} + +// +// End: Equivalent of clif/python/runtime.cc +// + /// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for /// forwarding a container element). Typically used indirect via forwarded_type(), below. template @@ -66,17 +153,10 @@ private: } void reserve_maybe(const anyset &, void *) {} -public: - bool load(handle src, bool convert) { - if (!isinstance(src)) { - return false; - } - auto s = reinterpret_borrow(src); - value.clear(); - reserve_maybe(s, &value); - for (auto entry : s) { + bool convert_iterable(const iterable &itbl, bool convert) { + for (const auto &it : itbl) { key_conv conv; - if (!conv.load(entry, convert)) { + if (!conv.load(it, convert)) { return false; } value.insert(cast_op(std::move(conv))); @@ -84,6 +164,29 @@ public: return true; } + bool convert_anyset(anyset s, bool convert) { + value.clear(); + reserve_maybe(s, &value); + return convert_iterable(s, convert); + } + +public: + bool load(handle src, bool convert) { + if (!PyObjectTypeIsConvertibleToStdSet(src.ptr())) { + return false; + } + if (isinstance(src)) { + value.clear(); + return convert_anyset(reinterpret_borrow(src), convert); + } + if (!convert) { + return false; + } + assert(isinstance(src)); + value.clear(); + return convert_iterable(reinterpret_borrow(src), convert); + } + template static handle cast(T &&src, return_value_policy policy, handle parent) { if (!std::is_lvalue_reference::value) { @@ -115,15 +218,10 @@ private: } void reserve_maybe(const dict &, void *) {} -public: - bool load(handle src, bool convert) { - if (!isinstance(src)) { - return false; - } - auto d = reinterpret_borrow(src); + bool convert_elements(const dict &d, bool convert) { value.clear(); reserve_maybe(d, &value); - for (auto it : d) { + for (const auto &it : d) { key_conv kconv; value_conv vconv; if (!kconv.load(it.first.ptr(), convert) || !vconv.load(it.second.ptr(), convert)) { @@ -134,6 +232,25 @@ public: return true; } +public: + bool load(handle src, bool convert) { + if (!PyObjectTypeIsConvertibleToStdMap(src.ptr())) { + return false; + } + if (isinstance(src)) { + return convert_elements(reinterpret_borrow(src), convert); + } + if (!convert) { + return false; + } + auto items = reinterpret_steal(PyMapping_Items(src.ptr())); + if (!items) { + throw error_already_set(); + } + assert(isinstance(items)); + return convert_elements(dict(reinterpret_borrow(items)), convert); + } + template static handle cast(T &&src, return_value_policy policy, handle parent) { dict d; @@ -166,20 +283,21 @@ struct list_caster { using value_conv = make_caster; bool load(handle src, bool convert) { - if (!isinstance(src) || isinstance(src) || isinstance(src)) { + if (!PyObjectTypeIsConvertibleToStdVector(src.ptr())) { return false; } - auto s = reinterpret_borrow(src); - value.clear(); - reserve_maybe(s, &value); - for (const auto &it : s) { - value_conv conv; - if (!conv.load(it, convert)) { - return false; - } - value.push_back(cast_op(std::move(conv))); + if (isinstance(src)) { + return convert_elements(src, convert); } - return true; + if (!convert) { + return false; + } + // Designed to be behavior-equivalent to passing tuple(src) from Python: + // The conversion to a tuple will first exhaust the generator object, to ensure that + // the generator is not left in an unpredictable (to the caller) partially-consumed + // state. + assert(isinstance(src)); + return convert_elements(tuple(reinterpret_borrow(src)), convert); } private: @@ -189,6 +307,20 @@ private: } void reserve_maybe(const sequence &, void *) {} + bool convert_elements(handle seq, bool convert) { + auto s = reinterpret_borrow(seq); + value.clear(); + reserve_maybe(s, &value); + for (const auto &it : seq) { + value_conv conv; + if (!conv.load(it, convert)) { + return false; + } + value.push_back(cast_op(std::move(conv))); + } + return true; + } + public: template static handle cast(T &&src, return_value_policy policy, handle parent) { @@ -220,43 +352,87 @@ struct type_caster> : list_caster struct type_caster> : list_caster, Type> {}; +template +ArrayType vector_to_array_impl(V &&v, index_sequence) { + return {{std::move(v[I])...}}; +} + +// Based on https://en.cppreference.com/w/cpp/container/array/to_array +template +ArrayType vector_to_array(V &&v) { + return vector_to_array_impl(std::forward(v), make_index_sequence{}); +} + template struct array_caster { using value_conv = make_caster; private: - template - bool require_size(enable_if_t size) { - if (value.size() != size) { - value.resize(size); - } - return true; - } - template - bool require_size(enable_if_t size) { - return size == Size; - } + std::unique_ptr value; -public: - bool load(handle src, bool convert) { - if (!isinstance(src)) { - return false; - } - auto l = reinterpret_borrow(src); - if (!require_size(l.size())) { - return false; - } + template = 0> + bool convert_elements(handle seq, bool convert) { + auto l = reinterpret_borrow(seq); + value.reset(new ArrayType{}); + // Using `resize` to preserve the behavior exactly as it was before PR #5305 + // For the `resize` to work, `Value` must be default constructible. + // For `std::valarray`, this is a requirement: + // https://en.cppreference.com/w/cpp/named_req/NumericType + value->resize(l.size()); size_t ctr = 0; for (const auto &it : l) { value_conv conv; if (!conv.load(it, convert)) { return false; } - value[ctr++] = cast_op(std::move(conv)); + (*value)[ctr++] = cast_op(std::move(conv)); } return true; } + template = 0> + bool convert_elements(handle seq, bool convert) { + auto l = reinterpret_borrow(seq); + if (l.size() != Size) { + return false; + } + // The `temp` storage is needed to support `Value` types that are not + // default-constructible. + // Deliberate choice: no template specializations, for simplicity, and + // because the compile time overhead for the specializations is deemed + // more significant than the runtime overhead for the `temp` storage. + std::vector temp; + temp.reserve(l.size()); + for (auto it : l) { + value_conv conv; + if (!conv.load(it, convert)) { + return false; + } + temp.emplace_back(cast_op(std::move(conv))); + } + value.reset(new ArrayType(vector_to_array(std::move(temp)))); + return true; + } + +public: + bool load(handle src, bool convert) { + if (!PyObjectTypeIsConvertibleToStdVector(src.ptr())) { + return false; + } + if (isinstance(src)) { + return convert_elements(src, convert); + } + if (!convert) { + return false; + } + // Designed to be behavior-equivalent to passing tuple(src) from Python: + // The conversion to a tuple will first exhaust the generator object, to ensure that + // the generator is not left in an unpredictable (to the caller) partially-consumed + // state. + assert(isinstance(src)); + return convert_elements(tuple(reinterpret_borrow(src)), convert); + } + template static handle cast(T &&src, return_value_policy policy, handle parent) { list l(src.size()); @@ -272,12 +448,36 @@ public: return l.release(); } - PYBIND11_TYPE_CASTER(ArrayType, - const_name(const_name(""), const_name("Annotated[")) - + const_name("list[") + value_conv::name + const_name("]") - + const_name(const_name(""), - const_name(", FixedSize(") - + const_name() + const_name(")]"))); + // Code copied from PYBIND11_TYPE_CASTER macro. + // Intentionally preserving the behavior exactly as it was before PR #5305 + template >::value, int> = 0> + static handle cast(T_ *src, return_value_policy policy, handle parent) { + if (!src) { + return none().release(); + } + if (policy == return_value_policy::take_ownership) { + auto h = cast(std::move(*src), policy, parent); + delete src; // WARNING: Assumes `src` was allocated with `new`. + return h; + } + return cast(*src, policy, parent); + } + + // NOLINTNEXTLINE(google-explicit-constructor) + operator ArrayType *() { return &(*value); } + // NOLINTNEXTLINE(google-explicit-constructor) + operator ArrayType &() { return *value; } + // NOLINTNEXTLINE(google-explicit-constructor) + operator ArrayType &&() && { return std::move(*value); } + + template + using cast_op_type = movable_cast_op_type; + + static constexpr auto name + = const_name(const_name(""), const_name("Annotated[")) + const_name("list[") + + value_conv::name + const_name("]") + + const_name( + const_name(""), const_name(", FixedSize(") + const_name() + const_name(")]")); }; template diff --git a/include/pybind11/stl/filesystem.h b/include/pybind11/stl/filesystem.h index 935d79481..c16a9ae5c 100644 --- a/include/pybind11/stl/filesystem.h +++ b/include/pybind11/stl/filesystem.h @@ -4,11 +4,11 @@ #pragma once -#include "../pybind11.h" -#include "../detail/common.h" -#include "../detail/descr.h" -#include "../cast.h" -#include "../pytypes.h" +#include +#include +#include +#include +#include #include diff --git a/pybind11/__main__.py b/pybind11/__main__.py index b656ce6fe..0abc7e211 100644 --- a/pybind11/__main__.py +++ b/pybind11/__main__.py @@ -2,12 +2,35 @@ from __future__ import annotations import argparse +import re import sys import sysconfig from ._version import __version__ from .commands import get_cmake_dir, get_include, get_pkgconfig_dir +# This is the conditional used for os.path being posixpath +if "posix" in sys.builtin_module_names: + from shlex import quote +elif "nt" in sys.builtin_module_names: + # See https://github.com/mesonbuild/meson/blob/db22551ed9d2dd7889abea01cc1c7bba02bf1c75/mesonbuild/utils/universal.py#L1092-L1121 + # and the original documents: + # https://docs.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments and + # https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ + UNSAFE = re.compile("[ \t\n\r]") + + def quote(s: str) -> str: + if s and not UNSAFE.search(s): + return s + + # Paths cannot contain a '"' on Windows, so we don't need to worry + # about nuanced counting here. + return f'"{s}\\"' if s.endswith("\\") else f'"{s}"' +else: + + def quote(s: str) -> str: + return s + def print_includes() -> None: dirs = [ @@ -22,7 +45,7 @@ def print_includes() -> None: if d and d not in unique_dirs: unique_dirs.append(d) - print(" ".join("-I" + d for d in unique_dirs)) + print(" ".join(quote(f"-I{d}") for d in unique_dirs)) def main() -> None: @@ -54,9 +77,9 @@ def main() -> None: if args.includes: print_includes() if args.cmakedir: - print(get_cmake_dir()) + print(quote(get_cmake_dir())) if args.pkgconfigdir: - print(get_pkgconfig_dir()) + print(quote(get_pkgconfig_dir())) if __name__ == "__main__": diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1a3ef7149..ef30c95d7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,16 +5,7 @@ # All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. -cmake_minimum_required(VERSION 3.5) - -# The `cmake_minimum_required(VERSION 3.5...3.29)` syntax does not work with -# some versions of VS that have a patched CMake 3.11. This forces us to emulate -# the behavior using the following workaround: -if(${CMAKE_VERSION} VERSION_LESS 3.29) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -else() - cmake_policy(VERSION 3.29) -endif() +cmake_minimum_required(VERSION 3.15...3.30) # Filter out items; print an optional message if any items filtered. This ignores extensions. # @@ -76,8 +67,8 @@ project(pybind11_tests CXX) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../tools") option(PYBIND11_WERROR "Report all warnings as errors" OFF) -option(DOWNLOAD_EIGEN "Download EIGEN (requires CMake 3.11+)" OFF) -option(PYBIND11_CUDA_TESTS "Enable building CUDA tests (requires CMake 3.12+)" OFF) +option(DOWNLOAD_EIGEN "Download EIGEN" OFF) +option(PYBIND11_CUDA_TESTS "Enable building CUDA tests" OFF) set(PYBIND11_TEST_OVERRIDE "" CACHE STRING "Tests from ;-separated list of *.cpp files will be built instead of all tests") @@ -130,7 +121,6 @@ set(PYBIND11_TEST_FILES test_class_sh_factory_constructors test_class_sh_inheritance test_class_sh_mi_thunks - test_class_sh_module_local.py test_class_sh_property test_class_sh_property_non_owning test_class_sh_shared_ptr_copy_move @@ -178,6 +168,7 @@ set(PYBIND11_TEST_FILES test_tagbased_polymorphic test_thread test_type_caster_pyobject_ptr + test_type_caster_std_function_specializations test_union test_unnamed_namespace_a test_unnamed_namespace_b @@ -245,8 +236,6 @@ tests_extra_targets("test_exceptions.py;test_local_bindings.py;test_stl.py;test_ # And add additional targets for other tests. tests_extra_targets("test_exceptions.py" "cross_module_interleaved_error_already_set") tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils") -tests_extra_targets("test_class_sh_module_local.py" - "class_sh_module_local_0;class_sh_module_local_1;class_sh_module_local_2") set(PYBIND11_EIGEN_REPO "https://gitlab.com/libeigen/eigen.git" @@ -270,25 +259,21 @@ endif() if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) # Try loading via newer Eigen's Eigen3Config first (bypassing tools/FindEigen3.cmake). # Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also - # produces a fatal error if loaded from a pre-3.0 cmake. if(DOWNLOAD_EIGEN) - if(CMAKE_VERSION VERSION_LESS 3.11) - message(FATAL_ERROR "CMake 3.11+ required when using DOWNLOAD_EIGEN") + if(CMAKE_VERSION VERSION_LESS 3.18) + set(_opts) + else() + set(_opts SOURCE_SUBDIR no-cmake-build) endif() - include(FetchContent) FetchContent_Declare( eigen GIT_REPOSITORY "${PYBIND11_EIGEN_REPO}" - GIT_TAG "${PYBIND11_EIGEN_VERSION_HASH}") - - FetchContent_GetProperties(eigen) - if(NOT eigen_POPULATED) - message( - STATUS - "Downloading Eigen ${PYBIND11_EIGEN_VERSION_STRING} (${PYBIND11_EIGEN_VERSION_HASH}) from ${PYBIND11_EIGEN_REPO}" - ) - FetchContent_Populate(eigen) + GIT_TAG "${PYBIND11_EIGEN_VERSION_HASH}" + ${_opts}) + FetchContent_MakeAvailable(eigen) + if(NOT CMAKE_VERSION VERSION_LESS 3.18) + set(EIGEN3_INCLUDE_DIR "${eigen_SOURCE_DIR}") endif() set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR}) @@ -336,8 +321,7 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I}) endif() - message( - STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN=ON on CMake 3.11+ to download") + message(STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN=ON to download") endif() endif() @@ -417,6 +401,9 @@ function(pybind11_enable_warnings target_name) -Wdeprecated -Wundef -Wnon-virtual-dtor) + if(DEFINED CMAKE_CXX_STANDARD AND NOT CMAKE_CXX_STANDARD VERSION_LESS 20) + target_compile_options(${target_name} PRIVATE -Wpedantic) + endif() endif() if(PYBIND11_WERROR) @@ -522,12 +509,10 @@ foreach(target ${test_targets}) endforeach() # Provide nice organisation in IDEs -if(NOT CMAKE_VERSION VERSION_LESS 3.8) - source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}/../include" - PREFIX "Header Files" - FILES ${PYBIND11_HEADERS}) -endif() +source_group( + TREE "${CMAKE_CURRENT_SOURCE_DIR}/../include" + PREFIX "Header Files" + FILES ${PYBIND11_HEADERS}) # Make sure pytest is found or produce a warning pybind11_find_import(pytest VERSION 3.1) diff --git a/tests/class_sh_module_local_0.cpp b/tests/class_sh_module_local_0.cpp deleted file mode 100644 index 4b570624a..000000000 --- a/tests/class_sh_module_local_0.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include - -#include - -namespace pybind11_tests { -namespace class_sh_module_local { - -struct atyp { // Short for "any type". - std::string mtxt; -}; - -std::string get_mtxt(const atyp &obj) { return obj.mtxt; } - -atyp rtrn_valu_atyp() { return atyp(); } - -} // namespace class_sh_module_local -} // namespace pybind11_tests - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_module_local::atyp) - -PYBIND11_MODULE(class_sh_module_local_0, m) { - m.attr("defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT") = -#ifndef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT - false; -#else - true; - - using namespace pybind11_tests::class_sh_module_local; - - m.def("get_mtxt", get_mtxt); - - m.def("rtrn_valu_atyp", rtrn_valu_atyp); -#endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT -} diff --git a/tests/class_sh_module_local_1.cpp b/tests/class_sh_module_local_1.cpp deleted file mode 100644 index 5197d25f9..000000000 --- a/tests/class_sh_module_local_1.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Identical to class_sh_module_local_2.cpp, except 2 replaced with 1. -#include - -#include - -namespace pybind11_tests { -namespace class_sh_module_local { - -struct atyp { // Short for "any type". - std::string mtxt; -}; - -std::string get_mtxt(const atyp &obj) { return obj.mtxt; } - -} // namespace class_sh_module_local -} // namespace pybind11_tests - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_module_local::atyp) - -PYBIND11_MODULE(class_sh_module_local_1, m) { - m.attr("defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT") = -#ifndef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT - false; -#else - true; - - namespace py = pybind11; - using namespace pybind11_tests::class_sh_module_local; - - py::classh(m, "atyp", py::module_local()) - .def(py::init([](const std::string &mtxt) { - atyp obj; - obj.mtxt = mtxt; - return obj; - })) - .def("tag", [](const atyp &) { return 1; }); - - m.def("get_mtxt", get_mtxt); -#endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT -} diff --git a/tests/class_sh_module_local_2.cpp b/tests/class_sh_module_local_2.cpp deleted file mode 100644 index 0e3a39ba3..000000000 --- a/tests/class_sh_module_local_2.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Identical to class_sh_module_local_1.cpp, except 1 replaced with 2. -#include - -#include - -namespace pybind11_tests { -namespace class_sh_module_local { - -struct atyp { // Short for "any type". - std::string mtxt; -}; - -std::string get_mtxt(const atyp &obj) { return obj.mtxt; } - -} // namespace class_sh_module_local -} // namespace pybind11_tests - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_module_local::atyp) - -PYBIND11_MODULE(class_sh_module_local_2, m) { - m.attr("defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT") = -#ifndef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT - false; -#else - true; - - namespace py = pybind11; - using namespace pybind11_tests::class_sh_module_local; - - py::classh(m, "atyp", py::module_local()) - .def(py::init([](const std::string &mtxt) { - atyp obj; - obj.mtxt = mtxt; - return obj; - })) - .def("tag", [](const atyp &) { return 2; }); - - m.def("get_mtxt", get_mtxt); -#endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT -} diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index e95ed4d36..ad2e7c7bd 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -9,7 +9,6 @@ import tarfile import zipfile # These tests must be run explicitly -# They require CMake 3.15+ (--install) DIR = os.path.abspath(os.path.dirname(__file__)) MAIN_DIR = os.path.dirname(os.path.dirname(DIR)) @@ -61,7 +60,7 @@ detail_headers = { "include/pybind11/detail/init.h", "include/pybind11/detail/internals.h", "include/pybind11/detail/native_enum_data.h", - "include/pybind11/detail/smart_holder_poc.h", + "include/pybind11/detail/struct_smart_holder.h", "include/pybind11/detail/type_caster_base.h", "include/pybind11/detail/typeid.h", "include/pybind11/detail/using_smart_holder.h", diff --git a/tests/local_bindings.h b/tests/local_bindings.h index 01d278535..dea181310 100644 --- a/tests/local_bindings.h +++ b/tests/local_bindings.h @@ -56,13 +56,13 @@ private: std::string message = ""; }; -PYBIND11_MAKE_OPAQUE(LocalVec); -PYBIND11_MAKE_OPAQUE(LocalVec2); -PYBIND11_MAKE_OPAQUE(LocalMap); -PYBIND11_MAKE_OPAQUE(NonLocalVec); -// PYBIND11_MAKE_OPAQUE(NonLocalVec2); // same type as LocalVec2 -PYBIND11_MAKE_OPAQUE(NonLocalMap); -PYBIND11_MAKE_OPAQUE(NonLocalMap2); +PYBIND11_MAKE_OPAQUE(LocalVec) +PYBIND11_MAKE_OPAQUE(LocalVec2) +PYBIND11_MAKE_OPAQUE(LocalMap) +PYBIND11_MAKE_OPAQUE(NonLocalVec) +// PYBIND11_MAKE_OPAQUE(NonLocalVec2) // same type as LocalVec2 +PYBIND11_MAKE_OPAQUE(NonLocalMap) +PYBIND11_MAKE_OPAQUE(NonLocalMap2) // Simple bindings (used with the above): template @@ -70,7 +70,7 @@ py::class_ bind_local(Args &&...args) { return py::class_(std::forward(args)...).def(py::init()).def("get", [](T &i) { return i.i + Adjust; }); -}; +} // Simulate a foreign library base class (to match the example in the docs): namespace pets { diff --git a/tests/pure_cpp/smart_holder_poc.h b/tests/pure_cpp/smart_holder_poc.h new file mode 100644 index 000000000..320311b7d --- /dev/null +++ b/tests/pure_cpp/smart_holder_poc.h @@ -0,0 +1,51 @@ +// Copyright (c) 2020-2024 The Pybind Development Team. +// All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#pragma once + +#include "pybind11/detail/struct_smart_holder.h" + +namespace pybindit { +namespace memory { +namespace smart_holder_poc { // Proof-of-Concept implementations. + +template +T &as_lvalue_ref(const smart_holder &hld) { + static const char *context = "as_lvalue_ref"; + hld.ensure_is_populated(context); + hld.ensure_has_pointee(context); + return *hld.as_raw_ptr_unowned(); +} + +template +T &&as_rvalue_ref(const smart_holder &hld) { + static const char *context = "as_rvalue_ref"; + hld.ensure_is_populated(context); + hld.ensure_has_pointee(context); + return std::move(*hld.as_raw_ptr_unowned()); +} + +template +T *as_raw_ptr_release_ownership(smart_holder &hld, + const char *context = "as_raw_ptr_release_ownership") { + hld.ensure_can_release_ownership(context); + T *raw_ptr = hld.as_raw_ptr_unowned(); + hld.release_ownership(); + return raw_ptr; +} + +template > +std::unique_ptr as_unique_ptr(smart_holder &hld) { + static const char *context = "as_unique_ptr"; + hld.ensure_compatible_rtti_uqp_del(context); + hld.ensure_use_count_1(context); + T *raw_ptr = hld.as_raw_ptr_unowned(); + hld.release_ownership(); + // KNOWN DEFECT (see PR #4850): Does not copy the deleter. + return std::unique_ptr(raw_ptr); +} + +} // namespace smart_holder_poc +} // namespace memory +} // namespace pybindit diff --git a/tests/pure_cpp/smart_holder_poc_test.cpp b/tests/pure_cpp/smart_holder_poc_test.cpp index a9ad0364e..24ab643ee 100644 --- a/tests/pure_cpp/smart_holder_poc_test.cpp +++ b/tests/pure_cpp/smart_holder_poc_test.cpp @@ -1,6 +1,4 @@ -#define PYBIND11_TESTS_PURE_CPP_SMART_HOLDER_POC_TEST_CPP - -#include "pybind11/detail/smart_holder_poc.h" +#include "smart_holder_poc.h" #include #include @@ -16,6 +14,7 @@ #include "catch.hpp" using pybindit::memory::smart_holder; +namespace poc = pybindit::memory::smart_holder_poc; namespace helpers { @@ -70,14 +69,14 @@ TEST_CASE("from_raw_ptr_unowned+as_raw_ptr_unowned", "[S]") { TEST_CASE("from_raw_ptr_unowned+as_lvalue_ref", "[S]") { static int value = 19; auto hld = smart_holder::from_raw_ptr_unowned(&value); - REQUIRE(hld.as_lvalue_ref() == 19); + REQUIRE(poc::as_lvalue_ref(hld) == 19); } TEST_CASE("from_raw_ptr_unowned+as_rvalue_ref", "[S]") { helpers::movable_int orig(19); { auto hld = smart_holder::from_raw_ptr_unowned(&orig); - helpers::movable_int othr(hld.as_rvalue_ref()); + helpers::movable_int othr(poc::as_rvalue_ref(hld)); REQUIRE(othr.valu == 19); REQUIRE(orig.valu == 91); } @@ -86,21 +85,21 @@ TEST_CASE("from_raw_ptr_unowned+as_rvalue_ref", "[S]") { TEST_CASE("from_raw_ptr_unowned+as_raw_ptr_release_ownership", "[E]") { static int value = 19; auto hld = smart_holder::from_raw_ptr_unowned(&value); - REQUIRE_THROWS_WITH(hld.as_raw_ptr_release_ownership(), + REQUIRE_THROWS_WITH(poc::as_raw_ptr_release_ownership(hld), "Cannot disown non-owning holder (as_raw_ptr_release_ownership)."); } TEST_CASE("from_raw_ptr_unowned+as_unique_ptr", "[E]") { static int value = 19; auto hld = smart_holder::from_raw_ptr_unowned(&value); - REQUIRE_THROWS_WITH(hld.as_unique_ptr(), + REQUIRE_THROWS_WITH(poc::as_unique_ptr(hld), "Cannot disown non-owning holder (as_unique_ptr)."); } TEST_CASE("from_raw_ptr_unowned+as_unique_ptr_with_deleter", "[E]") { static int value = 19; auto hld = smart_holder::from_raw_ptr_unowned(&value); - REQUIRE_THROWS_WITH((hld.as_unique_ptr>()), + REQUIRE_THROWS_WITH((poc::as_unique_ptr>(hld)), "Missing unique_ptr deleter (as_unique_ptr)."); } @@ -113,12 +112,12 @@ TEST_CASE("from_raw_ptr_unowned+as_shared_ptr", "[S]") { TEST_CASE("from_raw_ptr_take_ownership+as_lvalue_ref", "[S]") { auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); REQUIRE(hld.has_pointee()); - REQUIRE(hld.as_lvalue_ref() == 19); + REQUIRE(poc::as_lvalue_ref(hld) == 19); } TEST_CASE("from_raw_ptr_take_ownership+as_raw_ptr_release_ownership1", "[S]") { auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); - auto new_owner = std::unique_ptr(hld.as_raw_ptr_release_ownership()); + auto new_owner = std::unique_ptr(poc::as_raw_ptr_release_ownership(hld)); REQUIRE(!hld.has_pointee()); REQUIRE(*new_owner == 19); } @@ -126,13 +125,13 @@ TEST_CASE("from_raw_ptr_take_ownership+as_raw_ptr_release_ownership1", "[S]") { TEST_CASE("from_raw_ptr_take_ownership+as_raw_ptr_release_ownership2", "[E]") { auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); auto shd_ptr = hld.as_shared_ptr(); - REQUIRE_THROWS_WITH(hld.as_raw_ptr_release_ownership(), + REQUIRE_THROWS_WITH(poc::as_raw_ptr_release_ownership(hld), "Cannot disown use_count != 1 (as_raw_ptr_release_ownership)."); } TEST_CASE("from_raw_ptr_take_ownership+as_unique_ptr1", "[S]") { auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); - std::unique_ptr new_owner = hld.as_unique_ptr(); + std::unique_ptr new_owner = poc::as_unique_ptr(hld); REQUIRE(!hld.has_pointee()); REQUIRE(*new_owner == 19); } @@ -140,12 +139,13 @@ TEST_CASE("from_raw_ptr_take_ownership+as_unique_ptr1", "[S]") { TEST_CASE("from_raw_ptr_take_ownership+as_unique_ptr2", "[E]") { auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); auto shd_ptr = hld.as_shared_ptr(); - REQUIRE_THROWS_WITH(hld.as_unique_ptr(), "Cannot disown use_count != 1 (as_unique_ptr)."); + REQUIRE_THROWS_WITH(poc::as_unique_ptr(hld), + "Cannot disown use_count != 1 (as_unique_ptr)."); } TEST_CASE("from_raw_ptr_take_ownership+as_unique_ptr_with_deleter", "[E]") { auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); - REQUIRE_THROWS_WITH((hld.as_unique_ptr>()), + REQUIRE_THROWS_WITH((poc::as_unique_ptr>(hld)), "Missing unique_ptr deleter (as_unique_ptr)."); } @@ -160,14 +160,14 @@ TEST_CASE("from_raw_ptr_take_ownership+disown+reclaim_disowned", "[S]") { auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); std::unique_ptr new_owner(hld.as_raw_ptr_unowned()); hld.disown(); - REQUIRE(hld.as_lvalue_ref() == 19); + REQUIRE(poc::as_lvalue_ref(hld) == 19); REQUIRE(*new_owner == 19); hld.reclaim_disowned(); // Manually veriified: without this, clang++ -fsanitize=address reports // "detected memory leaks". // NOLINTNEXTLINE(bugprone-unused-return-value) (void) new_owner.release(); // Manually verified: without this, clang++ -fsanitize=address // reports "attempting double-free". - REQUIRE(hld.as_lvalue_ref() == 19); + REQUIRE(poc::as_lvalue_ref(hld) == 19); REQUIRE(new_owner.get() == nullptr); } @@ -175,7 +175,7 @@ TEST_CASE("from_raw_ptr_take_ownership+disown+release_disowned", "[S]") { auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); std::unique_ptr new_owner(hld.as_raw_ptr_unowned()); hld.disown(); - REQUIRE(hld.as_lvalue_ref() == 19); + REQUIRE(poc::as_lvalue_ref(hld) == 19); REQUIRE(*new_owner == 19); hld.release_disowned(); REQUIRE(!hld.has_pointee()); @@ -195,14 +195,14 @@ TEST_CASE("from_unique_ptr+as_lvalue_ref", "[S]") { std::unique_ptr orig_owner(new int(19)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); REQUIRE(orig_owner.get() == nullptr); - REQUIRE(hld.as_lvalue_ref() == 19); + REQUIRE(poc::as_lvalue_ref(hld) == 19); } TEST_CASE("from_unique_ptr+as_raw_ptr_release_ownership1", "[S]") { std::unique_ptr orig_owner(new int(19)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); REQUIRE(orig_owner.get() == nullptr); - auto new_owner = std::unique_ptr(hld.as_raw_ptr_release_ownership()); + auto new_owner = std::unique_ptr(poc::as_raw_ptr_release_ownership(hld)); REQUIRE(!hld.has_pointee()); REQUIRE(*new_owner == 19); } @@ -212,7 +212,7 @@ TEST_CASE("from_unique_ptr+as_raw_ptr_release_ownership2", "[E]") { auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); REQUIRE(orig_owner.get() == nullptr); auto shd_ptr = hld.as_shared_ptr(); - REQUIRE_THROWS_WITH(hld.as_raw_ptr_release_ownership(), + REQUIRE_THROWS_WITH(poc::as_raw_ptr_release_ownership(hld), "Cannot disown use_count != 1 (as_raw_ptr_release_ownership)."); } @@ -220,7 +220,7 @@ TEST_CASE("from_unique_ptr+as_unique_ptr1", "[S]") { std::unique_ptr orig_owner(new int(19)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); REQUIRE(orig_owner.get() == nullptr); - std::unique_ptr new_owner = hld.as_unique_ptr(); + std::unique_ptr new_owner = poc::as_unique_ptr(hld); REQUIRE(!hld.has_pointee()); REQUIRE(*new_owner == 19); } @@ -230,14 +230,15 @@ TEST_CASE("from_unique_ptr+as_unique_ptr2", "[E]") { auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); REQUIRE(orig_owner.get() == nullptr); auto shd_ptr = hld.as_shared_ptr(); - REQUIRE_THROWS_WITH(hld.as_unique_ptr(), "Cannot disown use_count != 1 (as_unique_ptr)."); + REQUIRE_THROWS_WITH(poc::as_unique_ptr(hld), + "Cannot disown use_count != 1 (as_unique_ptr)."); } TEST_CASE("from_unique_ptr+as_unique_ptr_with_deleter", "[E]") { std::unique_ptr orig_owner(new int(19)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); REQUIRE(orig_owner.get() == nullptr); - REQUIRE_THROWS_WITH((hld.as_unique_ptr>()), + REQUIRE_THROWS_WITH((poc::as_unique_ptr>(hld)), "Incompatible unique_ptr deleter (as_unique_ptr)."); } @@ -254,7 +255,7 @@ TEST_CASE("from_unique_ptr_derived+as_unique_ptr_base", "[S]") { std::unique_ptr orig_owner(new helpers::derived()); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); REQUIRE(orig_owner.get() == nullptr); - std::unique_ptr new_owner = hld.as_unique_ptr(); + std::unique_ptr new_owner = poc::as_unique_ptr(hld); REQUIRE(!hld.has_pointee()); REQUIRE(new_owner->get() == 100); } @@ -265,7 +266,7 @@ TEST_CASE("from_unique_ptr_derived+as_unique_ptr_base2", "[E]") { auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); REQUIRE(orig_owner.get() == nullptr); REQUIRE_THROWS_WITH( - (hld.as_unique_ptr>()), + (poc::as_unique_ptr>(hld)), "Incompatible unique_ptr deleter (as_unique_ptr)."); } @@ -273,7 +274,7 @@ TEST_CASE("from_unique_ptr_with_deleter+as_lvalue_ref", "[S]") { std::unique_ptr> orig_owner(new int(19)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); REQUIRE(orig_owner.get() == nullptr); - REQUIRE(hld.as_lvalue_ref() == 19); + REQUIRE(poc::as_lvalue_ref(hld) == 19); } TEST_CASE("from_unique_ptr_with_std_function_deleter+as_lvalue_ref", "[S]") { @@ -281,14 +282,14 @@ TEST_CASE("from_unique_ptr_with_std_function_deleter+as_lvalue_ref", "[S]") { new int(19), [](const int *raw_ptr) { delete raw_ptr; }); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); REQUIRE(orig_owner.get() == nullptr); - REQUIRE(hld.as_lvalue_ref() == 19); + REQUIRE(poc::as_lvalue_ref(hld) == 19); } TEST_CASE("from_unique_ptr_with_deleter+as_raw_ptr_release_ownership", "[E]") { std::unique_ptr> orig_owner(new int(19)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); REQUIRE(orig_owner.get() == nullptr); - REQUIRE_THROWS_WITH(hld.as_raw_ptr_release_ownership(), + REQUIRE_THROWS_WITH(poc::as_raw_ptr_release_ownership(hld), "Cannot disown custom deleter (as_raw_ptr_release_ownership)."); } @@ -296,7 +297,7 @@ TEST_CASE("from_unique_ptr_with_deleter+as_unique_ptr", "[E]") { std::unique_ptr> orig_owner(new int(19)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); REQUIRE(orig_owner.get() == nullptr); - REQUIRE_THROWS_WITH(hld.as_unique_ptr(), + REQUIRE_THROWS_WITH(poc::as_unique_ptr(hld), "Incompatible unique_ptr deleter (as_unique_ptr)."); } @@ -305,7 +306,7 @@ TEST_CASE("from_unique_ptr_with_deleter+as_unique_ptr_with_deleter1", "[S]") { auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); REQUIRE(orig_owner.get() == nullptr); std::unique_ptr> new_owner - = hld.as_unique_ptr>(); + = poc::as_unique_ptr>(hld); REQUIRE(!hld.has_pointee()); REQUIRE(*new_owner == 19); } @@ -314,7 +315,7 @@ TEST_CASE("from_unique_ptr_with_deleter+as_unique_ptr_with_deleter2", "[E]") { std::unique_ptr> orig_owner(new int(19)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); REQUIRE(orig_owner.get() == nullptr); - REQUIRE_THROWS_WITH((hld.as_unique_ptr>()), + REQUIRE_THROWS_WITH((poc::as_unique_ptr>(hld)), "Incompatible unique_ptr deleter (as_unique_ptr)."); } @@ -330,27 +331,27 @@ TEST_CASE("from_unique_ptr_with_deleter+as_shared_ptr", "[S]") { TEST_CASE("from_shared_ptr+as_lvalue_ref", "[S]") { std::shared_ptr orig_owner(new int(19)); auto hld = smart_holder::from_shared_ptr(orig_owner); - REQUIRE(hld.as_lvalue_ref() == 19); + REQUIRE(poc::as_lvalue_ref(hld) == 19); } TEST_CASE("from_shared_ptr+as_raw_ptr_release_ownership", "[E]") { std::shared_ptr orig_owner(new int(19)); auto hld = smart_holder::from_shared_ptr(orig_owner); - REQUIRE_THROWS_WITH(hld.as_raw_ptr_release_ownership(), + REQUIRE_THROWS_WITH(poc::as_raw_ptr_release_ownership(hld), "Cannot disown external shared_ptr (as_raw_ptr_release_ownership)."); } TEST_CASE("from_shared_ptr+as_unique_ptr", "[E]") { std::shared_ptr orig_owner(new int(19)); auto hld = smart_holder::from_shared_ptr(orig_owner); - REQUIRE_THROWS_WITH(hld.as_unique_ptr(), + REQUIRE_THROWS_WITH(poc::as_unique_ptr(hld), "Cannot disown external shared_ptr (as_unique_ptr)."); } TEST_CASE("from_shared_ptr+as_unique_ptr_with_deleter", "[E]") { std::shared_ptr orig_owner(new int(19)); auto hld = smart_holder::from_shared_ptr(orig_owner); - REQUIRE_THROWS_WITH((hld.as_unique_ptr>()), + REQUIRE_THROWS_WITH((poc::as_unique_ptr>(hld)), "Missing unique_ptr deleter (as_unique_ptr)."); } @@ -362,19 +363,19 @@ TEST_CASE("from_shared_ptr+as_shared_ptr", "[S]") { TEST_CASE("error_unpopulated_holder", "[E]") { smart_holder hld; - REQUIRE_THROWS_WITH(hld.as_lvalue_ref(), "Unpopulated holder (as_lvalue_ref)."); + REQUIRE_THROWS_WITH(poc::as_lvalue_ref(hld), "Unpopulated holder (as_lvalue_ref)."); } TEST_CASE("error_disowned_holder", "[E]") { auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); - hld.as_unique_ptr(); - REQUIRE_THROWS_WITH(hld.as_lvalue_ref(), "Disowned holder (as_lvalue_ref)."); + poc::as_unique_ptr(hld); + REQUIRE_THROWS_WITH(poc::as_lvalue_ref(hld), "Disowned holder (as_lvalue_ref)."); } TEST_CASE("error_cannot_disown_nullptr", "[E]") { auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); - hld.as_unique_ptr(); - REQUIRE_THROWS_WITH(hld.as_unique_ptr(), "Cannot disown nullptr (as_unique_ptr)."); + poc::as_unique_ptr(hld); + REQUIRE_THROWS_WITH(poc::as_unique_ptr(hld), "Cannot disown nullptr (as_unique_ptr)."); } TEST_CASE("indestructible_int-from_raw_ptr_unowned+as_raw_ptr_unowned", "[S]") { diff --git a/tests/test_callbacks.cpp b/tests/test_callbacks.cpp index 2fd05dec7..e303e7656 100644 --- a/tests/test_callbacks.cpp +++ b/tests/test_callbacks.cpp @@ -148,7 +148,7 @@ TEST_SUBMODULE(callbacks, m) { m.def("dummy_function2", [](int i, int j) { return i + j; }); m.def( "roundtrip", - [](std::function f, bool expect_none = false) { + [](std::function f, bool expect_none) { if (expect_none && f) { throw std::runtime_error("Expected None to be converted to empty std::function"); } diff --git a/tests/test_class_sh_basic.cpp b/tests/test_class_sh_basic.cpp index 9602387b3..ac19a750b 100644 --- a/tests/test_class_sh_basic.cpp +++ b/tests/test_class_sh_basic.cpp @@ -120,6 +120,17 @@ std::string get_mtxt(atyp const &obj) { return obj.mtxt; } std::ptrdiff_t get_ptr(atyp const &obj) { return reinterpret_cast(&obj); } std::unique_ptr unique_ptr_roundtrip(std::unique_ptr obj) { return obj; } + +std::string pass_unique_ptr_cref(const std::unique_ptr &obj) { return obj->mtxt; } + +const std::unique_ptr &rtrn_unique_ptr_cref(const std::string &mtxt) { + static std::unique_ptr obj{new atyp{"static_ctor_arg"}}; + if (!mtxt.empty()) { + obj->mtxt = mtxt; + } + return obj; +} + const std::unique_ptr &unique_ptr_cref_roundtrip(const std::unique_ptr &obj) { return obj; } @@ -217,6 +228,9 @@ TEST_SUBMODULE(class_sh_basic, m) { m.def("get_ptr", get_ptr); // pass_cref m.def("unique_ptr_roundtrip", unique_ptr_roundtrip); // pass_uqmp, rtrn_uqmp + + m.def("pass_unique_ptr_cref", pass_unique_ptr_cref); + m.def("rtrn_unique_ptr_cref", rtrn_unique_ptr_cref); m.def("unique_ptr_cref_roundtrip", unique_ptr_cref_roundtrip); py::classh(m, "SharedPtrStash") diff --git a/tests/test_class_sh_basic.py b/tests/test_class_sh_basic.py index 87f1f8f09..7db7d31b7 100644 --- a/tests/test_class_sh_basic.py +++ b/tests/test_class_sh_basic.py @@ -151,19 +151,31 @@ def test_unique_ptr_roundtrip(num_round_trips=1000): id_orig = id_rtrn -# This currently fails, because a unique_ptr is always loaded by value -# due to pybind11/detail/smart_holder_type_casters.h:689 -# I think, we need to provide more cast operators. -@pytest.mark.skip() -def test_unique_ptr_cref_roundtrip(): - orig = m.atyp("passenger") - id_orig = id(orig) - mtxt_orig = m.get_mtxt(orig) +def test_pass_unique_ptr_cref(): + obj = m.atyp("ctor_arg") + assert re.match("ctor_arg(_MvCtor)*_MvCtor", m.get_mtxt(obj)) + assert re.match("ctor_arg(_MvCtor)*_MvCtor", m.pass_unique_ptr_cref(obj)) + assert re.match("ctor_arg(_MvCtor)*_MvCtor", m.get_mtxt(obj)) - recycled = m.unique_ptr_cref_roundtrip(orig) - assert m.get_mtxt(orig) == mtxt_orig - assert m.get_mtxt(recycled) == mtxt_orig - assert id(recycled) == id_orig + +def test_rtrn_unique_ptr_cref(): + obj0 = m.rtrn_unique_ptr_cref("") + assert m.get_mtxt(obj0) == "static_ctor_arg" + obj1 = m.rtrn_unique_ptr_cref("passed_mtxt_1") + assert m.get_mtxt(obj1) == "passed_mtxt_1" + assert m.get_mtxt(obj0) == "passed_mtxt_1" + assert obj0 is obj1 + + +def test_unique_ptr_cref_roundtrip(num_round_trips=1000): + # Multiple roundtrips to stress-test implementation. + orig = m.atyp("passenger") + mtxt_orig = m.get_mtxt(orig) + recycled = orig + for _ in range(num_round_trips): + recycled = m.unique_ptr_cref_roundtrip(recycled) + assert recycled is orig + assert m.get_mtxt(recycled) == mtxt_orig @pytest.mark.parametrize( diff --git a/tests/test_class_sh_module_local.py b/tests/test_class_sh_module_local.py deleted file mode 100644 index 9f4230052..000000000 --- a/tests/test_class_sh_module_local.py +++ /dev/null @@ -1,30 +0,0 @@ -from __future__ import annotations - -import class_sh_module_local_0 as m0 -import class_sh_module_local_1 as m1 -import class_sh_module_local_2 as m2 -import pytest - -if not m0.defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -def test_cross_module_get_mtxt(): - obj1 = m1.atyp("A") - assert obj1.tag() == 1 - obj2 = m2.atyp("B") - assert obj2.tag() == 2 - assert m1.get_mtxt(obj1) == "A" - assert m2.get_mtxt(obj2) == "B" - assert m1.get_mtxt(obj2) == "B" - assert m2.get_mtxt(obj1) == "A" - assert m0.get_mtxt(obj1) == "A" - assert m0.get_mtxt(obj2) == "B" - - -def test_m0_rtrn_valu_atyp(): - with pytest.raises(TypeError) as exc_info: - m0.rtrn_valu_atyp() - assert str(exc_info.value).startswith( - "Unable to convert function return value to a Python type!" - ) diff --git a/tests/test_class_sh_trampoline_shared_from_this.cpp b/tests/test_class_sh_trampoline_shared_from_this.cpp index e5d688af6..9c2e4ec76 100644 --- a/tests/test_class_sh_trampoline_shared_from_this.cpp +++ b/tests/test_class_sh_trampoline_shared_from_this.cpp @@ -87,7 +87,12 @@ long pass_shared_ptr(const std::shared_ptr &obj) { return sft.use_count(); } -void pass_unique_ptr(const std::unique_ptr &) {} +std::string pass_unique_ptr_cref(const std::unique_ptr &obj) { + return obj ? obj->history : ""; +} +void pass_unique_ptr_rref(std::unique_ptr &&) { + throw std::runtime_error("Expected to not be reached."); +} Sft *make_pure_cpp_sft_raw_ptr(const std::string &history_seed) { return new Sft{history_seed}; } @@ -135,7 +140,8 @@ TEST_SUBMODULE(class_sh_trampoline_shared_from_this, m) { m.def("use_count", use_count); m.def("pass_shared_ptr", pass_shared_ptr); - m.def("pass_unique_ptr", pass_unique_ptr); + m.def("pass_unique_ptr_cref", pass_unique_ptr_cref); + m.def("pass_unique_ptr_rref", pass_unique_ptr_rref); m.def("make_pure_cpp_sft_raw_ptr", make_pure_cpp_sft_raw_ptr); m.def("make_pure_cpp_sft_unq_ptr", make_pure_cpp_sft_unq_ptr); m.def("make_pure_cpp_sft_shd_ptr", make_pure_cpp_sft_shd_ptr); diff --git a/tests/test_class_sh_trampoline_shared_from_this.py b/tests/test_class_sh_trampoline_shared_from_this.py index a074b20a1..7c5ee0e8e 100644 --- a/tests/test_class_sh_trampoline_shared_from_this.py +++ b/tests/test_class_sh_trampoline_shared_from_this.py @@ -137,11 +137,14 @@ def test_pass_released_shared_ptr_as_unique_ptr(): obj = PySft("PySft") stash1 = m.SftSharedPtrStash(1) stash1.Add(obj) # Releases shared_ptr to C++. + assert m.pass_unique_ptr_cref(obj) == "PySft_Stash1Add" + assert obj.history == "PySft_Stash1Add" with pytest.raises(ValueError) as exc_info: - m.pass_unique_ptr(obj) + m.pass_unique_ptr_rref(obj) assert str(exc_info.value) == ( "Python instance is currently owned by a std::shared_ptr." ) + assert obj.history == "PySft_Stash1Add" @pytest.mark.parametrize( diff --git a/tests/test_cmake_build/installed_embed/CMakeLists.txt b/tests/test_cmake_build/installed_embed/CMakeLists.txt index 2be0aa659..5c6267c72 100644 --- a/tests/test_cmake_build/installed_embed/CMakeLists.txt +++ b/tests/test_cmake_build/installed_embed/CMakeLists.txt @@ -1,13 +1,4 @@ -cmake_minimum_required(VERSION 3.5) - -# The `cmake_minimum_required(VERSION 3.5...3.29)` syntax does not work with -# some versions of VS that have a patched CMake 3.11. This forces us to emulate -# the behavior using the following workaround: -if(${CMAKE_VERSION} VERSION_LESS 3.29) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -else() - cmake_policy(VERSION 3.29) -endif() +cmake_minimum_required(VERSION 3.15...3.30) project(test_installed_embed CXX) diff --git a/tests/test_cmake_build/installed_function/CMakeLists.txt b/tests/test_cmake_build/installed_function/CMakeLists.txt index fa7795e1e..2945b3d2e 100644 --- a/tests/test_cmake_build/installed_function/CMakeLists.txt +++ b/tests/test_cmake_build/installed_function/CMakeLists.txt @@ -1,14 +1,4 @@ -cmake_minimum_required(VERSION 3.5) -project(test_installed_module CXX) - -# The `cmake_minimum_required(VERSION 3.5...3.29)` syntax does not work with -# some versions of VS that have a patched CMake 3.11. This forces us to emulate -# the behavior using the following workaround: -if(${CMAKE_VERSION} VERSION_LESS 3.29) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -else() - cmake_policy(VERSION 3.29) -endif() +cmake_minimum_required(VERSION 3.15...3.30) project(test_installed_function CXX) diff --git a/tests/test_cmake_build/installed_target/CMakeLists.txt b/tests/test_cmake_build/installed_target/CMakeLists.txt index 7e73f4243..344c8bc69 100644 --- a/tests/test_cmake_build/installed_target/CMakeLists.txt +++ b/tests/test_cmake_build/installed_target/CMakeLists.txt @@ -1,13 +1,4 @@ -cmake_minimum_required(VERSION 3.5) - -# The `cmake_minimum_required(VERSION 3.5...3.29)` syntax does not work with -# some versions of VS that have a patched CMake 3.11. This forces us to emulate -# the behavior using the following workaround: -if(${CMAKE_VERSION} VERSION_LESS 3.29) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -else() - cmake_policy(VERSION 3.29) -endif() +cmake_minimum_required(VERSION 3.15...3.30) project(test_installed_target CXX) diff --git a/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt b/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt index 33a366472..d0ae0798e 100644 --- a/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt +++ b/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt @@ -1,13 +1,4 @@ -cmake_minimum_required(VERSION 3.5) - -# The `cmake_minimum_required(VERSION 3.5...3.29)` syntax does not work with -# some versions of VS that have a patched CMake 3.11. This forces us to emulate -# the behavior using the following workaround: -if(${CMAKE_VERSION} VERSION_LESS 3.29) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -else() - cmake_policy(VERSION 3.29) -endif() +cmake_minimum_required(VERSION 3.15...3.30) project(test_subdirectory_embed CXX) diff --git a/tests/test_cmake_build/subdirectory_function/CMakeLists.txt b/tests/test_cmake_build/subdirectory_function/CMakeLists.txt index 76418a71f..a521f33df 100644 --- a/tests/test_cmake_build/subdirectory_function/CMakeLists.txt +++ b/tests/test_cmake_build/subdirectory_function/CMakeLists.txt @@ -1,13 +1,4 @@ -cmake_minimum_required(VERSION 3.5) - -# The `cmake_minimum_required(VERSION 3.5...3.29)` syntax does not work with -# some versions of VS that have a patched CMake 3.11. This forces us to emulate -# the behavior using the following workaround: -if(${CMAKE_VERSION} VERSION_LESS 3.29) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -else() - cmake_policy(VERSION 3.29) -endif() +cmake_minimum_required(VERSION 3.15...3.30) project(test_subdirectory_function CXX) diff --git a/tests/test_cmake_build/subdirectory_target/CMakeLists.txt b/tests/test_cmake_build/subdirectory_target/CMakeLists.txt index 28e903187..4a5a7f2be 100644 --- a/tests/test_cmake_build/subdirectory_target/CMakeLists.txt +++ b/tests/test_cmake_build/subdirectory_target/CMakeLists.txt @@ -1,13 +1,4 @@ -cmake_minimum_required(VERSION 3.5) - -# The `cmake_minimum_required(VERSION 3.5...3.29)` syntax does not work with -# some versions of VS that have a patched CMake 3.11. This forces us to emulate -# the behavior using the following workaround: -if(${CMAKE_VERSION} VERSION_LESS 3.29) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -else() - cmake_policy(VERSION 3.29) -endif() +cmake_minimum_required(VERSION 3.15...3.30) project(test_subdirectory_target CXX) diff --git a/tests/test_eigen_matrix.cpp b/tests/test_eigen_matrix.cpp index 21261bfc2..cb8e8c625 100644 --- a/tests/test_eigen_matrix.cpp +++ b/tests/test_eigen_matrix.cpp @@ -55,7 +55,7 @@ void reset_refs() { } // Returns element 2,1 from a matrix (used to test copy/nocopy) -double get_elem(const Eigen::Ref &m) { return m(2, 1); }; +double get_elem(const Eigen::Ref &m) { return m(2, 1); } // Returns a matrix with 10*r + 100*c added to each matrix element (to help test that the matrix // reference is referencing rows/columns correctly). @@ -76,7 +76,7 @@ struct CustomOperatorNew { Eigen::Matrix4d a = Eigen::Matrix4d::Zero(); Eigen::Matrix4d b = Eigen::Matrix4d::Identity(); - EIGEN_MAKE_ALIGNED_OPERATOR_NEW; + EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; TEST_SUBMODULE(eigen_matrix, m) { diff --git a/tests/test_opaque_types.cpp b/tests/test_opaque_types.cpp index 154d0a8d3..da3866cd0 100644 --- a/tests/test_opaque_types.cpp +++ b/tests/test_opaque_types.cpp @@ -18,7 +18,7 @@ // This also deliberately doesn't use the below StringList type alias to test // that MAKE_OPAQUE can handle a type containing a `,`. (The `std::allocator` // bit is just the default `std::vector` allocator). -PYBIND11_MAKE_OPAQUE(std::vector>); +PYBIND11_MAKE_OPAQUE(std::vector>) using StringList = std::vector>; diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index ecb44939a..19f65ce7e 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -13,6 +13,14 @@ #include +//__has_include has been part of C++17, no need to check it +#if defined(PYBIND11_CPP20) && __has_include() +# if !defined(PYBIND11_COMPILER_CLANG) || __clang_major__ >= 16 // llvm/llvm-project#52696 +# define PYBIND11_TEST_PYTYPES_HAS_RANGES +# include +# endif +#endif + namespace external { namespace detail { bool check(PyObject *o) { return PyFloat_Check(o) != 0; } @@ -923,4 +931,59 @@ TEST_SUBMODULE(pytypes, m) { #else m.attr("defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL") = false; #endif + +#if defined(PYBIND11_TEST_PYTYPES_HAS_RANGES) + + // test_tuple_ranges + m.def("tuple_iterator_default_initialization", []() { + using TupleIterator = decltype(std::declval().begin()); + static_assert(std::random_access_iterator); + return TupleIterator{} == TupleIterator{}; + }); + + m.def("transform_tuple_plus_one", [](py::tuple &tpl) { + py::list ret{}; + for (auto it : tpl | std::views::transform([](auto &o) { return py::cast(o) + 1; })) { + ret.append(py::int_(it)); + } + return ret; + }); + + // test_list_ranges + m.def("list_iterator_default_initialization", []() { + using ListIterator = decltype(std::declval().begin()); + static_assert(std::random_access_iterator); + return ListIterator{} == ListIterator{}; + }); + + m.def("transform_list_plus_one", [](py::list &lst) { + py::list ret{}; + for (auto it : lst | std::views::transform([](auto &o) { return py::cast(o) + 1; })) { + ret.append(py::int_(it)); + } + return ret; + }); + + // test_dict_ranges + m.def("dict_iterator_default_initialization", []() { + using DictIterator = decltype(std::declval().begin()); + static_assert(std::forward_iterator); + return DictIterator{} == DictIterator{}; + }); + + m.def("transform_dict_plus_one", [](py::dict &dct) { + py::list ret{}; + for (auto it : dct | std::views::transform([](auto &o) { + return std::pair{py::cast(o.first) + 1, + py::cast(o.second) + 1}; + })) { + ret.append(py::make_tuple(py::int_(it.first), py::int_(it.second))); + } + return ret; + }); + + m.attr("defined_PYBIND11_TEST_PYTYPES_HAS_RANGES") = true; +#else + m.attr("defined_PYBIND11_TEST_PYTYPES_HAS_RANGES") = false; +#endif } diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 218092b43..1c6335f75 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -1048,3 +1048,45 @@ def test_typevar(doc): assert doc(m.annotate_listT_to_T) == "annotate_listT_to_T(arg0: list[T]) -> T" assert doc(m.annotate_object_to_T) == "annotate_object_to_T(arg0: object) -> T" + + +@pytest.mark.skipif( + not m.defined_PYBIND11_TEST_PYTYPES_HAS_RANGES, + reason=" not available.", +) +@pytest.mark.parametrize( + ("tested_tuple", "expected"), + [((1,), [2]), ((3, 4), [4, 5]), ((7, 8, 9), [8, 9, 10])], +) +def test_tuple_ranges(tested_tuple, expected): + assert m.tuple_iterator_default_initialization() + assert m.transform_tuple_plus_one(tested_tuple) == expected + + +@pytest.mark.skipif( + not m.defined_PYBIND11_TEST_PYTYPES_HAS_RANGES, + reason=" not available.", +) +@pytest.mark.parametrize( + ("tested_list", "expected"), [([1], [2]), ([3, 4], [4, 5]), ([7, 8, 9], [8, 9, 10])] +) +def test_list_ranges(tested_list, expected): + assert m.list_iterator_default_initialization() + assert m.transform_list_plus_one(tested_list) == expected + + +@pytest.mark.skipif( + not m.defined_PYBIND11_TEST_PYTYPES_HAS_RANGES, + reason=" not available.", +) +@pytest.mark.parametrize( + ("tested_dict", "expected"), + [ + ({1: 2}, [(2, 3)]), + ({3: 4, 5: 6}, [(4, 5), (6, 7)]), + ({7: 8, 9: 10, 11: 12}, [(8, 9), (10, 11), (12, 13)]), + ], +) +def test_dict_ranges(tested_dict, expected): + assert m.dict_iterator_default_initialization() + assert m.transform_dict_plus_one(tested_dict) == expected diff --git a/tests/test_sequences_and_iterators.cpp b/tests/test_sequences_and_iterators.cpp index 4a1d37f4d..c997b7300 100644 --- a/tests/test_sequences_and_iterators.cpp +++ b/tests/test_sequences_and_iterators.cpp @@ -86,8 +86,8 @@ private: }; using NonCopyableIntPair = std::pair; -PYBIND11_MAKE_OPAQUE(std::vector); -PYBIND11_MAKE_OPAQUE(std::vector); +PYBIND11_MAKE_OPAQUE(std::vector) +PYBIND11_MAKE_OPAQUE(std::vector) template py::list test_random_access_iterator(PythonType x) { diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp index 496073b3c..4ab43953f 100644 --- a/tests/test_smart_ptr.cpp +++ b/tests/test_smart_ptr.cpp @@ -11,6 +11,9 @@ #include "object.h" #include "pybind11_tests.h" +// This breaks on PYBIND11_DECLARE_HOLDER_TYPE +PYBIND11_WARNING_DISABLE_GCC("-Wpedantic") + namespace { // This is just a wrapper around unique_ptr, but with extra fields to deliberately bloat up the @@ -279,13 +282,13 @@ struct holder_helper> { } // namespace PYBIND11_NAMESPACE // Make pybind aware of the ref-counted wrapper type (s): -PYBIND11_DECLARE_HOLDER_TYPE(T, ref, true); +PYBIND11_DECLARE_HOLDER_TYPE(T, ref, true) // The following is not required anymore for std::shared_ptr, but it should compile without error: -PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); -PYBIND11_DECLARE_HOLDER_TYPE(T, huge_unique_ptr); -PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr); -PYBIND11_DECLARE_HOLDER_TYPE(T, shared_ptr_with_addressof_operator); -PYBIND11_DECLARE_HOLDER_TYPE(T, unique_ptr_with_addressof_operator); +PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr) +PYBIND11_DECLARE_HOLDER_TYPE(T, huge_unique_ptr) +PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr) +PYBIND11_DECLARE_HOLDER_TYPE(T, shared_ptr_with_addressof_operator) +PYBIND11_DECLARE_HOLDER_TYPE(T, unique_ptr_with_addressof_operator) TEST_SUBMODULE(smart_ptr, m) { // Please do not interleave `struct` and `class` definitions with bindings code, diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp index 48c907ff3..dd93d51d0 100644 --- a/tests/test_stl.cpp +++ b/tests/test_stl.cpp @@ -59,7 +59,7 @@ struct visit_helper { } // namespace PYBIND11_NAMESPACE #endif -PYBIND11_MAKE_OPAQUE(std::vector>); +PYBIND11_MAKE_OPAQUE(std::vector>) /// Issue #528: templated constructor struct TplCtorClass { @@ -167,6 +167,14 @@ struct type_caster> } // namespace detail } // namespace PYBIND11_NAMESPACE +int pass_std_vector_int(const std::vector &v) { + int zum = 100; + for (const int i : v) { + zum += 2 * i; + } + return zum; +} + TEST_SUBMODULE(stl, m) { // test_vector m.def("cast_vector", []() { return std::vector{1}; }); @@ -193,6 +201,23 @@ TEST_SUBMODULE(stl, m) { m.def("cast_array", []() { return std::array{{1, 2}}; }); m.def("load_array", [](const std::array &a) { return a[0] == 1 && a[1] == 2; }); + struct NoDefaultCtor { + explicit constexpr NoDefaultCtor(int val) : val{val} {} + int val; + }; + + struct NoDefaultCtorArray { + explicit constexpr NoDefaultCtorArray(int i) + : arr{{NoDefaultCtor(10 + i), NoDefaultCtor(20 + i)}} {} + std::array arr; + }; + + // test_array_no_default_ctor + py::class_(m, "NoDefaultCtor").def_readonly("val", &NoDefaultCtor::val); + py::class_(m, "NoDefaultCtorArray") + .def(py::init()) + .def_readwrite("arr", &NoDefaultCtorArray::arr); + // test_valarray m.def("cast_valarray", []() { return std::valarray{1, 4, 9}; }); m.def("load_valarray", [](const std::valarray &v) { @@ -546,4 +571,30 @@ TEST_SUBMODULE(stl, m) { []() { return new std::vector(4513); }, // Without explicitly specifying `take_ownership`, this function leaks. py::return_value_policy::take_ownership); + + m.def("pass_std_vector_int", pass_std_vector_int); + m.def("pass_std_vector_pair_int", [](const std::vector> &v) { + int zum = 0; + for (const auto &ij : v) { + zum += ij.first * 100 + ij.second; + } + return zum; + }); + m.def("pass_std_array_int_2", [](const std::array &a) { + return pass_std_vector_int(std::vector(a.begin(), a.end())) + 1; + }); + m.def("pass_std_set_int", [](const std::set &s) { + int zum = 200; + for (const int i : s) { + zum += 3 * i; + } + return zum; + }); + m.def("pass_std_map_int", [](const std::map &m) { + int zum = 500; + for (const auto &p : m) { + zum += p.first * 1000 + p.second; + } + return zum; + }); } diff --git a/tests/test_stl.py b/tests/test_stl.py index 65fda54cc..6f548789e 100644 --- a/tests/test_stl.py +++ b/tests/test_stl.py @@ -48,6 +48,13 @@ def test_array(doc): ) +def test_array_no_default_ctor(): + lst = m.NoDefaultCtorArray(3) + assert [e.val for e in lst.arr] == [13, 23] + lst.arr = m.NoDefaultCtorArray(4).arr + assert [e.val for e in lst.arr] == [14, 24] + + def test_valarray(doc): """std::valarray <-> list""" lst = m.cast_valarray() @@ -381,3 +388,129 @@ def test_return_vector_bool_raw_ptr(): v = m.return_vector_bool_raw_ptr() assert isinstance(v, list) assert len(v) == 4513 + + +@pytest.mark.parametrize( + ("fn", "offset"), [(m.pass_std_vector_int, 0), (m.pass_std_array_int_2, 1)] +) +def test_pass_std_vector_int(fn, offset): + assert fn([7, 13]) == 140 + offset + assert fn({6, 2}) == 116 + offset + assert fn({"x": 8, "y": 11}.values()) == 138 + offset + assert fn({3: None, 9: None}.keys()) == 124 + offset + assert fn(i for i in [4, 17]) == 142 + offset + assert fn(map(lambda i: i * 3, [8, 7])) == 190 + offset # noqa: C417 + with pytest.raises(TypeError): + fn({"x": 0, "y": 1}) + with pytest.raises(TypeError): + fn({}) + + +def test_pass_std_vector_pair_int(): + fn = m.pass_std_vector_pair_int + assert fn({1: 2, 3: 4}.items()) == 406 + assert fn(zip([5, 17], [13, 9])) == 2222 + + +def test_list_caster_fully_consumes_generator_object(): + def gen_invalid(): + yield from [1, 2.0, 3] + + gen_obj = gen_invalid() + with pytest.raises(TypeError): + m.pass_std_vector_int(gen_obj) + assert not tuple(gen_obj) + + +def test_pass_std_set_int(): + fn = m.pass_std_set_int + assert fn({3, 15}) == 254 + assert fn({5: None, 12: None}.keys()) == 251 + with pytest.raises(TypeError): + fn([]) + with pytest.raises(TypeError): + fn({}) + with pytest.raises(TypeError): + fn({}.values()) + with pytest.raises(TypeError): + fn(i for i in []) + + +def test_set_caster_dict_keys_failure(): + dict_keys = {1: None, 2.0: None, 3: None}.keys() + # The asserts does not really exercise anything in pybind11, but if one of + # them fails in some future version of Python, the set_caster load + # implementation may need to be revisited. + assert tuple(dict_keys) == (1, 2.0, 3) + assert tuple(dict_keys) == (1, 2.0, 3) + with pytest.raises(TypeError): + m.pass_std_set_int(dict_keys) + assert tuple(dict_keys) == (1, 2.0, 3) + + +class FakePyMappingMissingItems: + def __getitem__(self, _): + raise RuntimeError("Not expected to be called.") + + +class FakePyMappingWithItems(FakePyMappingMissingItems): + def items(self): + return ((1, 3), (2, 4)) + + +class FakePyMappingBadItems(FakePyMappingMissingItems): + def items(self): + return ((1, 2), (3, "x")) + + +class FakePyMappingItemsNotCallable(FakePyMappingMissingItems): + @property + def items(self): + return ((1, 2), (3, 4)) + + +class FakePyMappingItemsWithArg(FakePyMappingMissingItems): + def items(self, _): + return ((1, 2), (3, 4)) + + +class FakePyMappingGenObj(FakePyMappingMissingItems): + def __init__(self, gen_obj): + super().__init__() + self.gen_obj = gen_obj + + def items(self): + yield from self.gen_obj + + +def test_pass_std_map_int(): + fn = m.pass_std_map_int + assert fn({1: 2, 3: 4}) == 4506 + with pytest.raises(TypeError): + fn([]) + assert fn(FakePyMappingWithItems()) == 3507 + with pytest.raises(TypeError): + fn(FakePyMappingMissingItems()) + with pytest.raises(TypeError): + fn(FakePyMappingBadItems()) + with pytest.raises(TypeError): + fn(FakePyMappingItemsNotCallable()) + with pytest.raises(TypeError): + fn(FakePyMappingItemsWithArg()) + + +@pytest.mark.parametrize( + ("items", "expected_exception"), + [ + (((1, 2), (3, "x"), (4, 5)), TypeError), + (((1, 2), (3, 4, 5), (6, 7)), ValueError), + ], +) +def test_map_caster_fully_consumes_generator_object(items, expected_exception): + def gen_invalid(): + yield from items + + gen_obj = gen_invalid() + with pytest.raises(expected_exception): + m.pass_std_map_int(FakePyMappingGenObj(gen_obj)) + assert not tuple(gen_obj) diff --git a/tests/test_tagbased_polymorphic.cpp b/tests/test_tagbased_polymorphic.cpp index 24b49021b..13e5ed319 100644 --- a/tests/test_tagbased_polymorphic.cpp +++ b/tests/test_tagbased_polymorphic.cpp @@ -145,4 +145,4 @@ TEST_SUBMODULE(tagbased_polymorphic, m) { .def(py::init()) .def("purr", &Panther::purr); m.def("create_zoo", &create_zoo); -}; +} diff --git a/tests/test_type_caster_std_function_specializations.cpp b/tests/test_type_caster_std_function_specializations.cpp new file mode 100644 index 000000000..89213ddb1 --- /dev/null +++ b/tests/test_type_caster_std_function_specializations.cpp @@ -0,0 +1,46 @@ +#include +#include + +#include "pybind11_tests.h" + +namespace py = pybind11; + +namespace { + +struct SpecialReturn { + int value = 99; +}; + +} // namespace + +namespace pybind11 { +namespace detail { +namespace type_caster_std_function_specializations { + +template +struct func_wrapper : func_wrapper_base { + using func_wrapper_base::func_wrapper_base; + SpecialReturn operator()(Args... args) const { + gil_scoped_acquire acq; + SpecialReturn result; + try { + result = hfunc.f(std::forward(args)...).template cast(); + } catch (error_already_set &) { + result.value += 1; + } + result.value += 100; + return result; + } +}; + +} // namespace type_caster_std_function_specializations +} // namespace detail +} // namespace pybind11 + +TEST_SUBMODULE(type_caster_std_function_specializations, m) { + py::class_(m, "SpecialReturn") + .def(py::init<>()) + .def_readwrite("value", &SpecialReturn::value); + m.def("call_callback_with_special_return", + [](const std::function &func) { return func(); }); +} diff --git a/tests/test_type_caster_std_function_specializations.py b/tests/test_type_caster_std_function_specializations.py new file mode 100644 index 000000000..9e45d4f59 --- /dev/null +++ b/tests/test_type_caster_std_function_specializations.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +from pybind11_tests import type_caster_std_function_specializations as m + + +def test_callback_with_special_return(): + def return_special(): + return m.SpecialReturn() + + def raise_exception(): + raise ValueError("called raise_exception.") + + assert return_special().value == 99 + assert m.call_callback_with_special_return(return_special).value == 199 + assert m.call_callback_with_special_return(raise_exception).value == 200 diff --git a/tests/test_virtual_functions.cpp b/tests/test_virtual_functions.cpp index 93b136ad3..a6164eb81 100644 --- a/tests/test_virtual_functions.cpp +++ b/tests/test_virtual_functions.cpp @@ -589,4 +589,4 @@ void initialize_inherited_virtuals(py::module_ &m) { // Fix issue #1454 (crash when acquiring/releasing GIL on another thread in Python 2.7) m.def("test_gil", &test_gil); m.def("test_gil_from_thread", &test_gil_from_thread); -}; +} diff --git a/tools/pybind11Common.cmake b/tools/pybind11Common.cmake index 8467b45d2..450a14156 100644 --- a/tools/pybind11Common.cmake +++ b/tools/pybind11Common.cmake @@ -2,7 +2,7 @@ Adds the following targets:: - pybind11::pybind11 - link to headers and pybind11 + pybind11::pybind11 - link to Python headers and pybind11::headers pybind11::module - Adds module links pybind11::embed - Adds embed links pybind11::lto - Link time optimizations (only if CMAKE_INTERPROCEDURAL_OPTIMIZATION is not set) @@ -18,11 +18,7 @@ Adds the following functions:: #]======================================================] -# CMake 3.10 has an include_guard command, but we can't use that yet -# include_guard(global) (pre-CMake 3.10) -if(TARGET pybind11::pybind11) - return() -endif() +include_guard(GLOBAL) # If we are in subdirectory mode, all IMPORTED targets must be GLOBAL. If we # are in CONFIG mode, they should be "normal" targets instead. @@ -75,26 +71,36 @@ set_property( APPEND PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11) +# -------------- emscripten requires exceptions enabled ------------- +# _pybind11_no_exceptions is a private mechanism to disable this addition. +# Please open an issue if you need to use it; it will be removed if no one +# needs it. +if(CMAKE_SYSTEM_NAME MATCHES Emscripten AND NOT _pybind11_no_exceptions) + if(is_config) + set(_tmp_config_target pybind11::pybind11_headers) + else() + set(_tmp_config_target pybind11_headers) + endif() + + set_property( + TARGET ${_tmp_config_target} + APPEND + PROPERTY INTERFACE_LINK_OPTIONS -fexceptions) + set_property( + TARGET ${_tmp_config_target} + APPEND + PROPERTY INTERFACE_COMPILE_OPTIONS -fexceptions) + unset(_tmp_config_target) +endif() + # --------------------------- link helper --------------------------- add_library(pybind11::python_link_helper IMPORTED INTERFACE ${optional_global}) -if(CMAKE_VERSION VERSION_LESS 3.13) - # In CMake 3.11+, you can set INTERFACE properties via the normal methods, and - # this would be simpler. - set_property( - TARGET pybind11::python_link_helper - APPEND - PROPERTY INTERFACE_LINK_LIBRARIES "$<$:-undefined dynamic_lookup>") -else() - # link_options was added in 3.13+ - # This is safer, because you are ensured the deduplication pass in CMake will not consider - # these separate and remove one but not the other. - set_property( - TARGET pybind11::python_link_helper - APPEND - PROPERTY INTERFACE_LINK_OPTIONS "$<$:LINKER:-undefined,dynamic_lookup>") -endif() +set_property( + TARGET pybind11::python_link_helper + APPEND + PROPERTY INTERFACE_LINK_OPTIONS "$<$:LINKER:-undefined,dynamic_lookup>") # ------------------------ Windows extras ------------------------- @@ -110,22 +116,14 @@ if(MSVC) # That's also clang-cl # /MP enables multithreaded builds (relevant when there are many files) for MSVC if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # no Clang no Intel - if(CMAKE_VERSION VERSION_LESS 3.11) - set_property( - TARGET pybind11::windows_extras - APPEND - PROPERTY INTERFACE_COMPILE_OPTIONS $<$>:/MP>) - else() - # Only set these options for C++ files. This is important so that, for - # instance, projects that include other types of source files like CUDA - # .cu files don't get these options propagated to nvcc since that would - # cause the build to fail. - set_property( - TARGET pybind11::windows_extras - APPEND - PROPERTY INTERFACE_COMPILE_OPTIONS - $<$>:$<$:/MP>>) - endif() + # Only set these options for C++ files. This is important so that, for + # instance, projects that include other types of source files like CUDA + # .cu files don't get these options propagated to nvcc since that would + # cause the build to fail. + set_property( + TARGET pybind11::windows_extras + APPEND + PROPERTY INTERFACE_COMPILE_OPTIONS $<$>:$<$:/MP>>) endif() endif() @@ -329,7 +327,7 @@ function(_pybind11_generate_lto target prefer_thin_lto) if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le" OR CMAKE_SYSTEM_PROCESSOR MATCHES "mips64") # Do nothing - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES emscripten) + elseif(CMAKE_SYSTEM_NAME MATCHES Emscripten) # This compile is very costly when cross-compiling, so set this without checking set(PYBIND11_LTO_CXX_FLAGS "-flto${thin}${cxx_append}") set(PYBIND11_LTO_LINKER_FLAGS "-flto${thin}${linker_append}") @@ -371,11 +369,7 @@ function(_pybind11_generate_lto target prefer_thin_lto) set(is_debug "$,$>") set(not_debug "$") set(cxx_lang "$") - if(MSVC AND CMAKE_VERSION VERSION_LESS 3.11) - set(genex "${not_debug}") - else() - set(genex "$") - endif() + set(genex "$") set_property( TARGET ${target} APPEND @@ -390,17 +384,10 @@ function(_pybind11_generate_lto target prefer_thin_lto) endif() if(PYBIND11_LTO_LINKER_FLAGS) - if(CMAKE_VERSION VERSION_LESS 3.11) - set_property( - TARGET ${target} - APPEND - PROPERTY INTERFACE_LINK_LIBRARIES "$<${not_debug}:${PYBIND11_LTO_LINKER_FLAGS}>") - else() - set_property( - TARGET ${target} - APPEND - PROPERTY INTERFACE_LINK_OPTIONS "$<${not_debug}:${PYBIND11_LTO_LINKER_FLAGS}>") - endif() + set_property( + TARGET ${target} + APPEND + PROPERTY INTERFACE_LINK_OPTIONS "$<${not_debug}:${PYBIND11_LTO_LINKER_FLAGS}>") endif() endfunction() diff --git a/tools/pybind11Config.cmake.in b/tools/pybind11Config.cmake.in index 304f1d907..2d9fa94f6 100644 --- a/tools/pybind11Config.cmake.in +++ b/tools/pybind11Config.cmake.in @@ -84,7 +84,7 @@ you can either use the basic targets, or use the FindPython tools: # Python method: Python_add_library(MyModule2 src2.cpp) - target_link_libraries(MyModule2 pybind11::headers) + target_link_libraries(MyModule2 PUBLIC pybind11::headers) set_target_properties(MyModule2 PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON CXX_VISIBILITY_PRESET ON diff --git a/tools/pybind11GuessPythonExtSuffix.cmake b/tools/pybind11GuessPythonExtSuffix.cmake index c5fb3b42c..b550f3935 100644 --- a/tools/pybind11GuessPythonExtSuffix.cmake +++ b/tools/pybind11GuessPythonExtSuffix.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15...3.30) function(pybind11_guess_python_module_extension python) diff --git a/tools/pybind11NewTools.cmake b/tools/pybind11NewTools.cmake index 444f3aaf5..d3e938fab 100644 --- a/tools/pybind11NewTools.cmake +++ b/tools/pybind11NewTools.cmake @@ -5,10 +5,6 @@ # All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. -if(CMAKE_VERSION VERSION_LESS 3.12) - message(FATAL_ERROR "You cannot use the new FindPython module with CMake < 3.12") -endif() - include_guard(DIRECTORY) get_property( @@ -236,7 +232,6 @@ if(TARGET ${_Python}::Python) PROPERTY INTERFACE_LINK_LIBRARIES ${_Python}::Python) endif() -# CMake 3.15+ has this if(TARGET ${_Python}::Module) set_property( TARGET pybind11::module diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index 021038d95..d91e7670c 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -5,13 +5,7 @@ # All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. -# include_guard(global) (pre-CMake 3.10) -if(TARGET pybind11::python_headers) - return() -endif() - -# Built-in in CMake 3.5+ -include(CMakeParseArguments) +include_guard(GLOBAL) if(pybind11_FIND_QUIETLY) set(_pybind11_quiet QUIET) @@ -116,36 +110,19 @@ if(PYTHON_IS_DEBUG) PROPERTY INTERFACE_COMPILE_DEFINITIONS Py_DEBUG) endif() -# The <3.11 code here does not support release/debug builds at the same time, like on vcpkg -if(CMAKE_VERSION VERSION_LESS 3.11) - set_property( - TARGET pybind11::module - APPEND - PROPERTY - INTERFACE_LINK_LIBRARIES - pybind11::python_link_helper - "$<$,$>:$>" - ) +# The IMPORTED INTERFACE library here is to ensure that "debug" and "release" get processed outside +# of a generator expression - https://gitlab.kitware.com/cmake/cmake/-/issues/18424, as they are +# target_link_library keywords rather than real libraries. +add_library(pybind11::_ClassicPythonLibraries IMPORTED INTERFACE) +target_link_libraries(pybind11::_ClassicPythonLibraries INTERFACE ${PYTHON_LIBRARIES}) +target_link_libraries( + pybind11::module + INTERFACE + pybind11::python_link_helper + "$<$,$>:pybind11::_ClassicPythonLibraries>") - set_property( - TARGET pybind11::embed - APPEND - PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11 $) -else() - # The IMPORTED INTERFACE library here is to ensure that "debug" and "release" get processed outside - # of a generator expression - https://gitlab.kitware.com/cmake/cmake/-/issues/18424, as they are - # target_link_library keywords rather than real libraries. - add_library(pybind11::_ClassicPythonLibraries IMPORTED INTERFACE) - target_link_libraries(pybind11::_ClassicPythonLibraries INTERFACE ${PYTHON_LIBRARIES}) - target_link_libraries( - pybind11::module - INTERFACE - pybind11::python_link_helper - "$<$,$>:pybind11::_ClassicPythonLibraries>") - - target_link_libraries(pybind11::embed INTERFACE pybind11::pybind11 - pybind11::_ClassicPythonLibraries) -endif() +target_link_libraries(pybind11::embed INTERFACE pybind11::pybind11 + pybind11::_ClassicPythonLibraries) function(pybind11_extension name) # The prefix and extension are provided by FindPythonLibsNew.cmake diff --git a/tools/test-pybind11GuessPythonExtSuffix.cmake b/tools/test-pybind11GuessPythonExtSuffix.cmake index 0de2c0169..ac90e039b 100644 --- a/tools/test-pybind11GuessPythonExtSuffix.cmake +++ b/tools/test-pybind11GuessPythonExtSuffix.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15...3.30) # Tests for pybind11_guess_python_module_extension # Run using `cmake -P tools/test-pybind11GuessPythonExtSuffix.cmake`