diff --git a/pybind11/.appveyor.yml b/pybind11/.appveyor.yml index 149a8a3..85445d4 100644 --- a/pybind11/.appveyor.yml +++ b/pybind11/.appveyor.yml @@ -19,7 +19,7 @@ install: if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" } $env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH" python -W ignore -m pip install --upgrade pip wheel - python -W ignore -m pip install pytest numpy --no-warn-script-location + python -W ignore -m pip install pytest numpy --no-warn-script-location pytest-timeout - ps: | Start-FileDownload 'https://gitlab.com/libeigen/eigen/-/archive/3.3.7/eigen-3.3.7.zip' 7z x eigen-3.3.7.zip -y > $null diff --git a/pybind11/.clang-format b/pybind11/.clang-format new file mode 100644 index 0000000..8700fca --- /dev/null +++ b/pybind11/.clang-format @@ -0,0 +1,21 @@ +--- +# See all possible options and defaults with: +# clang-format --style=llvm --dump-config +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignConsecutiveAssignments: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BreakBeforeBinaryOperators: All +BreakConstructorInitializers: BeforeColon +ColumnLimit: 99 +IndentCaseLabels: true +IndentPPDirectives: AfterHash +IndentWidth: 4 +Language: Cpp +SpaceAfterCStyleCast: true +# SpaceInEmptyBlock: true # too new +Standard: Cpp11 +TabWidth: 4 +... diff --git a/pybind11/.github/CONTRIBUTING.md b/pybind11/.github/CONTRIBUTING.md index 4ced21b..08d9e7c 100644 --- a/pybind11/.github/CONTRIBUTING.md +++ b/pybind11/.github/CONTRIBUTING.md @@ -126,7 +126,8 @@ cmake --build build --target check `--target` can be spelled `-t` in CMake 3.15+. You can also run individual tests with these targets: -* `pytest`: Python tests only +* `pytest`: Python tests only, using the +[pytest](https://docs.pytest.org/en/stable/) framework * `cpptest`: C++ tests only * `test_cmake_build`: Install / subdirectory tests @@ -134,6 +135,17 @@ If you want to build just a subset of tests, use `-DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_pickling.cpp"`. If this is empty, all tests will be built. +You may also pass flags to the `pytest` target by editing `tests/pytest.ini` or +by using the `PYTEST_ADDOPTS` environment variable +(see [`pytest` docs](https://docs.pytest.org/en/2.7.3/customize.html#adding-default-options)). As an example: + +```bash +env PYTEST_ADDOPTS="--capture=no --exitfirst" \ + cmake --build build --target pytest +# Or using abbreviated flags +env PYTEST_ADDOPTS="-s -x" cmake --build build --target pytest +``` + ### Formatting All formatting is handled by pre-commit. @@ -164,10 +176,35 @@ name, pre-commit): pre-commit install ``` +### Clang-Format + +As of v2.6.2, pybind11 ships with a [`clang-format`][clang-format] +configuration file at the top level of the repo (the filename is +`.clang-format`). Currently, formatting is NOT applied automatically, but +manually using `clang-format` for newly developed files is highly encouraged. +To check if a file needs formatting: + +```bash +clang-format -style=file --dry-run some.cpp +``` + +The output will show things to be fixed, if any. To actually format the file: + +```bash +clang-format -style=file -i some.cpp +``` + +Note that the `-style-file` option searches the parent directories for the +`.clang-format` file, i.e. the commands above can be run in any subdirectory +of the pybind11 repo. + ### Clang-Tidy -To run Clang tidy, the following recipe should work. Files will be modified in -place, so you can use git to monitor the changes. +[`clang-tidy`][clang-tidy] performs deeper static code analyses and is +more complex to run, compared to `clang-format`, but support for `clang-tidy` +is built into the pybind11 CMake configuration. To run `clang-tidy`, the +following recipe should work. Files will be modified in place, so you can +use git to monitor the changes. ```bash docker run --rm -v $PWD:/pybind11 -it silkeh/clang:10 @@ -186,7 +223,7 @@ cmake -S . -B build-iwyu -DCMAKE_CXX_INCLUDE_WHAT_YOU_USE=$(which include-what-y cmake --build build ``` -The report is sent to stderr; you can pip it into a file if you wish. +The report is sent to stderr; you can pipe it into a file if you wish. ### Build recipes @@ -313,6 +350,8 @@ if you really want to. [pre-commit]: https://pre-commit.com +[clang-format]: https://clang.llvm.org/docs/ClangFormat.html +[clang-tidy]: https://clang.llvm.org/extra/clang-tidy/ [pybind11.readthedocs.org]: http://pybind11.readthedocs.org/en/latest [issue tracker]: https://github.com/pybind/pybind11/issues [gitter]: https://gitter.im/pybind/Lobby diff --git a/pybind11/.github/dependabot.yml b/pybind11/.github/dependabot.yml index c1eac3c..7327336 100644 --- a/pybind11/.github/dependabot.yml +++ b/pybind11/.github/dependabot.yml @@ -6,6 +6,11 @@ updates: schedule: interval: "daily" ignore: - # Offical actions have moving tags like v1 + # Official actions have moving tags like v1 # that are used, so they don't need updates here - - dependency-name: "actions/*" + - dependency-name: "actions/checkout" + - dependency-name: "actions/setup-python" + - dependency-name: "actions/cache" + - dependency-name: "actions/upload-artifact" + - dependency-name: "actions/download-artifact" + - dependency-name: "actions/labeler" diff --git a/pybind11/.github/pull_request_template.md b/pybind11/.github/pull_request_template.md index 5570f6f..97a6ff7 100644 --- a/pybind11/.github/pull_request_template.md +++ b/pybind11/.github/pull_request_template.md @@ -5,7 +5,8 @@ ## Suggested changelog entry: - + ```rst diff --git a/pybind11/.github/workflows/ci.yml b/pybind11/.github/workflows/ci.yml index 73424f9..f90c199 100644 --- a/pybind11/.github/workflows/ci.yml +++ b/pybind11/.github/workflows/ci.yml @@ -21,10 +21,8 @@ jobs: - 2.7 - 3.5 - 3.6 - - 3.7 - - 3.8 - 3.9 - # - 3.10.0-alpha.1 - need next release for pybind11 fix + # - 3.10-dev # Re-enable once 3.10.0a5 is released - pypy2 - pypy3 @@ -44,10 +42,6 @@ jobs: python: 3.6 args: > -DPYBIND11_FINDPYTHON=ON - - runs-on: ubuntu-latest - python: 3.8 - args: > - -DPYBIND11_FINDPYTHON=ON # These items will be removed from the build matrix, keys must match. exclude: @@ -57,11 +51,9 @@ jobs: - runs-on: windows-latest python: pypy3 - # Let's drop a few macOS runs since that tends to be 2.7 or 3.8+ - - runs-on: macos-latest - python: 3.6 - - runs-on: macos-latest - python: 3.7 + # TODO: PyPy2 7.3.3 segfaults, while 7.3.2 was fine. + - runs-on: ubuntu-latest + python: pypy2 name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • x64 ${{ matrix.args }}" runs-on: ${{ matrix.runs-on }} @@ -79,7 +71,7 @@ jobs: run: echo "BOOST_ROOT=$BOOST_ROOT_1_72_0" >> $GITHUB_ENV - name: Update CMake - uses: jwlawson/actions-setup-cmake@v1.4 + uses: jwlawson/actions-setup-cmake@v1.7 - name: Cache wheels if: runner.os == 'macOS' @@ -117,7 +109,7 @@ jobs: - name: C++11 tests # TODO: Figure out how to load the DLL on Python 3.8+ - if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9))" + if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10-dev'))" run: cmake --build . --target cpptest -j 2 - name: Interface test C++11 @@ -145,7 +137,7 @@ jobs: - name: C++ tests # TODO: Figure out how to load the DLL on Python 3.8+ - if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9))" + if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10-dev'))" run: cmake --build build2 --target cpptest - name: Interface test @@ -173,6 +165,84 @@ jobs: run: pytest tests/extra_setuptools + deadsnakes: + strategy: + fail-fast: false + matrix: + include: + - python-version: 3.9 + python-debug: true + valgrind: true + - python-version: 3.10-dev + python-debug: false + + name: "🐍 ${{ matrix.python-version }}${{ matrix.python-debug && '-dbg' || '' }} (deadsnakes)${{ matrix.valgrind && ' • Valgrind' || '' }} • x64" + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Setup Python ${{ matrix.python-version }} (deadsnakes) + uses: deadsnakes/action@v2.1.1 + with: + python-version: ${{ matrix.python-version }} + debug: ${{ matrix.python-debug }} + + - name: Update CMake + uses: jwlawson/actions-setup-cmake@v1.7 + + - name: Valgrind cache + if: matrix.valgrind + uses: actions/cache@v2 + 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 --prefer-binary + + - name: Configure + run: > + cmake -S . -B build + -DCMAKE_BUILD_TYPE=Debug + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DDOWNLOAD_EIGEN=ON + -DCMAKE_CXX_STANDARD=17 + + - 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 @@ -250,44 +320,48 @@ jobs: run: cmake --build build --target pytest - # Testing CentOS 8 + PGI compilers - centos-nvhpc8: - runs-on: ubuntu-latest - name: "🐍 3 • CentOS8 / PGI 20.7 • x64" - container: centos:8 - - steps: - - uses: actions/checkout@v2 - - - 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/nvhpc-20-7-20.7-1.x86_64.rpm https://developer.download.nvidia.com/hpc-sdk/nvhpc-2020-20.7-1.x86_64.rpm - - - name: Configure - shell: bash - run: | - source /etc/profile.d/modules.sh - module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/20.7 - 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 +# 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@v2 +# +# - 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 CentOS 7 + PGI compilers, which seems to require more workarounds @@ -338,6 +412,7 @@ jobs: - name: Interface test run: cmake3 --build build --target test_cmake_build + # Testing on GCC using the GCC docker images (only recent images supported) gcc: runs-on: ubuntu-latest @@ -365,10 +440,8 @@ jobs: - name: Update pip run: python3 -m pip install --upgrade pip - - name: Setup CMake 3.18 - uses: jwlawson/actions-setup-cmake@v1.4 - with: - cmake-version: 3.18 + - name: Update CMake + uses: jwlawson/actions-setup-cmake@v1.7 - name: Configure shell: bash @@ -392,6 +465,103 @@ jobs: run: cmake --build build --target test_cmake_build + # Testing on ICC using the oneAPI apt repo + icc: + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + + name: "🐍 3 • ICC latest • x64" + + steps: + - uses: actions/checkout@v2 + + - 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 --prefer-binary + + - 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_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_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, and this is an easy way # to get GCC 4.8, which is the manylinux1 compiler). centos: @@ -553,7 +723,7 @@ jobs: architecture: x86 - name: Update CMake - uses: jwlawson/actions-setup-cmake@v1.4 + uses: jwlawson/actions-setup-cmake@v1.7 - name: Prepare MSVC uses: ilammy/msvc-dev-cmd@v1 @@ -599,7 +769,7 @@ jobs: python-version: ${{ matrix.python }} - name: Update CMake - uses: jwlawson/actions-setup-cmake@v1.4 + uses: jwlawson/actions-setup-cmake@v1.7 - name: Prepare MSVC uses: ilammy/msvc-dev-cmd@v1 @@ -653,7 +823,7 @@ jobs: python-version: ${{ matrix.python }} - name: Update CMake - uses: jwlawson/actions-setup-cmake@v1.4 + uses: jwlawson/actions-setup-cmake@v1.7 - name: Prepare env run: python -m pip install -r tests/requirements.txt --prefer-binary diff --git a/pybind11/.github/workflows/configure.yml b/pybind11/.github/workflows/configure.yml index 23f6022..578dba6 100644 --- a/pybind11/.github/workflows/configure.yml +++ b/pybind11/.github/workflows/configure.yml @@ -55,7 +55,7 @@ jobs: # An action for adding a specific version of CMake: # https://github.com/jwlawson/actions-setup-cmake - name: Setup CMake ${{ matrix.cmake }} - uses: jwlawson/actions-setup-cmake@v1.3 + uses: jwlawson/actions-setup-cmake@v1.7 with: cmake-version: ${{ matrix.cmake }} diff --git a/pybind11/.pre-commit-config.yaml b/pybind11/.pre-commit-config.yaml index 85254a8..6781ac4 100644 --- a/pybind11/.pre-commit-config.yaml +++ b/pybind11/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: # Standard hooks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 + rev: v3.4.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -46,7 +46,7 @@ repos: # Flake8 also supports pre-commit natively (same author) - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.3 + rev: 3.8.4 hooks: - id: flake8 additional_dependencies: [flake8-bugbear, pep8-naming] @@ -63,7 +63,7 @@ repos: # Check static types with mypy - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.790 + rev: v0.800 hooks: - id: mypy # The default Python type ignores .pyi files, so let's rerun if detected @@ -74,7 +74,7 @@ repos: # Checks the manifest for missing files (native support) - repo: https://github.com/mgedmin/check-manifest - rev: "0.43" + rev: "0.46" hooks: - id: check-manifest # This is a slow hook, so only run this if --hook-stage manual is passed diff --git a/pybind11/CMakeLists.txt b/pybind11/CMakeLists.txt index 2c08ff0..ded4dad 100644 --- a/pybind11/CMakeLists.txt +++ b/pybind11/CMakeLists.txt @@ -45,8 +45,13 @@ if(NOT pybind11_FIND_QUIETLY) message(STATUS "pybind11 v${pybind11_VERSION} ${pybind11_VERSION_TYPE}") endif() +# Avoid infinite recursion if tests include this as a subdirectory +if(DEFINED PYBIND11_MASTER_PROJECT) + set(PYBIND11_TEST OFF) +endif() + # Check if pybind11 is being used directly or via add_subdirectory -if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR AND NOT DEFINED PYBIND11_MASTER_PROJECT) ### Warn if not an out-of-source builds if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) set(lines @@ -73,6 +78,8 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD_REQUIRED ON) endif() + + set(pybind11_system "") else() set(PYBIND11_MASTER_PROJECT OFF) set(pybind11_system SYSTEM) @@ -159,12 +166,24 @@ endif() # You can also place ifs *in* the Config.in, but not here. # This section builds targets, but does *not* touch Python - -# Build the headers-only target (no Python included): -# (long name used here to keep this from clashing in subdirectory mode) -add_library(pybind11_headers INTERFACE) -add_library(pybind11::pybind11_headers ALIAS pybind11_headers) # to match exported target -add_library(pybind11::headers ALIAS pybind11_headers) # easier to use/remember +# Non-IMPORT targets cannot be defined twice +if(NOT TARGET pybind11_headers) + # Build the headers-only target (no Python included): + # (long name used here to keep this from clashing in subdirectory mode) + add_library(pybind11_headers INTERFACE) + add_library(pybind11::pybind11_headers ALIAS pybind11_headers) # to match exported target + add_library(pybind11::headers ALIAS pybind11_headers) # easier to use/remember + + target_include_directories( + pybind11_headers ${pybind11_system} INTERFACE $ + $) + + target_compile_features(pybind11_headers INTERFACE cxx_inheriting_constructors cxx_user_literals + cxx_right_angle_brackets) +else() + # It is invalid to install a target twice, too. + set(PYBIND11_INSTALL OFF) +endif() include("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11Common.cmake") @@ -175,14 +194,6 @@ elseif(USE_PYTHON_INCLUDE_DIR AND DEFINED PYTHON_INCLUDE_DIR) file(RELATIVE_PATH CMAKE_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_PREFIX} ${PYTHON_INCLUDE_DIRS}) endif() -# Fill in headers target -target_include_directories( - pybind11_headers ${pybind11_system} INTERFACE $ - $) - -target_compile_features(pybind11_headers INTERFACE cxx_inheriting_constructors cxx_user_literals - cxx_right_angle_brackets) - if(PYBIND11_INSTALL) install(DIRECTORY ${pybind11_INCLUDE_DIR}/pybind11 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) set(PYBIND11_CMAKECONFIG_INSTALL_DIR diff --git a/pybind11/README.rst b/pybind11/README.rst index 1474cb9..6c2a255 100644 --- a/pybind11/README.rst +++ b/pybind11/README.rst @@ -5,18 +5,27 @@ |Latest Documentation Status| |Stable Documentation Status| |Gitter chat| |CI| |Build status| +|Repology| |PyPI package| |Conda-forge| |Python Versions| + +`Setuptools example `_ +• `Scikit-build example `_ +• `CMake example `_ + +.. start + .. warning:: - Combining older versions of pybind11 (< 2.6.0) with the brand-new Python - 3.9.0 will trigger undefined behavior that typically manifests as crashes - during interpreter shutdown (but could also destroy your data. **You have been + Combining older versions of pybind11 (< 2.6.0) with Python 3.9.0 will + trigger undefined behavior that typically manifests as crashes during + interpreter shutdown (but could also destroy your data. **You have been warned.**) - We recommend that you wait for Python 3.9.1 slated for release in December, - which will include a `fix `_ - that resolves this problem. In the meantime, please update to the latest - version of pybind11 (2.6.0 or newer), which includes a temporary workaround - specifically when Python 3.9.0 is detected at runtime. + We recommend that you update to the latest patch release of Python (3.9.1), + which includes a `fix `_ + that resolves this problem. If you do use Python 3.9.0, please update to + the latest version of pybind11 (2.6.0 or newer), which includes a temporary + workaround specifically when Python 3.9.0 is detected at runtime. + **pybind11** is a lightweight header-only library that exposes C++ types in Python and vice versa, mainly to create Python bindings of existing @@ -127,11 +136,10 @@ Supported compilers newer) 2. GCC 4.8 or newer 3. Microsoft Visual Studio 2015 Update 3 or newer -4. Intel C++ compiler 18 or newer - (`possible issue `_ on 20.2) -5. Cygwin/GCC (tested on 2.5.1) -6. NVCC (CUDA 11.0 tested) -7. NVIDIA PGI (20.7 and 20.9 tested) +4. Intel classic C++ compiler 18 or newer (ICC 20.2 tested in CI) +5. Cygwin/GCC (previously tested on 2.5.1) +6. NVCC (CUDA 11.0 tested in CI) +7. NVIDIA PGI (20.9 tested in CI) About ----- @@ -165,7 +173,7 @@ to the terms and conditions of this license. .. |Latest Documentation Status| image:: https://readthedocs.org/projects/pybind11/badge?version=latest :target: http://pybind11.readthedocs.org/en/latest -.. |Stable Documentation Status| image:: https://img.shields.io/badge/docs-stable-blue +.. |Stable Documentation Status| image:: https://img.shields.io/badge/docs-stable-blue.svg :target: http://pybind11.readthedocs.org/en/stable .. |Gitter chat| image:: https://img.shields.io/gitter/room/gitterHQ/gitter.svg :target: https://gitter.im/pybind/Lobby @@ -173,3 +181,11 @@ to the terms and conditions of this license. :target: https://github.com/pybind/pybind11/actions .. |Build status| image:: https://ci.appveyor.com/api/projects/status/riaj54pn4h08xy40?svg=true :target: https://ci.appveyor.com/project/wjakob/pybind11 +.. |PyPI package| image:: https://img.shields.io/pypi/v/pybind11.svg + :target: https://pypi.org/project/pybind11/ +.. |Conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pybind11.svg + :target: https://github.com/conda-forge/pybind11-feedstock +.. |Repology| image:: https://repology.org/badge/latest-versions/python:pybind11.svg + :target: https://repology.org/project/python:pybind11/versions +.. |Python Versions| image:: https://img.shields.io/pypi/pyversions/pybind11.svg + :target: https://pypi.org/project/pybind11/ diff --git a/pybind11/docs/advanced/cast/stl.rst b/pybind11/docs/advanced/cast/stl.rst index 70fde0d..b8622ee 100644 --- a/pybind11/docs/advanced/cast/stl.rst +++ b/pybind11/docs/advanced/cast/stl.rst @@ -72,6 +72,17 @@ The ``visit_helper`` specialization is not required if your ``name::variant`` pr a ``name::visit()`` function. For any other function name, the specialization must be included to tell pybind11 how to visit the variant. +.. warning:: + + When converting a ``variant`` type, pybind11 follows the same rules as when + determining which function overload to call (:ref:`overload_resolution`), and + so the same caveats hold. In particular, the order in which the ``variant``'s + alternatives are listed is important, since pybind11 will try conversions in + this order. This means that, for example, when converting ``variant``, + the ``bool`` variant will never be selected, as any Python ``bool`` is already + an ``int`` and is convertible to a C++ ``int``. Changing the order of alternatives + (and using ``variant``, in this example) provides a solution. + .. note:: pybind11 only supports the modern implementation of ``boost::variant`` diff --git a/pybind11/docs/advanced/exceptions.rst b/pybind11/docs/advanced/exceptions.rst index 5eae556..7a4d6cb 100644 --- a/pybind11/docs/advanced/exceptions.rst +++ b/pybind11/docs/advanced/exceptions.rst @@ -43,15 +43,23 @@ at its exception handler. | | of bounds access in ``__getitem__``, | | | ``__setitem__``, etc.) | +--------------------------------------+--------------------------------------+ -| :class:`pybind11::value_error` | ``ValueError`` (used to indicate | -| | wrong value passed in | -| | ``container.remove(...)``) | -+--------------------------------------+--------------------------------------+ | :class:`pybind11::key_error` | ``KeyError`` (used to indicate out | | | of bounds access in ``__getitem__``, | | | ``__setitem__`` in dict-like | | | objects, etc.) | +--------------------------------------+--------------------------------------+ +| :class:`pybind11::value_error` | ``ValueError`` (used to indicate | +| | wrong value passed in | +| | ``container.remove(...)``) | ++--------------------------------------+--------------------------------------+ +| :class:`pybind11::type_error` | ``TypeError`` | ++--------------------------------------+--------------------------------------+ +| :class:`pybind11::buffer_error` | ``BufferError`` | ++--------------------------------------+--------------------------------------+ +| :class:`pybind11::import_error` | ``import_error`` | ++--------------------------------------+--------------------------------------+ +| Any other exception | ``RuntimeError`` | ++--------------------------------------+--------------------------------------+ Exception translation is not bidirectional. That is, *catching* the C++ exceptions defined above above will not trap exceptions that originate from @@ -188,7 +196,7 @@ For example: } catch (py::error_already_set &e) { if (e.matches(PyExc_FileNotFoundError)) { py::print("missing.txt not found"); - } else if (e.match(PyExc_PermissionError)) { + } else if (e.matches(PyExc_PermissionError)) { py::print("missing.txt found but not accessible"); } else { throw; diff --git a/pybind11/docs/advanced/functions.rst b/pybind11/docs/advanced/functions.rst index ebdff9c..55a40a5 100644 --- a/pybind11/docs/advanced/functions.rst +++ b/pybind11/docs/advanced/functions.rst @@ -524,6 +524,8 @@ The default behaviour when the tag is unspecified is to allow ``None``. not allow ``None`` as argument. To pass optional argument of these copied types consider using ``std::optional`` +.. _overload_resolution: + Overload resolution order ========================= diff --git a/pybind11/docs/advanced/pycpp/numpy.rst b/pybind11/docs/advanced/pycpp/numpy.rst index 19ed10b..0a81aa8 100644 --- a/pybind11/docs/advanced/pycpp/numpy.rst +++ b/pybind11/docs/advanced/pycpp/numpy.rst @@ -150,8 +150,10 @@ NumPy array containing double precision values. When it is invoked with a different type (e.g. an integer or a list of integers), the binding code will attempt to cast the input into a NumPy array -of the requested type. Note that this feature requires the -:file:`pybind11/numpy.h` header to be included. +of the requested type. This feature requires the :file:`pybind11/numpy.h` +header to be included. Note that :file:`pybind11/numpy.h` does not depend on +the NumPy headers, and thus can be used without declaring a build-time +dependency on NumPy; NumPy>=1.7.0 is a runtime dependency. Data in NumPy arrays is not guaranteed to packed in a dense manner; furthermore, entries can be separated by arbitrary column and row strides. diff --git a/pybind11/docs/basics.rst b/pybind11/docs/basics.rst index b9d386c..0b1d85c 100644 --- a/pybind11/docs/basics.rst +++ b/pybind11/docs/basics.rst @@ -136,7 +136,14 @@ On Linux, the above example can be compiled using the following command: .. code-block:: bash - $ c++ -O3 -Wall -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix` + $ c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix) + +.. note:: + + If you used :ref:`include_as_a_submodule` to get the pybind11 source, then + use ``$(python3-config --includes) -Iextern/pybind11/include`` instead of + ``$(python3 -m pybind11 --includes)`` in the above compilation, as + explained in :ref:`building_manually`. For more details on the required compiler flags on Linux and macOS, see :ref:`building_manually`. For complete cross-platform compilation instructions, diff --git a/pybind11/docs/changelog.rst b/pybind11/docs/changelog.rst index 561baa5..157b296 100644 --- a/pybind11/docs/changelog.rst +++ b/pybind11/docs/changelog.rst @@ -6,6 +6,127 @@ Changelog Starting with version 1.8.0, pybind11 releases use a `semantic versioning `_ policy. + +v2.6.2 (Jan 26, 2021) +--------------------- + +Minor missing functionality added: + +* enum: add missing Enum.value property. + `#2739 `_ + +* Allow thread termination to be avoided during shutdown for CPython 3.7+ via + ``.disarm`` for ``gil_scoped_acquire``/``gil_scoped_release``. + `#2657 `_ + +Fixed or improved behavior in a few special cases: + +* Fix bug where the constructor of ``object`` subclasses would not throw on + being passed a Python object of the wrong type. + `#2701 `_ + +* The ``type_caster`` for integers does not convert Python objects with + ``__int__`` anymore with ``noconvert`` or during the first round of trying + overloads. + `#2698 `_ + +* When casting to a C++ integer, ``__index__`` is always called and not + considered as conversion, consistent with Python 3.8+. + `#2801 `_ + +Build improvements: + +* Setup helpers: ``extra_compile_args`` and ``extra_link_args`` automatically set by + Pybind11Extension are now prepended, which allows them to be overridden + by user-set ``extra_compile_args`` and ``extra_link_args``. + `#2808 `_ + +* Setup helpers: Don't trigger unused parameter warning. + `#2735 `_ + +* CMake: Support running with ``--warn-uninitialized`` active. + `#2806 `_ + +* CMake: Avoid error if included from two submodule directories. + `#2804 `_ + +* CMake: Fix ``STATIC`` / ``SHARED`` being ignored in FindPython mode. + `#2796 `_ + +* CMake: Respect the setting for ``CMAKE_CXX_VISIBILITY_PRESET`` if defined. + `#2793 `_ + +* CMake: Fix issue with FindPython2/FindPython3 not working with ``pybind11::embed``. + `#2662 `_ + +* CMake: mixing local and installed pybind11's would prioritize the installed + one over the local one (regression in 2.6.0). + `#2716 `_ + + +Bug fixes: + +* Fixed segfault in multithreaded environments when using + ``scoped_ostream_redirect``. + `#2675 `_ + +* Leave docstring unset when all docstring-related options are disabled, rather + than set an empty string. + `#2745 `_ + +* The module key in builtins that pybind11 uses to store its internals changed + from std::string to a python str type (more natural on Python 2, no change on + Python 3). + `#2814 `_ + +* Fixed assertion error related to unhandled (later overwritten) exception in + CPython 3.8 and 3.9 debug builds. + `#2685 `_ + +* Fix ``py::gil_scoped_acquire`` assert with CPython 3.9 debug build. + `#2683 `_ + +* Fix issue with a test failing on PyTest 6.2. + `#2741 `_ + +Warning fixes: + +* Fix warning modifying constructor parameter 'flag' that shadows a field of + 'set_flag' ``[-Wshadow-field-in-constructor-modified]``. + `#2780 `_ + +* Suppressed some deprecation warnings about old-style + ``__init__``/``__setstate__`` in the tests. + `#2759 `_ + +Valgrind work: + +* Fix invalid access when calling a pybind11 ``__init__`` on a non-pybind11 + class instance. + `#2755 `_ + +* Fixed various minor memory leaks in pybind11's test suite. + `#2758 `_ + +* Resolved memory leak in cpp_function initialization when exceptions occurred. + `#2756 `_ + +* Added a Valgrind build, checking for leaks and memory-related UB, to CI. + `#2746 `_ + +Compiler support: + +* Intel compiler was not activating C++14 support due to a broken define. + `#2679 `_ + +* Support ICC and NVIDIA HPC SDK in C++17 mode. + `#2729 `_ + +* Support Intel OneAPI compiler (ICC 20.2) and add to CI. + `#2573 `_ + + + v2.6.1 (Nov 11, 2020) --------------------- @@ -14,7 +135,7 @@ v2.6.1 (Nov 11, 2020) and ``eval`` in pure Python. `#2616 `_ -* ``setup_helpers`` will no longer set a minimum macOS version lower than the +* ``setup_helpers`` will no longer set a minimum macOS version higher than the current version. `#2622 `_ diff --git a/pybind11/docs/compiling.rst b/pybind11/docs/compiling.rst index f26e6cf..3a8a270 100644 --- a/pybind11/docs/compiling.rst +++ b/pybind11/docs/compiling.rst @@ -84,10 +84,11 @@ for NumPy's parallel compilation distutils tool is included. Use it like this: The argument is the name of an environment variable to control the number of threads, such as ``NPY_NUM_BUILD_JOBS`` (as used by NumPy), though you can set -something different if you want. You can also pass ``default=N`` to set the -default number of threads (0 will take the number of threads available) and -``max=N``, the maximum number of threads; if you have a large extension you may -want set this to a memory dependent number. +something different if you want; ``CMAKE_BUILD_PARALLEL_LEVEL`` is another choice +a user might expect. You can also pass ``default=N`` to set the default number +of threads (0 will take the number of threads available) and ``max=N``, the +maximum number of threads; if you have a large extension you may want set this +to a memory dependent number. If you are developing rapidly and have a lot of C++ files, you may want to avoid rebuilding files that have not changed. For simple cases were you are @@ -136,7 +137,7 @@ Your ``pyproject.toml`` file will likely look something like this: .. code-block:: toml [build-system] - requires = ["setuptools", "wheel", "pybind11==2.6.0"] + requires = ["setuptools>=42", "wheel", "pybind11~=2.6.1"] build-backend = "setuptools.build_meta" .. note:: @@ -147,10 +148,12 @@ Your ``pyproject.toml`` file will likely look something like this: in Python) using something like `cibuildwheel`_, remember that ``setup.py`` and ``pyproject.toml`` are not even contained in the wheel, so this high Pip requirement is only for source builds, and will not affect users of - your binary wheels. + your binary wheels. If you are building SDists and wheels, then + `pypa-build`_ is the recommended offical tool. .. _PEP 517: https://www.python.org/dev/peps/pep-0517/ .. _cibuildwheel: https://cibuildwheel.readthedocs.io +.. _pypa-build: https://pypa-build.readthedocs.io/en/latest/ .. _setup_helpers-setup_requires: @@ -401,13 +404,14 @@ can refer to the same [cmake_example]_ repository for a full sample project FindPython mode --------------- -CMake 3.12+ (3.15+ recommended) 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: +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: .. code-block:: cmake - cmake_minumum_required(VERSION 3.15...3.18) + cmake_minumum_required(VERSION 3.15...3.19) project(example LANGUAGES CXX) find_package(Python COMPONENTS Interpreter Development REQUIRED) @@ -433,6 +437,14 @@ setting ``Python_ROOT_DIR`` may be the most common one (though with virtualenv/venv support, and Conda support, this tends to find the correct Python version more often than the old system did). +.. warning:: + + When the Python libraries (i.e. ``libpythonXX.a`` and ``libpythonXX.so`` + on Unix) are not available, as is the case on a manylinux image, the + ``Development`` component will not be resolved by ``FindPython``. When not + using the embedding functionality, CMake 3.18+ allows you to specify + ``Development.Module`` instead of ``Development`` to resolve this issue. + .. versionadded:: 2.6 Advanced: interface library targets @@ -557,7 +569,7 @@ On Linux, you can compile an example such as the one given in .. code-block:: bash - $ c++ -O3 -Wall -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix` + $ c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix) The flags given here assume that you're using Python 3. For Python 2, just change the executable appropriately (to ``python`` or ``python2``). @@ -569,7 +581,7 @@ using ``pip`` or ``conda``. If it hasn't, you can also manually specify ``python3-config --includes``. Note that Python 2.7 modules don't use a special suffix, so you should simply -use ``example.so`` instead of ``example`python3-config --extension-suffix```. +use ``example.so`` instead of ``example$(python3-config --extension-suffix)``. Besides, the ``--extension-suffix`` option may or may not be available, depending on the distribution; in the latter case, the module extension can be manually set to ``.so``. @@ -580,7 +592,7 @@ building the module: .. code-block:: bash - $ c++ -O3 -Wall -shared -std=c++11 -undefined dynamic_lookup `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix` + $ c++ -O3 -Wall -shared -std=c++11 -undefined dynamic_lookup $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix) In general, it is advisable to include several additional build parameters that can considerably reduce the size of the created binary. Refer to section diff --git a/pybind11/docs/conf.py b/pybind11/docs/conf.py index 66db310..6ac054c 100644 --- a/pybind11/docs/conf.py +++ b/pybind11/docs/conf.py @@ -239,6 +239,8 @@ # -- Options for LaTeX output --------------------------------------------- +latex_engine = "pdflatex" + latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', @@ -353,8 +355,11 @@ def prepare(app): with open(DIR.parent / "README.rst") as f: contents = f.read() - # Filter out section titles for index.rst for LaTeX if app.builder.name == "latex": + # Remove badges and stuff from start + contents = contents[contents.find(r".. start") :] + + # Filter out section titles for index.rst for LaTeX contents = re.sub(r"^(.*)\n[-~]{3,}$", r"**\1**", contents, flags=re.MULTILINE) with open(DIR / "readme.rst", "w") as f: diff --git a/pybind11/docs/installing.rst b/pybind11/docs/installing.rst index 2597285..30b9f18 100644 --- a/pybind11/docs/installing.rst +++ b/pybind11/docs/installing.rst @@ -8,6 +8,8 @@ There are several ways to get the pybind11 source, which lives at developers recommend one of the first three ways listed here, submodule, PyPI, or conda-forge, for obtaining pybind11. +.. _include_as_a_submodule: + Include as a submodule ====================== @@ -16,7 +18,7 @@ as a submodule. From your git repository, use: .. code-block:: bash - git submodule add ../../pybind/pybind11 extern/pybind11 -b stable + git submodule add -b stable ../../pybind/pybind11 extern/pybind11 git submodule update --init This assumes you are placing your dependencies in ``extern/``, and that you are diff --git a/pybind11/docs/release.rst b/pybind11/docs/release.rst index 43f502a..9dbff03 100644 --- a/pybind11/docs/release.rst +++ b/pybind11/docs/release.rst @@ -25,12 +25,17 @@ To release a new version of pybind11: - Update ``PYBIND11_VERSION_MAJOR`` etc. in ``include/pybind11/detail/common.h``. PATCH should be a simple integer. - Update ``pybind11/_version.py`` (match above) - - Ensure that all the information in ``setup.py`` is up-to-date. + - Ensure that all the information in ``setup.cfg`` is up-to-date, like + supported Python versions. - Add release date in ``docs/changelog.rst``. + - Check to make sure + `needs-changelog `_ + issues are entered in the changelog (clear the label when done). - ``git add`` and ``git commit``, ``git push``. **Ensure CI passes**. (If it fails due to a known flake issue, either ignore or restart CI.) -- Add a release branch if this is a new minor version - - ``git checkout -b vX.Y``, ``git push -u origin vX.Y`` +- Add a release branch if this is a new minor version, or update the existing release branch if it is a patch version + - New branch: ``git checkout -b vX.Y``, ``git push -u origin vX.Y`` + - Update branch: ``git checkout vX.Y``, ``git merge ``, ``git push`` - Update tags (optional; if you skip this, the GitHub release makes a non-annotated tag for you) - ``git tag -a vX.Y.Z -m 'vX.Y.Z release'``. @@ -47,7 +52,8 @@ To release a new version of pybind11: name (if you didn't tag above, it will be made here), fill in a release name like "Version X.Y.Z", and optionally copy-and-paste the changelog into the description (processed as markdown by Pandoc). Check "pre-release" if - this is a beta/RC. + this is a beta/RC. You can get partway there with + ``cat docs/changelog.rst | pandsoc -f rst -t markdown``. - CLI method: with ``gh`` installed, run ``gh release create vX.Y.Z -t "Version X.Y.Z"`` If this is a pre-release, add ``-p``. @@ -56,11 +62,20 @@ To release a new version of pybind11: - Update version macros in ``include/pybind11/detail/common.h`` (set PATCH to ``0.dev1`` and increment MINOR). - Update ``_version.py`` to match - - Add a plot for in-development updates in ``docs/changelog.rst``. + - Add a spot for in-development updates in ``docs/changelog.rst``. - ``git add``, ``git commit``, ``git push`` If a version branch is updated, remember to set PATCH to ``1.dev1``. +If you'd like to bump homebrew, run: + +.. code-block:: + + brew bump-formula-pr --url https://github.com/pybind/pybind11/archive/vX.Y.Z.tar.gz + +Conda-forge should automatically make a PR in a few hours, and automatically +merge it if there are no issues. + Manual packaging ^^^^^^^^^^^^^^^^ diff --git a/pybind11/docs/requirements.txt b/pybind11/docs/requirements.txt index 35366e3..9e6a3f6 100644 --- a/pybind11/docs/requirements.txt +++ b/pybind11/docs/requirements.txt @@ -1,7 +1,7 @@ -breathe==4.20.0 +breathe==4.25.1 commonmark==0.9.1 -recommonmark==0.6.0 -sphinx==3.2.1 +recommonmark==0.7.1 +sphinx==3.3.1 sphinx_rtd_theme==0.5.0 -sphinxcontrib-moderncmakedomain==3.13 +sphinxcontrib-moderncmakedomain==3.17 sphinxcontrib-svg2pdfconverter==1.1.0 diff --git a/pybind11/docs/upgrade.rst b/pybind11/docs/upgrade.rst index 87bcebe..f13fbcf 100644 --- a/pybind11/docs/upgrade.rst +++ b/pybind11/docs/upgrade.rst @@ -192,7 +192,7 @@ way to get and set object state. See :ref:`pickling` for details. ... .def(py::pickle( [](const Foo &self) { // __getstate__ - return py::make_tuple(f.value1(), f.value2(), ...); // unchanged + return py::make_tuple(self.value1(), self.value2(), ...); // unchanged }, [](py::tuple t) { // __setstate__, note: no `self` argument return new Foo(t[0].cast(), ...); diff --git a/pybind11/include/pybind11/attr.h b/pybind11/include/pybind11/attr.h index 0c41670..50efdc7 100644 --- a/pybind11/include/pybind11/attr.h +++ b/pybind11/include/pybind11/attr.h @@ -544,7 +544,7 @@ template ::value...), size_t self = constexpr_sum(std::is_same::value...)> constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { - return named == 0 || (self + named + has_args + has_kwargs) == nargs; + return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs; } PYBIND11_NAMESPACE_END(detail) diff --git a/pybind11/include/pybind11/cast.h b/pybind11/include/pybind11/cast.h index 11c61a4..0caccdb 100644 --- a/pybind11/include/pybind11/cast.h +++ b/pybind11/include/pybind11/cast.h @@ -960,9 +960,14 @@ template class type_caster> { private: using caster_t = make_caster; caster_t subcaster; - using subcaster_cast_op_type = typename caster_t::template cast_op_type; - static_assert(std::is_same::type &, subcaster_cast_op_type>::value, - "std::reference_wrapper caster requires T to have a caster with an `T &` operator"); + using reference_t = type&; + using subcaster_cast_op_type = + typename caster_t::template cast_op_type; + + static_assert(std::is_same::type &, subcaster_cast_op_type>::value || + std::is_same::value, + "std::reference_wrapper caster requires T to have a caster with an " + "`operator T &()` or `operator const T &()`"); public: bool load(handle src, bool convert) { return subcaster.load(src, convert); } static constexpr auto name = caster_t::name; @@ -973,7 +978,7 @@ template class type_caster> { return caster_t::cast(&src.get(), policy, parent); } template using cast_op_type = std::reference_wrapper; - operator std::reference_wrapper() { return subcaster.operator subcaster_cast_op_type&(); } + operator std::reference_wrapper() { return cast_op(subcaster); } }; #define PYBIND11_TYPE_CASTER(type, py_name) \ @@ -1020,6 +1025,14 @@ struct type_caster::value && !is_std_char_t if (!src) return false; +#if !defined(PYPY_VERSION) + auto index_check = [](PyObject *o) { return PyIndex_Check(o); }; +#else + // In PyPy 7.3.3, `PyIndex_Check` is implemented by calling `__index__`, + // while CPython only considers the existence of `nb_index`/`__index__`. + auto index_check = [](PyObject *o) { return hasattr(o, "__index__"); }; +#endif + if (std::is_floating_point::value) { if (convert || PyFloat_Check(src.ptr())) py_value = (py_type) PyFloat_AsDouble(src.ptr()); @@ -1027,12 +1040,31 @@ struct type_caster::value && !is_std_char_t return false; } else if (PyFloat_Check(src.ptr())) { return false; - } else if (std::is_unsigned::value) { - py_value = as_unsigned(src.ptr()); - } else { // signed integer: - py_value = sizeof(T) <= sizeof(long) - ? (py_type) PyLong_AsLong(src.ptr()) - : (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr()); + } else if (!convert && !PYBIND11_LONG_CHECK(src.ptr()) && !index_check(src.ptr())) { + return false; + } else { + handle src_or_index = src; +#if PY_VERSION_HEX < 0x03080000 + object index; + if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: index_check(src.ptr()) + index = reinterpret_steal(PyNumber_Index(src.ptr())); + if (!index) { + PyErr_Clear(); + if (!convert) + return false; + } + else { + src_or_index = index; + } + } +#endif + if (std::is_unsigned::value) { + py_value = as_unsigned(src_or_index.ptr()); + } else { // signed integer: + py_value = sizeof(T) <= sizeof(long) + ? (py_type) PyLong_AsLong(src_or_index.ptr()) + : (py_type) PYBIND11_LONG_AS_LONGLONG(src_or_index.ptr()); + } } // Python API reported an error @@ -1041,15 +1073,8 @@ struct type_caster::value && !is_std_char_t // Check to see if the conversion is valid (integers should match exactly) // Signed/unsigned checks happen elsewhere if (py_err || (std::is_integral::value && sizeof(py_type) != sizeof(T) && py_value != (py_type) (T) py_value)) { - bool type_error = py_err && PyErr_ExceptionMatches( -#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION) - PyExc_SystemError -#else - PyExc_TypeError -#endif - ); PyErr_Clear(); - if (type_error && convert && PyNumber_Check(src.ptr())) { + if (py_err && convert && PyNumber_Check(src.ptr())) { auto tmp = reinterpret_steal(std::is_floating_point::value ? PyNumber_Float(src.ptr()) : PyNumber_Long(src.ptr())); @@ -1870,7 +1895,14 @@ struct arg_v : arg { #if !defined(NDEBUG) , type(type_id()) #endif - { } + { + // Workaround! See: + // https://github.com/pybind/pybind11/issues/2336 + // https://github.com/pybind/pybind11/pull/2685#issuecomment-731286700 + if (PyErr_Occurred()) { + PyErr_Clear(); + } + } public: /// Direct construction with name, default, and description @@ -2160,16 +2192,26 @@ class unpacking_collector { dict m_kwargs; }; +// [workaround(intel)] Separate function required here +// We need to put this into a separate function because the Intel compiler +// fails to compile enable_if_t...>::value> +// (tested with ICC 2021.1 Beta 20200827). +template +constexpr bool args_are_all_positional() +{ + return all_of...>::value; +} + /// Collect only positional arguments for a Python function call template ...>::value>> + typename = enable_if_t()>> simple_collector collect_arguments(Args &&...args) { return simple_collector(std::forward(args)...); } /// Collect all arguments, including keywords and unpacking (only instantiated when needed) template ...>::value>> + typename = enable_if_t()>> unpacking_collector collect_arguments(Args &&...args) { // Following argument order rules for generalized unpacking according to PEP 448 static_assert( diff --git a/pybind11/include/pybind11/detail/class.h b/pybind11/include/pybind11/detail/class.h index 65dad5a..2f414e5 100644 --- a/pybind11/include/pybind11/detail/class.h +++ b/pybind11/include/pybind11/detail/class.h @@ -340,8 +340,6 @@ inline PyObject *make_new_instance(PyTypeObject *type) { // Allocate the value/holder internals: inst->allocate_layout(); - inst->owned = true; - return self; } @@ -552,6 +550,12 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla } std::memset(view, 0, sizeof(Py_buffer)); buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data); + if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) { + delete info; + // view->obj = nullptr; // Was just memset to 0, so not necessary + PyErr_SetString(PyExc_BufferError, "Writable buffer requested for readonly storage"); + return -1; + } view->obj = obj; view->ndim = 1; view->internal = info; @@ -561,12 +565,6 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla for (auto s : info->shape) view->len *= s; view->readonly = info->readonly; - if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) { - if (view) - view->obj = nullptr; - PyErr_SetString(PyExc_BufferError, "Writable buffer requested for readonly storage"); - return -1; - } if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) view->format = const_cast(info->format.c_str()); if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { diff --git a/pybind11/include/pybind11/detail/common.h b/pybind11/include/pybind11/detail/common.h index 751141a..de495e4 100644 --- a/pybind11/include/pybind11/detail/common.h +++ b/pybind11/include/pybind11/detail/common.h @@ -11,7 +11,7 @@ #define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MINOR 6 -#define PYBIND11_VERSION_PATCH 1 +#define PYBIND11_VERSION_PATCH 2 #define PYBIND11_NAMESPACE_BEGIN(name) namespace name { #define PYBIND11_NAMESPACE_END(name) } @@ -27,7 +27,7 @@ # endif #endif -#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER) +#if !(defined(_MSC_VER) && __cplusplus == 199711L) # if __cplusplus >= 201402L # define PYBIND11_CPP14 # if __cplusplus >= 201703L @@ -49,6 +49,8 @@ #if defined(__INTEL_COMPILER) # if __INTEL_COMPILER < 1800 # error pybind11 requires Intel C++ compiler v18 or newer +# elif __INTEL_COMPILER < 1900 && defined(PYBIND11_CPP14) +# error pybind11 supports only C++11 with Intel C++ compiler v18. Use v19 or newer for C++14. # endif #elif defined(__clang__) && !defined(__apple_build_version__) # if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3) @@ -663,6 +665,10 @@ template using is_function_pointer = bool_constant< std::is_pointer::value && std::is_function::type>::value>; template struct strip_function_object { + // If you are encountering an + // 'error: name followed by "::" must be a class or namespace name' + // with the Intel compiler and a noexcept function here, + // try to use noexcept(true) instead of plain noexcept. using type = typename remove_class::type; }; @@ -687,8 +693,10 @@ template using is_lambda = satisfies_none_of, /// Ignore that a variable is unused in compiler warnings inline void ignore_unused(const int *) { } +// [workaround(intel)] Internal error on fold expression /// Apply a function over each element of a parameter pack -#ifdef __cpp_fold_expressions +#if defined(__cpp_fold_expressions) && !defined(__INTEL_COMPILER) +// Intel compiler produces an internal error on this fold expression (tested with ICC 19.0.2) #define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (((PATTERN), void()), ...) #else using expand_side_effects = bool[]; diff --git a/pybind11/include/pybind11/detail/internals.h b/pybind11/include/pybind11/detail/internals.h index a455715..75fcd3c 100644 --- a/pybind11/include/pybind11/detail/internals.h +++ b/pybind11/include/pybind11/detail/internals.h @@ -112,7 +112,7 @@ struct internals { PyInterpreterState *istate = nullptr; ~internals() { // This destructor is called *after* Py_Finalize() in finalize_interpreter(). - // That *SHOULD BE* fine. The following details what happens whe PyThread_tss_free is called. + // That *SHOULD BE* fine. The following details what happens when PyThread_tss_free is called. // PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does nothing. // PyThread_tss_free calls PyThread_tss_delete and PyMem_RawFree. // PyThread_tss_delete just calls TlsFree (on Windows) or pthread_key_delete (on *NIX). Neither @@ -266,7 +266,7 @@ PYBIND11_NOINLINE inline internals &get_internals() { const PyGILState_STATE state; } gil; - constexpr auto *id = PYBIND11_INTERNALS_ID; + PYBIND11_STR_TYPE id(PYBIND11_INTERNALS_ID); auto builtins = handle(PyEval_GetBuiltins()); if (builtins.contains(id) && isinstance(builtins[id])) { internals_pp = static_cast(capsule(builtins[id])); diff --git a/pybind11/include/pybind11/iostream.h b/pybind11/include/pybind11/iostream.h index 5e9a814..9dee755 100644 --- a/pybind11/include/pybind11/iostream.h +++ b/pybind11/include/pybind11/iostream.h @@ -43,16 +43,20 @@ class pythonbuf : public std::streambuf { // simplified to a fully qualified call. int _sync() { if (pbase() != pptr()) { - // This subtraction cannot be negative, so dropping the sign - str line(pbase(), static_cast(pptr() - pbase())); { gil_scoped_acquire tmp; + + // This subtraction cannot be negative, so dropping the sign. + str line(pbase(), static_cast(pptr() - pbase())); + pywrite(line); pyflush(); + + // Placed inside gil_scoped_aquire as a mutex to avoid a race + setp(pbase(), epptr()); } - setp(pbase(), epptr()); } return 0; } @@ -102,8 +106,8 @@ PYBIND11_NAMESPACE_END(detail) .. code-block:: cpp { - py::scoped_ostream_redirect output{std::cerr, py::module_::import("sys").attr("stderr")}; - std::cerr << "Hello, World!"; + py::scoped_ostream_redirect output{std::cerr, py::module::import("sys").attr("stderr")}; + std::cout << "Hello, World!"; } \endrst */ class scoped_ostream_redirect { diff --git a/pybind11/include/pybind11/pybind11.h b/pybind11/include/pybind11/pybind11.h index e2ddda0..3bffbb2 100644 --- a/pybind11/include/pybind11/pybind11.h +++ b/pybind11/include/pybind11/pybind11.h @@ -114,9 +114,16 @@ class cpp_function : public function { object name() const { return attr("__name__"); } protected: + struct InitializingFunctionRecordDeleter { + // `destruct(function_record, false)`: `initialize_generic` copies strings and + // takes care of cleaning up in case of exceptions. So pass `false` to `free_strings`. + void operator()(detail::function_record * rec) { destruct(rec, false); } + }; + using unique_function_record = std::unique_ptr; + /// Space optimization: don't inline this frequently instantiated fragment - PYBIND11_NOINLINE detail::function_record *make_function_record() { - return new detail::function_record(); + PYBIND11_NOINLINE unique_function_record make_function_record() { + return unique_function_record(new detail::function_record()); } /// Special internal constructor for functors, lambda functions, etc. @@ -126,7 +133,9 @@ class cpp_function : public function { struct capture { remove_reference_t f; }; /* Store the function including any extra state it might have (e.g. a lambda capture object) */ - auto rec = make_function_record(); + // The unique_ptr makes sure nothing is leaked in case of an exception. + auto unique_rec = make_function_record(); + auto rec = unique_rec.get(); /* Store the capture object directly in the function record if there is enough space */ if (sizeof(capture) <= sizeof(rec->data)) { @@ -207,7 +216,8 @@ class cpp_function : public function { PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types(); /* Register the function with Python from generic (non-templated) code */ - initialize_generic(rec, signature.text, types.data(), sizeof...(Args)); + // Pass on the ownership over the `unique_rec` to `initialize_generic`. `rec` stays valid. + initialize_generic(std::move(unique_rec), signature.text, types.data(), sizeof...(Args)); if (cast_in::has_args) rec->has_args = true; if (cast_in::has_kwargs) rec->has_kwargs = true; @@ -223,20 +233,51 @@ class cpp_function : public function { } } + // Utility class that keeps track of all duplicated strings, and cleans them up in its destructor, + // unless they are released. Basically a RAII-solution to deal with exceptions along the way. + class strdup_guard { + public: + ~strdup_guard() { + for (auto s : strings) + std::free(s); + } + char *operator()(const char *s) { + auto t = strdup(s); + strings.push_back(t); + return t; + } + void release() { + strings.clear(); + } + private: + std::vector strings; + }; + /// Register a function call with Python (generic non-templated code goes here) - void initialize_generic(detail::function_record *rec, const char *text, + void initialize_generic(unique_function_record &&unique_rec, const char *text, const std::type_info *const *types, size_t args) { + // Do NOT receive `unique_rec` by value. If this function fails to move out the unique_ptr, + // we do not want this to destuct the pointer. `initialize` (the caller) still relies on the + // pointee being alive after this call. Only move out if a `capsule` is going to keep it alive. + auto rec = unique_rec.get(); + + // Keep track of strdup'ed strings, and clean them up as long as the function's capsule + // has not taken ownership yet (when `unique_rec.release()` is called). + // Note: This cannot easily be fixed by a `unique_ptr` with custom deleter, because the strings + // are only referenced before strdup'ing. So only *after* the following block could `destruct` + // safely be called, but even then, `repr` could still throw in the middle of copying all strings. + strdup_guard guarded_strdup; /* Create copies of all referenced C-style strings */ - rec->name = strdup(rec->name ? rec->name : ""); - if (rec->doc) rec->doc = strdup(rec->doc); + rec->name = guarded_strdup(rec->name ? rec->name : ""); + if (rec->doc) rec->doc = guarded_strdup(rec->doc); for (auto &a: rec->args) { if (a.name) - a.name = strdup(a.name); + a.name = guarded_strdup(a.name); if (a.descr) - a.descr = strdup(a.descr); + a.descr = guarded_strdup(a.descr); else if (a.value) - a.descr = strdup(repr(a.value).cast().c_str()); + a.descr = guarded_strdup(repr(a.value).cast().c_str()); } rec->is_constructor = !strcmp(rec->name, "__init__") || !strcmp(rec->name, "__setstate__"); @@ -319,13 +360,13 @@ class cpp_function : public function { #if PY_MAJOR_VERSION < 3 if (strcmp(rec->name, "__next__") == 0) { std::free(rec->name); - rec->name = strdup("next"); + rec->name = guarded_strdup("next"); } else if (strcmp(rec->name, "__bool__") == 0) { std::free(rec->name); - rec->name = strdup("__nonzero__"); + rec->name = guarded_strdup("__nonzero__"); } #endif - rec->signature = strdup(signature.c_str()); + rec->signature = guarded_strdup(signature.c_str()); rec->args.shrink_to_fit(); rec->nargs = (std::uint16_t) args; @@ -356,9 +397,10 @@ class cpp_function : public function { rec->def->ml_meth = reinterpret_cast(reinterpret_cast(*dispatcher)); rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS; - capsule rec_capsule(rec, [](void *ptr) { + capsule rec_capsule(unique_rec.release(), [](void *ptr) { destruct((detail::function_record *) ptr); }); + guarded_strdup.release(); object scope_module; if (rec->scope) { @@ -393,13 +435,15 @@ class cpp_function : public function { chain_start = rec; rec->next = chain; auto rec_capsule = reinterpret_borrow(((PyCFunctionObject *) m_ptr)->m_self); - rec_capsule.set_pointer(rec); + rec_capsule.set_pointer(unique_rec.release()); + guarded_strdup.release(); } else { // Or end of chain (normal behavior) chain_start = chain; while (chain->next) chain = chain->next; - chain->next = rec; + chain->next = unique_rec.release(); + guarded_strdup.release(); } } @@ -439,9 +483,9 @@ class cpp_function : public function { /* Install docstring */ auto *func = (PyCFunctionObject *) m_ptr; - if (func->m_ml->ml_doc) - std::free(const_cast(func->m_ml->ml_doc)); - func->m_ml->ml_doc = strdup(signatures.c_str()); + std::free(const_cast(func->m_ml->ml_doc)); + // Install docstring if it's non-empty (when at least one option is enabled) + func->m_ml->ml_doc = signatures.empty() ? nullptr : strdup(signatures.c_str()); if (rec->is_method) { m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr()); @@ -452,7 +496,7 @@ class cpp_function : public function { } /// When a cpp_function is GCed, release any memory allocated by pybind11 - static void destruct(detail::function_record *rec) { + static void destruct(detail::function_record *rec, bool free_strings = true) { // If on Python 3.9, check the interpreter "MICRO" (patch) version. // If this is running on 3.9.0, we have to work around a bug. #if !defined(PYPY_VERSION) && PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION == 9 @@ -463,14 +507,20 @@ class cpp_function : public function { detail::function_record *next = rec->next; if (rec->free_data) rec->free_data(rec); - std::free((char *) rec->name); - std::free((char *) rec->doc); - std::free((char *) rec->signature); - for (auto &arg: rec->args) { - std::free(const_cast(arg.name)); - std::free(const_cast(arg.descr)); - arg.value.dec_ref(); + // During initialization, these strings might not have been copied yet, + // so they cannot be freed. Once the function has been created, they can. + // Check `make_function_record` for more details. + if (free_strings) { + std::free((char *) rec->name); + std::free((char *) rec->doc); + std::free((char *) rec->signature); + for (auto &arg: rec->args) { + std::free(const_cast(arg.name)); + std::free(const_cast(arg.descr)); + } } + for (auto &arg: rec->args) + arg.value.dec_ref(); if (rec->def) { std::free(const_cast(rec->def->ml_doc)); // Python 3.9.0 decref's these in the wrong order; rec->def @@ -504,15 +554,15 @@ class cpp_function : public function { auto self_value_and_holder = value_and_holder(); if (overloads->is_constructor) { - const auto tinfo = get_type_info((PyTypeObject *) overloads->scope.ptr()); - const auto pi = reinterpret_cast(parent.ptr()); - self_value_and_holder = pi->get_value_and_holder(tinfo, false); - - if (!self_value_and_holder.type || !self_value_and_holder.inst) { + if (!PyObject_TypeCheck(parent.ptr(), (PyTypeObject *) overloads->scope.ptr())) { PyErr_SetString(PyExc_TypeError, "__init__(self, ...) called with invalid `self` argument"); return nullptr; } + const auto tinfo = get_type_info((PyTypeObject *) overloads->scope.ptr()); + const auto pi = reinterpret_cast(parent.ptr()); + self_value_and_holder = pi->get_value_and_holder(tinfo, true); + // If this value is already registered it must mean __init__ is invoked multiple times; // we really can't support that in C++, so just ignore the second __init__. if (self_value_and_holder.instance_registered()) @@ -977,7 +1027,7 @@ class module_ : public object { /** \rst Create a new top-level module that can be used as the main module of a C extension. - For Python 3, ``def`` should point to a staticly allocated module_def. + For Python 3, ``def`` should point to a statically allocated module_def. For Python 2, ``def`` can be a nullptr and is completely ignored. \endrst */ static module_ create_extension_module(const char *name, const char *doc, module_def *def) { @@ -1005,7 +1055,7 @@ class module_ : public object { throw error_already_set(); pybind11_fail("Internal error in module_::create_extension_module()"); } - // TODO: Sould be reinterpret_steal for Python 3, but Python also steals it again when returned from PyInit_... + // TODO: Should be reinterpret_steal for Python 3, but Python also steals it again when returned from PyInit_... // For Python 2, reinterpret_borrow is correct. return reinterpret_borrow(m); } @@ -1731,6 +1781,7 @@ template class enum_ : public class_ { m_base.init(is_arithmetic, is_convertible); def(init([](Scalar i) { return static_cast(i); }), arg("value")); + def_property_readonly("value", [](Type value) { return (Scalar) value; }); def("__int__", [](Type value) { return (Scalar) value; }); #if PY_MAJOR_VERSION < 3 def("__long__", [](Type value) { return (Scalar) value; }); @@ -1909,7 +1960,7 @@ template void implicitly_convertible() { struct set_flag { bool &flag; - set_flag(bool &flag) : flag(flag) { flag = true; } + set_flag(bool &flag_) : flag(flag_) { flag_ = true; } ~set_flag() { flag = false; } }; auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * { @@ -2089,15 +2140,7 @@ class gil_scoped_acquire { } if (release) { - /* Work around an annoying assertion in PyThreadState_Swap */ - #if defined(Py_DEBUG) - PyInterpreterState *interp = tstate->interp; - tstate->interp = nullptr; - #endif PyEval_AcquireThread(tstate); - #if defined(Py_DEBUG) - tstate->interp = interp; - #endif } inc_ref(); @@ -2121,12 +2164,22 @@ class gil_scoped_acquire { pybind11_fail("scoped_acquire::dec_ref(): internal error!"); #endif PyThreadState_Clear(tstate); - PyThreadState_DeleteCurrent(); + if (active) + PyThreadState_DeleteCurrent(); PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate); release = false; } } + /// This method will disable the PyThreadState_DeleteCurrent call and the + /// GIL won't be acquired. This method should be used if the interpreter + /// could be shutting down when this is called, as thread deletion is not + /// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and + /// protect subsequent code. + PYBIND11_NOINLINE void disarm() { + active = false; + } + PYBIND11_NOINLINE ~gil_scoped_acquire() { dec_ref(); if (release) @@ -2135,6 +2188,7 @@ class gil_scoped_acquire { private: PyThreadState *tstate = nullptr; bool release = true; + bool active = true; }; class gil_scoped_release { @@ -2150,10 +2204,22 @@ class gil_scoped_release { PYBIND11_TLS_DELETE_VALUE(key); } } + + /// This method will disable the PyThreadState_DeleteCurrent call and the + /// GIL won't be acquired. This method should be used if the interpreter + /// could be shutting down when this is called, as thread deletion is not + /// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and + /// protect subsequent code. + PYBIND11_NOINLINE void disarm() { + active = false; + } + ~gil_scoped_release() { if (!tstate) return; - PyEval_RestoreThread(tstate); + // `PyEval_RestoreThread()` should not be called if runtime is finalizing + if (active) + PyEval_RestoreThread(tstate); if (disassoc) { auto key = detail::get_internals().tstate; PYBIND11_TLS_REPLACE_VALUE(key, tstate); @@ -2162,6 +2228,7 @@ class gil_scoped_release { private: PyThreadState *tstate; bool disassoc; + bool active = true; }; #elif defined(PYPY_VERSION) class gil_scoped_acquire { @@ -2169,6 +2236,7 @@ class gil_scoped_acquire { public: gil_scoped_acquire() { state = PyGILState_Ensure(); } ~gil_scoped_acquire() { PyGILState_Release(state); } + void disarm() {} }; class gil_scoped_release { @@ -2176,10 +2244,15 @@ class gil_scoped_release { public: gil_scoped_release() { state = PyEval_SaveThread(); } ~gil_scoped_release() { PyEval_RestoreThread(state); } + void disarm() {} }; #else -class gil_scoped_acquire { }; -class gil_scoped_release { }; +class gil_scoped_acquire { + void disarm() {} +}; +class gil_scoped_release { + void disarm() {} +}; #endif error_already_set::~error_already_set() { @@ -2254,7 +2327,7 @@ PYBIND11_NAMESPACE_END(detail) /** \rst Try to retrieve a python method by the provided name from the instance pointed to by the this_ptr. - :this_ptr: The pointer to the object the overriden method should be retrieved for. This should be + :this_ptr: The pointer to the object the overridden method should be retrieved for. This should be the first non-trampoline class encountered in the inheritance chain. :name: The name of the overridden Python method to retrieve. :return: The Python method by this name from the object or an empty function wrapper. diff --git a/pybind11/include/pybind11/pytypes.h b/pybind11/include/pybind11/pytypes.h index 1010ad7..78db794 100644 --- a/pybind11/include/pybind11/pytypes.h +++ b/pybind11/include/pybind11/pytypes.h @@ -812,18 +812,18 @@ PYBIND11_NAMESPACE_END(detail) : Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) \ { if (!m_ptr) throw error_already_set(); } -#define PYBIND11_OBJECT_CHECK_FAILED(Name, o) \ +#define PYBIND11_OBJECT_CHECK_FAILED(Name, o_ptr) \ ::pybind11::type_error("Object of type '" + \ - ::pybind11::detail::get_fully_qualified_tp_name(Py_TYPE(o.ptr())) + \ + ::pybind11::detail::get_fully_qualified_tp_name(Py_TYPE(o_ptr)) + \ "' is not an instance of '" #Name "'") #define PYBIND11_OBJECT(Name, Parent, CheckFun) \ PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ Name(const object &o) : Parent(o) \ - { if (o && !check_(o)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, o); } \ + { if (m_ptr && !check_(m_ptr)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, m_ptr); } \ Name(object &&o) : Parent(std::move(o)) \ - { if (o && !check_(o)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, o); } + { if (m_ptr && !check_(m_ptr)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, m_ptr); } #define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \ PYBIND11_OBJECT(Name, Parent, CheckFun) \ @@ -1271,6 +1271,15 @@ class tuple : public object { detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; } }; +// We need to put this into a separate function because the Intel compiler +// fails to compile enable_if_t...>::value> part below +// (tested with ICC 2021.1 Beta 20200827). +template +constexpr bool args_are_all_keyword_or_ds() +{ + return detail::all_of...>::value; +} + class dict : public object { public: PYBIND11_OBJECT_CVT(dict, object, PyDict_Check, raw_dict) @@ -1278,7 +1287,7 @@ class dict : public object { if (!m_ptr) pybind11_fail("Could not allocate dict object!"); } template ...>::value>, + typename = detail::enable_if_t()>, // MSVC workaround: it can't compile an out-of-line definition, so defer the collector typename collector = detail::deferred_t, Args...>> explicit dict(Args &&...args) : dict(collector(std::forward(args)...).kwargs()) { } diff --git a/pybind11/include/pybind11/stl_bind.h b/pybind11/include/pybind11/stl_bind.h index 9d8ed0c..83195ee 100644 --- a/pybind11/include/pybind11/stl_bind.h +++ b/pybind11/include/pybind11/stl_bind.h @@ -375,10 +375,20 @@ struct vector_has_data_and_format : std::false_type {}; template struct vector_has_data_and_format::format(), std::declval().data()), typename Vector::value_type*>::value>> : std::true_type {}; +// [workaround(intel)] Separate function required here +// Workaround as the Intel compiler does not compile the enable_if_t part below +// (tested with icc (ICC) 2021.1 Beta 20200827) +template +constexpr bool args_any_are_buffer() { + return detail::any_of...>::value; +} + +// [workaround(intel)] Separate function required here +// [workaround(msvc)] Can't use constexpr bool in return type + // Add the buffer interface to a vector template -enable_if_t...>::value> -vector_buffer(Class_& cl) { +void vector_buffer_impl(Class_& cl, std::true_type) { using T = typename Vector::value_type; static_assert(vector_has_data_and_format::value, "There is not an appropriate format descriptor for this vector"); @@ -416,7 +426,12 @@ vector_buffer(Class_& cl) { } template -enable_if_t...>::value> vector_buffer(Class_&) {} +void vector_buffer_impl(Class_&, std::false_type) {} + +template +void vector_buffer(Class_& cl) { + vector_buffer_impl(cl, detail::any_of...>{}); +} PYBIND11_NAMESPACE_END(detail) diff --git a/pybind11/pybind11/_version.py b/pybind11/pybind11/_version.py index d18535c..f8b795e 100644 --- a/pybind11/pybind11/_version.py +++ b/pybind11/pybind11/_version.py @@ -8,5 +8,5 @@ def _to_int(s): return s -__version__ = "2.6.1" +__version__ = "2.6.2" version_info = tuple(_to_int(s) for s in __version__.split(".")) diff --git a/pybind11/pybind11/setup_helpers.py b/pybind11/pybind11/setup_helpers.py index 33605dd..c69064c 100644 --- a/pybind11/pybind11/setup_helpers.py +++ b/pybind11/pybind11/setup_helpers.py @@ -99,15 +99,14 @@ class Pybind11Extension(_Extension): this is an ugly old-style class due to Distutils. """ - def _add_cflags(self, *flags): - for flag in flags: - if flag not in self.extra_compile_args: - self.extra_compile_args.append(flag) + # flags are prepended, so that they can be further overridden, e.g. by + # ``extra_compile_args=["-g"]``. - def _add_lflags(self, *flags): - for flag in flags: - if flag not in self.extra_link_args: - self.extra_link_args.append(flag) + def _add_cflags(self, flags): + self.extra_compile_args[:0] = flags + + def _add_ldflags(self, flags): + self.extra_link_args[:0] = flags def __init__(self, *args, **kwargs): @@ -139,13 +138,17 @@ def __init__(self, *args, **kwargs): # Have to use the accessor manually to support Python 2 distutils Pybind11Extension.cxx_std.__set__(self, cxx_std) + cflags = [] + ldflags = [] if WIN: - self._add_cflags("/EHsc", "/bigobj") + cflags += ["/EHsc", "/bigobj"] else: - self._add_cflags("-fvisibility=hidden", "-g0") + cflags += ["-fvisibility=hidden", "-g0"] if MACOS: - self._add_cflags("-stdlib=libc++") - self._add_lflags("-stdlib=libc++") + cflags += ["-stdlib=libc++"] + ldflags += ["-stdlib=libc++"] + self._add_cflags(cflags) + self._add_ldflags(ldflags) @property def cxx_std(self): @@ -174,7 +177,8 @@ def cxx_std(self, level): if not level: return - self.extra_compile_args.append(STD_TMPL.format(level)) + cflags = [STD_TMPL.format(level)] + ldflags = [] if MACOS and "MACOSX_DEPLOYMENT_TARGET" not in os.environ: # C++17 requires a higher min version of macOS. An earlier version @@ -186,18 +190,21 @@ def cxx_std(self, level): desired_macos = (10, 9) if level < 17 else (10, 14) macos_string = ".".join(str(x) for x in min(current_macos, desired_macos)) macosx_min = "-mmacosx-version-min=" + macos_string - self.extra_compile_args.append(macosx_min) - self.extra_link_args.append(macosx_min) + cflags += [macosx_min] + ldflags += [macosx_min] if PY2: if WIN: # Will be ignored on MSVC 2015, where C++17 is not supported so # this flag is not valid. - self.extra_compile_args.append("/wd5033") + cflags += ["/wd5033"] elif level >= 17: - self.extra_compile_args.append("-Wno-register") + cflags += ["-Wno-register"] elif level >= 14: - self.extra_compile_args.append("-Wno-deprecated-register") + cflags += ["-Wno-deprecated-register"] + + self._add_cflags(cflags) + self._add_ldflags(ldflags) # Just in case someone clever tries to multithread @@ -232,7 +239,8 @@ def has_flag(compiler, flag): with tmp_chdir(): fname = "flagcheck.cpp" with open(fname, "w") as f: - f.write("int main (int argc, char **argv) { return 0; }") + # Don't trigger -Wunused-parameter. + f.write("int main (int, char **) { return 0; }") try: compiler.compile([fname], extra_postargs=[flag]) diff --git a/pybind11/setup.cfg b/pybind11/setup.cfg index e7fc8f4..041e62d 100644 --- a/pybind11/setup.cfg +++ b/pybind11/setup.cfg @@ -43,12 +43,7 @@ ignore = docs/** tools/** include/** - .appveyor.yml - .cmake-format.yaml - .gitmodules - .pre-commit-config.yaml - .readthedocs.yml - .clang-tidy + .* pybind11/include/** pybind11/share/** CMakeLists.txt @@ -68,4 +63,23 @@ ignore = [mypy] files = pybind11 -strict = True +python_version = 2.7 +warn_unused_configs = True + +# Currently (0.800) identical to --strict +disallow_any_generics = True +disallow_subclassing_any = True +disallow_untyped_calls = True +disallow_untyped_defs = True +disallow_incomplete_defs = True +check_untyped_defs = True +disallow_untyped_decorators = True +no_implicit_optional = True +warn_redundant_casts = True +warn_unused_ignores = True +warn_return_any = True +no_implicit_reexport = True +strict_equality = True + +[tool:pytest] +timeout = 300 diff --git a/pybind11/tests/CMakeLists.txt b/pybind11/tests/CMakeLists.txt index dae8b5a..3bfd5f1 100644 --- a/pybind11/tests/CMakeLists.txt +++ b/pybind11/tests/CMakeLists.txt @@ -24,7 +24,7 @@ include(CMakeParseArguments) # Usage: # pybind11_filter_tests(LISTNAME file1.cpp file2.cpp ... MESSAGE "") # -macro(PYBIND11_FILTER_TESTS LISTNAME) +macro(pybind11_filter_tests LISTNAME) cmake_parse_arguments(ARG "" "MESSAGE" "" ${ARGN}) set(PYBIND11_FILTER_TESTS_FOUND OFF) foreach(filename IN LISTS ARG_UNPARSED_ARGUMENTS) @@ -39,6 +39,14 @@ macro(PYBIND11_FILTER_TESTS LISTNAME) endif() endmacro() +macro(possibly_uninitialized) + foreach(VARNAME ${ARGN}) + if(NOT DEFINED "${VARNAME}") + set("${VARNAME}" "") + endif() + endforeach() +endmacro() + # New Python support if(DEFINED Python_EXECUTABLE) set(PYTHON_EXECUTABLE "${Python_EXECUTABLE}") @@ -67,7 +75,7 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) find_package(pybind11 REQUIRED CONFIG) endif() -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) +if(NOT CMAKE_BUILD_TYPE AND NOT DEFINED CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting tests build type to MinSizeRel as none was specified") set(CMAKE_BUILD_TYPE MinSizeRel @@ -345,11 +353,14 @@ foreach(target ${test_targets}) if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") - foreach(config ${CMAKE_CONFIGURATION_TYPES}) - string(TOUPPER ${config} config) - set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} - "${CMAKE_CURRENT_BINARY_DIR}") - endforeach() + + if(DEFINED CMAKE_CONFIGURATION_TYPES) + foreach(config ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${config} config) + set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} + "${CMAKE_CURRENT_BINARY_DIR}") + endforeach() + endif() endif() endforeach() @@ -370,12 +381,17 @@ endif() string(REPLACE "test_" "${CMAKE_CURRENT_SOURCE_DIR}/test_" PYBIND11_ABS_PYTEST_FILES "${PYBIND11_PYTEST_FILES}") +set(PYBIND11_TEST_PREFIX_COMMAND + "" + CACHE STRING "Put this before pytest, use for checkers and such") + # A single command to compile and run the tests add_custom_target( pytest - COMMAND ${PYTHON_EXECUTABLE} -m pytest ${PYBIND11_ABS_PYTEST_FILES} + COMMAND ${PYBIND11_TEST_PREFIX_COMMAND} ${PYTHON_EXECUTABLE} -m pytest + ${PYBIND11_ABS_PYTEST_FILES} DEPENDS ${test_targets} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" USES_TERMINAL) if(PYBIND11_TEST_OVERRIDE) @@ -386,6 +402,27 @@ if(PYBIND11_TEST_OVERRIDE) "Note: not all tests run: -DPYBIND11_TEST_OVERRIDE is in effect") endif() +# cmake-format: off +add_custom_target( + memcheck + COMMAND + PYTHONMALLOC=malloc + valgrind + --leak-check=full + --show-leak-kinds=definite,indirect + --errors-for-leak-kinds=definite,indirect + --error-exitcode=1 + --read-var-info=yes + --track-origins=yes + --suppressions="${CMAKE_CURRENT_SOURCE_DIR}/valgrind-python.supp" + --suppressions="${CMAKE_CURRENT_SOURCE_DIR}/valgrind-numpy-scipy.supp" + --gen-suppressions=all + ${PYTHON_EXECUTABLE} -m pytest ${PYBIND11_ABS_PYTEST_FILES} + DEPENDS ${test_targets} + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + USES_TERMINAL) +# cmake-format: on + # Add a check target to run all the tests, starting with pytest (we add dependencies to this below) add_custom_target(check DEPENDS pytest) diff --git a/pybind11/tests/pybind11_tests.h b/pybind11/tests/pybind11_tests.h index 4ff56c0..ccb0529 100644 --- a/pybind11/tests/pybind11_tests.h +++ b/pybind11/tests/pybind11_tests.h @@ -1,5 +1,6 @@ #pragma once #include +#include #if defined(_MSC_VER) && _MSC_VER < 1910 // We get some really long type names here which causes MSVC 2015 to emit warnings @@ -69,3 +70,15 @@ template<> class type_caster { }; PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(pybind11) + +template +void ignoreOldStyleInitWarnings(F &&body) { + py::exec(R"( + message = "pybind11-bound class '.+' is using an old-style placement-new '(?:__init__|__setstate__)' which has been deprecated" + + import warnings + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", message=message, category=FutureWarning) + body() + )", py::dict(py::arg("body") = py::cpp_function(body))); +} diff --git a/pybind11/tests/requirements.txt b/pybind11/tests/requirements.txt index 80ed617..8768fc1 100644 --- a/pybind11/tests/requirements.txt +++ b/pybind11/tests/requirements.txt @@ -3,6 +3,8 @@ numpy==1.16.6; python_version<"3.6" and sys_platform!="win32" numpy==1.18.0; platform_python_implementation=="PyPy" and sys_platform=="darwin" and python_version>="3.6" numpy==1.19.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.6" and python_version<"3.10" pytest==4.6.9; python_version<"3.5" -pytest==5.4.3; python_version>="3.5" +pytest==6.1.2; python_version=="3.5" +pytest==6.2.1; python_version>="3.6" +pytest-timeout scipy==1.2.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version<"3.6" -scipy==1.5.2; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.6" and python_version<"3.9" +scipy==1.5.4; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.6" and python_version<"3.10" diff --git a/pybind11/tests/test_buffers.py b/pybind11/tests/test_buffers.py index f0f3708..5084575 100644 --- a/pybind11/tests/test_buffers.py +++ b/pybind11/tests/test_buffers.py @@ -92,6 +92,8 @@ def test_readonly_buffer(): view = memoryview(buf) assert view[0] == b"d" if env.PY2 else 0x64 assert view.readonly + with pytest.raises(TypeError): + view[0] = b"\0" if env.PY2 else 0 def test_selective_readonly_buffer(): diff --git a/pybind11/tests/test_builtin_casters.cpp b/pybind11/tests/test_builtin_casters.cpp index acc9f8f..f4e7756 100644 --- a/pybind11/tests/test_builtin_casters.cpp +++ b/pybind11/tests/test_builtin_casters.cpp @@ -15,6 +15,49 @@ # pragma warning(disable: 4127) // warning C4127: Conditional expression is constant #endif +struct ConstRefCasted { + int tag; +}; + +PYBIND11_NAMESPACE_BEGIN(pybind11) +PYBIND11_NAMESPACE_BEGIN(detail) +template <> +class type_caster { + public: + static constexpr auto name = _(); + + // Input is unimportant, a new value will always be constructed based on the + // cast operator. + bool load(handle, bool) { return true; } + + operator ConstRefCasted&&() { value = {1}; return std::move(value); } + operator ConstRefCasted&() { value = {2}; return value; } + operator ConstRefCasted*() { value = {3}; return &value; } + + operator const ConstRefCasted&() { value = {4}; return value; } + operator const ConstRefCasted*() { value = {5}; return &value; } + + // custom cast_op to explicitly propagate types to the conversion operators. + template + using cast_op_type = + /// const + conditional_t< + std::is_same, const ConstRefCasted*>::value, const ConstRefCasted*, + conditional_t< + std::is_same::value, const ConstRefCasted&, + /// non-const + conditional_t< + std::is_same, ConstRefCasted*>::value, ConstRefCasted*, + conditional_t< + std::is_same::value, ConstRefCasted&, + /* else */ConstRefCasted&&>>>>; + + private: + ConstRefCasted value = {0}; +}; +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(pybind11) + TEST_SUBMODULE(builtin_casters, m) { // test_simple_string m.def("string_roundtrip", [](const char *s) { return s; }); @@ -98,6 +141,10 @@ TEST_SUBMODULE(builtin_casters, m) { m.def("i64_str", [](std::int64_t v) { return std::to_string(v); }); m.def("u64_str", [](std::uint64_t v) { return std::to_string(v); }); + // test_int_convert + m.def("int_passthrough", [](int arg) { return arg; }); + m.def("int_passthrough_noconvert", [](int arg) { return arg; }, py::arg{}.noconvert()); + // test_tuple m.def("pair_passthrough", [](std::pair input) { return std::make_pair(input.second, input.first); @@ -140,13 +187,35 @@ TEST_SUBMODULE(builtin_casters, m) { m.def("load_nullptr_t", [](std::nullptr_t) {}); // not useful, but it should still compile m.def("cast_nullptr_t", []() { return std::nullptr_t{}; }); + // [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works. + // test_bool_caster m.def("bool_passthrough", [](bool arg) { return arg; }); - m.def("bool_passthrough_noconvert", [](bool arg) { return arg; }, py::arg().noconvert()); + m.def("bool_passthrough_noconvert", [](bool arg) { return arg; }, py::arg{}.noconvert()); + + // TODO: This should be disabled and fixed in future Intel compilers +#if !defined(__INTEL_COMPILER) + // Test "bool_passthrough_noconvert" again, but using () instead of {} to construct py::arg + // When compiled with the Intel compiler, this results in segmentation faults when importing + // the module. Tested with icc (ICC) 2021.1 Beta 20200827, this should be tested again when + // a newer version of icc is available. + m.def("bool_passthrough_noconvert2", [](bool arg) { return arg; }, py::arg().noconvert()); +#endif // test_reference_wrapper m.def("refwrap_builtin", [](std::reference_wrapper p) { return 10 * p.get(); }); m.def("refwrap_usertype", [](std::reference_wrapper p) { return p.get().value(); }); + m.def("refwrap_usertype_const", [](std::reference_wrapper p) { return p.get().value(); }); + + m.def("refwrap_lvalue", []() -> std::reference_wrapper { + static UserType x(1); + return std::ref(x); + }); + m.def("refwrap_lvalue_const", []() -> std::reference_wrapper { + static UserType x(1); + return std::cref(x); + }); + // Not currently supported (std::pair caster has return-by-value cast operator); // triggers static_assert failure. //m.def("refwrap_pair", [](std::reference_wrapper>) { }); @@ -189,4 +258,14 @@ TEST_SUBMODULE(builtin_casters, m) { py::object o = py::cast(v); return py::cast(o) == v; }); + + // Tests const/non-const propagation in cast_op. + m.def("takes", [](ConstRefCasted x) { return x.tag; }); + m.def("takes_move", [](ConstRefCasted&& x) { return x.tag; }); + m.def("takes_ptr", [](ConstRefCasted* x) { return x->tag; }); + m.def("takes_ref", [](ConstRefCasted& x) { return x.tag; }); + m.def("takes_ref_wrap", [](std::reference_wrapper x) { return x.get().tag; }); + m.def("takes_const_ptr", [](const ConstRefCasted* x) { return x->tag; }); + m.def("takes_const_ref", [](const ConstRefCasted& x) { return x.tag; }); + m.def("takes_const_ref_wrap", [](std::reference_wrapper x) { return x.get().tag; }); } diff --git a/pybind11/tests/test_builtin_casters.py b/pybind11/tests/test_builtin_casters.py index bd7996b..cb37dbc 100644 --- a/pybind11/tests/test_builtin_casters.py +++ b/pybind11/tests/test_builtin_casters.py @@ -251,6 +251,88 @@ def test_integer_casting(): assert "incompatible function arguments" in str(excinfo.value) +def test_int_convert(): + class Int(object): + def __int__(self): + return 42 + + class NotInt(object): + pass + + class Float(object): + def __float__(self): + return 41.99999 + + class Index(object): + def __index__(self): + return 42 + + class IntAndIndex(object): + def __int__(self): + return 42 + + def __index__(self): + return 0 + + class RaisingTypeErrorOnIndex(object): + def __index__(self): + raise TypeError + + def __int__(self): + return 42 + + class RaisingValueErrorOnIndex(object): + def __index__(self): + raise ValueError + + def __int__(self): + return 42 + + convert, noconvert = m.int_passthrough, m.int_passthrough_noconvert + + def requires_conversion(v): + pytest.raises(TypeError, noconvert, v) + + def cant_convert(v): + pytest.raises(TypeError, convert, v) + + assert convert(7) == 7 + assert noconvert(7) == 7 + cant_convert(3.14159) + assert convert(Int()) == 42 + requires_conversion(Int()) + cant_convert(NotInt()) + cant_convert(Float()) + + # Before Python 3.8, `PyLong_AsLong` does not pick up on `obj.__index__`, + # but pybind11 "backports" this behavior. + assert convert(Index()) == 42 + assert noconvert(Index()) == 42 + assert convert(IntAndIndex()) == 0 # Fishy; `int(DoubleThought)` == 42 + assert noconvert(IntAndIndex()) == 0 + assert convert(RaisingTypeErrorOnIndex()) == 42 + requires_conversion(RaisingTypeErrorOnIndex()) + assert convert(RaisingValueErrorOnIndex()) == 42 + requires_conversion(RaisingValueErrorOnIndex()) + + +def test_numpy_int_convert(): + np = pytest.importorskip("numpy") + + convert, noconvert = m.int_passthrough, m.int_passthrough_noconvert + + def require_implicit(v): + pytest.raises(TypeError, noconvert, v) + + # `np.intc` is an alias that corresponds to a C++ `int` + assert convert(np.intc(42)) == 42 + assert noconvert(np.intc(42)) == 42 + + # The implicit conversion from np.float32 is undesirable but currently accepted. + assert convert(np.float32(3.14159)) == 3 + require_implicit(np.float32(3.14159)) + + def test_tuple(doc): """std::pair <-> tuple & std::tuple <-> tuple""" assert m.pair_passthrough((True, "test")) == ("test", True) @@ -315,6 +397,7 @@ def test_reference_wrapper(): """std::reference_wrapper for builtin and user types""" assert m.refwrap_builtin(42) == 420 assert m.refwrap_usertype(UserType(42)) == 42 + assert m.refwrap_usertype_const(UserType(42)) == 42 with pytest.raises(TypeError) as excinfo: m.refwrap_builtin(None) @@ -324,6 +407,9 @@ def test_reference_wrapper(): m.refwrap_usertype(None) assert "incompatible function arguments" in str(excinfo.value) + assert m.refwrap_lvalue().value == 1 + assert m.refwrap_lvalue_const().value == 1 + a1 = m.refwrap_list(copy=True) a2 = m.refwrap_list(copy=True) assert [x.value for x in a1] == [2, 3] @@ -421,3 +507,21 @@ def test_int_long(): def test_void_caster_2(): assert m.test_void_caster() + + +def test_const_ref_caster(): + """Verifies that const-ref is propagated through type_caster cast_op. + The returned ConstRefCasted type is a mimimal type that is constructed to + reference the casting mode used. + """ + x = False + assert m.takes(x) == 1 + assert m.takes_move(x) == 1 + + assert m.takes_ptr(x) == 3 + assert m.takes_ref(x) == 2 + assert m.takes_ref_wrap(x) == 2 + + assert m.takes_const_ptr(x) == 5 + assert m.takes_const_ref(x) == 4 + assert m.takes_const_ref_wrap(x) == 4 diff --git a/pybind11/tests/test_callbacks.cpp b/pybind11/tests/test_callbacks.cpp index 683dfb3..dffe538 100644 --- a/pybind11/tests/test_callbacks.cpp +++ b/pybind11/tests/test_callbacks.cpp @@ -119,7 +119,10 @@ TEST_SUBMODULE(callbacks, m) { class AbstractBase { public: - virtual ~AbstractBase() = default; + // [workaround(intel)] = default does not work here + // Defaulting this destructor results in linking errors with the Intel compiler + // (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827) + virtual ~AbstractBase() {}; // NOLINT(modernize-use-equals-default) virtual unsigned int func() = 0; }; m.def("func_accepting_func_accepting_base", [](std::function) { }); diff --git a/pybind11/tests/test_class.cpp b/pybind11/tests/test_class.cpp index 890fab7..6ce928c 100644 --- a/pybind11/tests/test_class.cpp +++ b/pybind11/tests/test_class.cpp @@ -7,6 +7,13 @@ BSD-style license that can be found in the LICENSE file. */ +#if defined(__INTEL_COMPILER) && __cplusplus >= 201703L +// Intel compiler requires a separate header file to support aligned new operators +// and does not set the __cpp_aligned_new feature macro. +// This header needs to be included before pybind11. +#include +#endif + #include "pybind11_tests.h" #include "constructor_stats.h" #include "local_bindings.h" @@ -231,7 +238,8 @@ TEST_SUBMODULE(class_, m) { }; auto def = new PyMethodDef{"f", f, METH_VARARGS, nullptr}; - return py::reinterpret_steal(PyCFunction_NewEx(def, nullptr, m.ptr())); + py::capsule def_capsule(def, [](void *ptr) { delete reinterpret_cast(ptr); }); + return py::reinterpret_steal(PyCFunction_NewEx(def, def_capsule.ptr(), m.ptr())); }()); // test_operator_new_delete @@ -322,6 +330,10 @@ TEST_SUBMODULE(class_, m) { class PublicistB : public ProtectedB { public: + // [workaround(intel)] = default does not work here + // Removing or defaulting this destructor results in linking errors with the Intel compiler + // (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827) + ~PublicistB() override {}; // NOLINT(modernize-use-equals-default) using ProtectedB::foo; }; diff --git a/pybind11/tests/test_cmake_build/CMakeLists.txt b/pybind11/tests/test_cmake_build/CMakeLists.txt index 0c0578a..8bfaa38 100644 --- a/pybind11/tests/test_cmake_build/CMakeLists.txt +++ b/pybind11/tests/test_cmake_build/CMakeLists.txt @@ -25,7 +25,7 @@ function(pybind11_add_build_test name) endif() if(NOT ARG_INSTALL) - list(APPEND build_options "-DPYBIND11_PROJECT_DIR=${pybind11_SOURCE_DIR}") + list(APPEND build_options "-Dpybind11_SOURCE_DIR=${pybind11_SOURCE_DIR}") else() list(APPEND build_options "-DCMAKE_PREFIX_PATH=${pybind11_BINARY_DIR}/mock_install") endif() @@ -55,6 +55,8 @@ function(pybind11_add_build_test name) add_dependencies(test_cmake_build test_build_${name}) endfunction() +possibly_uninitialized(PYTHON_MODULE_EXTENSION Python_INTERPRETER_ID) + pybind11_add_build_test(subdirectory_function) pybind11_add_build_test(subdirectory_target) if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy") @@ -77,3 +79,6 @@ if(PYBIND11_INSTALL) endif() add_dependencies(check test_cmake_build) + +add_subdirectory(subdirectory_target EXCLUDE_FROM_ALL) +add_subdirectory(subdirectory_embed EXCLUDE_FROM_ALL) diff --git a/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt b/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt index c7df0cf..dfb9cb8 100644 --- a/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt +++ b/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt @@ -16,7 +16,7 @@ set(PYBIND11_INSTALL CACHE BOOL "") set(PYBIND11_EXPORT_NAME test_export) -add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) +add_subdirectory("${pybind11_SOURCE_DIR}" pybind11) # Test basic target functionality add_executable(test_subdirectory_embed ../embed.cpp) @@ -24,7 +24,7 @@ target_link_libraries(test_subdirectory_embed PRIVATE pybind11::embed) set_target_properties(test_subdirectory_embed PROPERTIES OUTPUT_NAME test_cmake_build) add_custom_target(check_subdirectory_embed $ - ${PROJECT_SOURCE_DIR}/../test.py) + "${PROJECT_SOURCE_DIR}/../test.py") # Test custom export group -- PYBIND11_EXPORT_NAME add_library(test_embed_lib ../embed.cpp) diff --git a/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt b/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt index 624c600..34aedcf 100644 --- a/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt +++ b/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt @@ -11,7 +11,7 @@ endif() project(test_subdirectory_function CXX) -add_subdirectory("${PYBIND11_PROJECT_DIR}" pybind11) +add_subdirectory("${pybind11_SOURCE_DIR}" pybind11) pybind11_add_module(test_subdirectory_function ../main.cpp) set_target_properties(test_subdirectory_function PROPERTIES OUTPUT_NAME test_cmake_build) diff --git a/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt b/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt index 2471941..31d862f 100644 --- a/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt +++ b/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt @@ -11,7 +11,7 @@ endif() project(test_subdirectory_target CXX) -add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) +add_subdirectory("${pybind11_SOURCE_DIR}" pybind11) add_library(test_subdirectory_target MODULE ../main.cpp) set_target_properties(test_subdirectory_target PROPERTIES OUTPUT_NAME test_cmake_build) diff --git a/pybind11/tests/test_constants_and_functions.cpp b/pybind11/tests/test_constants_and_functions.cpp index f607795..8855dd7 100644 --- a/pybind11/tests/test_constants_and_functions.cpp +++ b/pybind11/tests/test_constants_and_functions.cpp @@ -46,7 +46,14 @@ std::string print_bytes(py::bytes bytes) { // Test that we properly handle C++17 exception specifiers (which are part of the function signature // in C++17). These should all still work before C++17, but don't affect the function signature. namespace test_exc_sp { +// [workaround(intel)] Unable to use noexcept instead of noexcept(true) +// Make the f1 test basically the same as the f2 test in C++17 mode for the Intel compiler as +// it fails to compile with a plain noexcept (tested with icc (ICC) 2021.1 Beta 20200827). +#if defined(__INTEL_COMPILER) && defined(PYBIND11_CPP17) +int f1(int x) noexcept(true) { return x+1; } +#else int f1(int x) noexcept { return x+1; } +#endif int f2(int x) noexcept(true) { return x+2; } int f3(int x) noexcept(false) { return x+3; } #if defined(__GNUG__) @@ -124,4 +131,19 @@ TEST_SUBMODULE(constants_and_functions, m) { m.def("f2", f2); m.def("f3", f3); m.def("f4", f4); + + // test_function_record_leaks + struct LargeCapture { + // This should always be enough to trigger the alternative branch + // where `sizeof(capture) > sizeof(rec->data)` + uint64_t zeros[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + }; + m.def("register_large_capture_with_invalid_arguments", [](py::module_ m) { + LargeCapture capture; // VS 2015's MSVC is acting up if we create the array here + m.def("should_raise", [capture](int) { return capture.zeros[9] + 33; }, py::kw_only(), py::arg()); + }); + m.def("register_with_raising_repr", [](py::module_ m, py::object default_value) { + m.def("should_raise", [](int, int, py::object) { return 42; }, "some docstring", + py::arg_v("x", 42), py::arg_v("y", 42, ""), py::arg_v("z", default_value)); + }); } diff --git a/pybind11/tests/test_constants_and_functions.py b/pybind11/tests/test_constants_and_functions.py index b980ccf..ff13bd0 100644 --- a/pybind11/tests/test_constants_and_functions.py +++ b/pybind11/tests/test_constants_and_functions.py @@ -40,3 +40,14 @@ def test_exception_specifiers(): assert m.f2(53) == 55 assert m.f3(86) == 89 assert m.f4(140) == 144 + + +def test_function_record_leaks(): + class RaisingRepr: + def __repr__(self): + raise RuntimeError("Surprise!") + + with pytest.raises(RuntimeError): + m.register_large_capture_with_invalid_arguments(m) + with pytest.raises(RuntimeError): + m.register_with_raising_repr(m, RaisingRepr()) diff --git a/pybind11/tests/test_copy_move.cpp b/pybind11/tests/test_copy_move.cpp index 2704217..322e9bb 100644 --- a/pybind11/tests/test_copy_move.cpp +++ b/pybind11/tests/test_copy_move.cpp @@ -214,6 +214,7 @@ TEST_SUBMODULE(copy_move_policies, m) { }; py::class_(m, "MoveIssue2").def(py::init()).def_readwrite("value", &MoveIssue2::v); - m.def("get_moveissue1", [](int i) { return new MoveIssue1(i); }, py::return_value_policy::move); + // #2742: Don't expect ownership of raw pointer to `new`ed object to be transferred with `py::return_value_policy::move` + m.def("get_moveissue1", [](int i) { return std::unique_ptr(new MoveIssue1(i)); }, py::return_value_policy::move); m.def("get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move); } diff --git a/pybind11/tests/test_copy_move.py b/pybind11/tests/test_copy_move.py index 7e3cc16..1d98952 100644 --- a/pybind11/tests/test_copy_move.py +++ b/pybind11/tests/test_copy_move.py @@ -119,7 +119,7 @@ def test_private_op_new(): def test_move_fallback(): """#389: rvp::move should fall-through to copy on non-movable objects""" - m2 = m.get_moveissue2(2) - assert m2.value == 2 m1 = m.get_moveissue1(1) assert m1.value == 1 + m2 = m.get_moveissue2(2) + assert m2.value == 2 diff --git a/pybind11/tests/test_custom_type_casters.cpp b/pybind11/tests/test_custom_type_casters.cpp index d565add..3fe910d 100644 --- a/pybind11/tests/test_custom_type_casters.cpp +++ b/pybind11/tests/test_custom_type_casters.cpp @@ -99,19 +99,20 @@ TEST_SUBMODULE(custom_type_casters, m) { } static ArgInspector2 h(ArgInspector2 a, ArgAlwaysConverts) { return a; } }; + // [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works. py::class_(m, "ArgInspector") .def(py::init<>()) .def("f", &ArgInspector::f, py::arg(), py::arg() = ArgAlwaysConverts()) .def("g", &ArgInspector::g, "a"_a.noconvert(), "b"_a, "c"_a.noconvert()=13, "d"_a=ArgInspector2(), py::arg() = ArgAlwaysConverts()) - .def_static("h", &ArgInspector::h, py::arg().noconvert(), py::arg() = ArgAlwaysConverts()) + .def_static("h", &ArgInspector::h, py::arg{}.noconvert(), py::arg() = ArgAlwaysConverts()) ; m.def("arg_inspect_func", [](ArgInspector2 a, ArgInspector1 b, ArgAlwaysConverts) { return a.arg + "\n" + b.arg; }, - py::arg().noconvert(false), py::arg_v(nullptr, ArgInspector1()).noconvert(true), py::arg() = ArgAlwaysConverts()); + py::arg{}.noconvert(false), py::arg_v(nullptr, ArgInspector1()).noconvert(true), py::arg() = ArgAlwaysConverts()); - m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f")); - m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert()); - m.def("ints_preferred", [](int i) { return i / 2; }, py::arg("i")); - m.def("ints_only", [](int i) { return i / 2; }, py::arg("i").noconvert()); + m.def("floats_preferred", [](double f) { return 0.5 * f; }, "f"_a); + m.def("floats_only", [](double f) { return 0.5 * f; }, "f"_a.noconvert()); + m.def("ints_preferred", [](int i) { return i / 2; }, "i"_a); + m.def("ints_only", [](int i) { return i / 2; }, "i"_a.noconvert()); // test_custom_caster_destruction // Test that `take_ownership` works on types with a custom type caster when given a pointer diff --git a/pybind11/tests/test_docstring_options.cpp b/pybind11/tests/test_docstring_options.cpp index 8c8f79f..8a97af5 100644 --- a/pybind11/tests/test_docstring_options.cpp +++ b/pybind11/tests/test_docstring_options.cpp @@ -45,6 +45,14 @@ TEST_SUBMODULE(docstring_options, m) { m.def("test_function7", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring"); + { + py::options options; + options.disable_user_defined_docstrings(); + options.disable_function_signatures(); + + m.def("test_function8", []() {}); + } + { py::options options; options.disable_user_defined_docstrings(); diff --git a/pybind11/tests/test_docstring_options.py b/pybind11/tests/test_docstring_options.py index 87d80d2..8ee6613 100644 --- a/pybind11/tests/test_docstring_options.py +++ b/pybind11/tests/test_docstring_options.py @@ -34,6 +34,9 @@ def test_docstring_options(): assert m.test_function7.__doc__.startswith("test_function7(a: int, b: int) -> None") assert m.test_function7.__doc__.endswith("A custom docstring\n") + # when all options are disabled, no docstring (instead of an empty one) should be generated + assert m.test_function8.__doc__ is None + # Suppression of user-defined docstrings for non-function objects assert not m.DocstringTestFoo.__doc__ assert not m.DocstringTestFoo.value_prop.__doc__ diff --git a/pybind11/tests/test_eigen.cpp b/pybind11/tests/test_eigen.cpp index 2cc2243..8432547 100644 --- a/pybind11/tests/test_eigen.cpp +++ b/pybind11/tests/test_eigen.cpp @@ -273,6 +273,7 @@ TEST_SUBMODULE(eigen, m) { m.def("cpp_ref_r", [](py::handle m) { return m.cast>()(1, 0); }); m.def("cpp_ref_any", [](py::handle m) { return m.cast>()(1, 0); }); + // [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works. // test_nocopy_wrapper // Test that we can prevent copying into an argument that would normally copy: First a version @@ -280,17 +281,17 @@ TEST_SUBMODULE(eigen, m) { m.def("get_elem", &get_elem); // Now this alternative that calls the tells pybind to fail rather than copy: m.def("get_elem_nocopy", [](Eigen::Ref m) -> double { return get_elem(m); }, - py::arg().noconvert()); + py::arg{}.noconvert()); // Also test a row-major-only no-copy const ref: m.def("get_elem_rm_nocopy", [](Eigen::Ref> &m) -> long { return m(2, 1); }, - py::arg().noconvert()); + py::arg{}.noconvert()); // test_issue738 // Issue #738: 1xN or Nx1 2D matrices were neither accepted nor properly copied with an // incompatible stride value on the length-1 dimension--but that should be allowed (without // requiring a copy!) because the stride value can be safely ignored on a size-1 dimension. - m.def("iss738_f1", &adjust_matrix &>, py::arg().noconvert()); - m.def("iss738_f2", &adjust_matrix> &>, py::arg().noconvert()); + m.def("iss738_f1", &adjust_matrix &>, py::arg{}.noconvert()); + m.def("iss738_f2", &adjust_matrix> &>, py::arg{}.noconvert()); // test_issue1105 // Issue #1105: when converting from a numpy two-dimensional (Nx1) or (1xN) value into a dense diff --git a/pybind11/tests/test_embed/CMakeLists.txt b/pybind11/tests/test_embed/CMakeLists.txt index fabcb24..c960c87 100644 --- a/pybind11/tests/test_embed/CMakeLists.txt +++ b/pybind11/tests/test_embed/CMakeLists.txt @@ -1,4 +1,7 @@ +possibly_uninitialized(PYTHON_MODULE_EXTENSION Python_INTERPRETER_ID) + if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy") + message(STATUS "Skipping embed test on PyPy") add_custom_target(cpptest) # Dummy target on PyPy. Embedding is not supported. set(_suppress_unused_variable_warning "${DOWNLOAD_CATCH}") return() diff --git a/pybind11/tests/test_enum.py b/pybind11/tests/test_enum.py index f6b24fc..e9732fa 100644 --- a/pybind11/tests/test_enum.py +++ b/pybind11/tests/test_enum.py @@ -13,15 +13,24 @@ def test_unscoped_enum(): # name property assert m.UnscopedEnum.EOne.name == "EOne" + assert m.UnscopedEnum.EOne.value == 1 assert m.UnscopedEnum.ETwo.name == "ETwo" - assert m.EOne.name == "EOne" - # name readonly + assert m.UnscopedEnum.ETwo.value == 2 + assert m.EOne is m.UnscopedEnum.EOne + # name, value readonly with pytest.raises(AttributeError): m.UnscopedEnum.EOne.name = "" - # name returns a copy - foo = m.UnscopedEnum.EOne.name - foo = "bar" + with pytest.raises(AttributeError): + m.UnscopedEnum.EOne.value = 10 + # name, value returns a copy + # TODO: Neither the name nor value tests actually check against aliasing. + # Use a mutable type that has reference semantics. + nonaliased_name = m.UnscopedEnum.EOne.name + nonaliased_name = "bar" # noqa: F841 assert m.UnscopedEnum.EOne.name == "EOne" + nonaliased_value = m.UnscopedEnum.EOne.value + nonaliased_value = 10 # noqa: F841 + assert m.UnscopedEnum.EOne.value == 1 # __members__ property assert m.UnscopedEnum.__members__ == { @@ -33,8 +42,8 @@ def test_unscoped_enum(): with pytest.raises(AttributeError): m.UnscopedEnum.__members__ = {} # __members__ returns a copy - foo = m.UnscopedEnum.__members__ - foo["bar"] = "baz" + nonaliased_members = m.UnscopedEnum.__members__ + nonaliased_members["bar"] = "baz" assert m.UnscopedEnum.__members__ == { "EOne": m.UnscopedEnum.EOne, "ETwo": m.UnscopedEnum.ETwo, @@ -73,25 +82,25 @@ def test_unscoped_enum(): assert not (y == "2") with pytest.raises(TypeError): - y < object() + y < object() # noqa: B015 with pytest.raises(TypeError): - y <= object() + y <= object() # noqa: B015 with pytest.raises(TypeError): - y > object() + y > object() # noqa: B015 with pytest.raises(TypeError): - y >= object() + y >= object() # noqa: B015 with pytest.raises(TypeError): - y | object() + y | object() # noqa: B015 with pytest.raises(TypeError): - y & object() + y & object() # noqa: B015 with pytest.raises(TypeError): - y ^ object() + y ^ object() # noqa: B015 assert int(m.UnscopedEnum.ETwo) == 2 assert str(m.UnscopedEnum(2)) == "UnscopedEnum.ETwo" @@ -134,13 +143,13 @@ def test_scoped_enum(): assert not (z == object()) # Scoped enums will *NOT* accept >, <, >= and <= int comparisons (Will throw exceptions) with pytest.raises(TypeError): - z > 3 + z > 3 # noqa: B015 with pytest.raises(TypeError): - z < 3 + z < 3 # noqa: B015 with pytest.raises(TypeError): - z >= 3 + z >= 3 # noqa: B015 with pytest.raises(TypeError): - z <= 3 + z <= 3 # noqa: B015 # order assert m.ScopedEnum.Two < m.ScopedEnum.Three diff --git a/pybind11/tests/test_exceptions.py b/pybind11/tests/test_exceptions.py index 95eac70..c6cb652 100644 --- a/pybind11/tests/test_exceptions.py +++ b/pybind11/tests/test_exceptions.py @@ -50,13 +50,24 @@ def test_python_call_in_catch(): assert d["good"] is True +def ignore_pytest_unraisable_warning(f): + unraisable = "PytestUnraisableExceptionWarning" + if hasattr(pytest, unraisable): # Python >= 3.8 and pytest >= 6 + dec = pytest.mark.filterwarnings("ignore::pytest.{}".format(unraisable)) + return dec(f) + else: + return f + + +@ignore_pytest_unraisable_warning def test_python_alreadyset_in_destructor(monkeypatch, capsys): hooked = False triggered = [False] # mutable, so Python 2.7 closure can modify it if hasattr(sys, "unraisablehook"): # Python 3.8+ hooked = True - default_hook = sys.unraisablehook + # Don't take `sys.unraisablehook`, as that's overwritten by pytest + default_hook = sys.__unraisablehook__ def hook(unraisable_hook_args): exc_type, exc_value, exc_tb, err_msg, obj = unraisable_hook_args diff --git a/pybind11/tests/test_factory_constructors.cpp b/pybind11/tests/test_factory_constructors.cpp index f42d1f2..7ff7e7b 100644 --- a/pybind11/tests/test_factory_constructors.cpp +++ b/pybind11/tests/test_factory_constructors.cpp @@ -183,11 +183,14 @@ TEST_SUBMODULE(factory_constructors, m) { auto c4a = [c](pointer_tag, TF4_tag, int a) { (void) c; return new TestFactory4(a);}; // test_init_factory_basic, test_init_factory_casting - py::class_>(m, "TestFactory3") + py::class_> pyTestFactory3(m, "TestFactory3"); + pyTestFactory3 .def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct3(v); })) - .def(py::init([](shared_ptr_tag) { return TestFactoryHelper::construct3(); })) - .def("__init__", [](TestFactory3 &self, std::string v) { new (&self) TestFactory3(v); }) // placement-new ctor - + .def(py::init([](shared_ptr_tag) { return TestFactoryHelper::construct3(); })); + ignoreOldStyleInitWarnings([&pyTestFactory3]() { + pyTestFactory3.def("__init__", [](TestFactory3 &self, std::string v) { new (&self) TestFactory3(v); }); // placement-new ctor + }); + pyTestFactory3 // factories returning a derived type: .def(py::init(c4a)) // derived ptr .def(py::init([](pointer_tag, TF5_tag, int a) { return new TestFactory5(a); })) @@ -304,24 +307,32 @@ TEST_SUBMODULE(factory_constructors, m) { static void operator delete(void *p) { py::print("noisy delete"); ::operator delete(p); } #endif }; - py::class_(m, "NoisyAlloc") + + + py::class_ pyNoisyAlloc(m, "NoisyAlloc"); // Since these overloads have the same number of arguments, the dispatcher will try each of // them until the arguments convert. Thus we can get a pre-allocation here when passing a // single non-integer: - .def("__init__", [](NoisyAlloc *a, int i) { new (a) NoisyAlloc(i); }) // Regular constructor, runs first, requires preallocation - .def(py::init([](double d) { return new NoisyAlloc(d); })) - - // The two-argument version: first the factory pointer overload. - .def(py::init([](int i, int) { return new NoisyAlloc(i); })) - // Return-by-value: - .def(py::init([](double d, int) { return NoisyAlloc(d); })) - // Old-style placement new init; requires preallocation - .def("__init__", [](NoisyAlloc &a, double d, double) { new (&a) NoisyAlloc(d); }) - // Requires deallocation of previous overload preallocated value: - .def(py::init([](int i, double) { return new NoisyAlloc(i); })) - // Regular again: requires yet another preallocation - .def("__init__", [](NoisyAlloc &a, int i, std::string) { new (&a) NoisyAlloc(i); }) - ; + ignoreOldStyleInitWarnings([&pyNoisyAlloc]() { + pyNoisyAlloc.def("__init__", [](NoisyAlloc *a, int i) { new (a) NoisyAlloc(i); }); // Regular constructor, runs first, requires preallocation + }); + + pyNoisyAlloc.def(py::init([](double d) { return new NoisyAlloc(d); })); + + // The two-argument version: first the factory pointer overload. + pyNoisyAlloc.def(py::init([](int i, int) { return new NoisyAlloc(i); })); + // Return-by-value: + pyNoisyAlloc.def(py::init([](double d, int) { return NoisyAlloc(d); })); + // Old-style placement new init; requires preallocation + ignoreOldStyleInitWarnings([&pyNoisyAlloc]() { + pyNoisyAlloc.def("__init__", [](NoisyAlloc &a, double d, double) { new (&a) NoisyAlloc(d); }); + }); + // Requires deallocation of previous overload preallocated value: + pyNoisyAlloc.def(py::init([](int i, double) { return new NoisyAlloc(i); })); + // Regular again: requires yet another preallocation + ignoreOldStyleInitWarnings([&pyNoisyAlloc]() { + pyNoisyAlloc.def("__init__", [](NoisyAlloc &a, int i, std::string) { new (&a) NoisyAlloc(i); }); + }); diff --git a/pybind11/tests/test_iostream.cpp b/pybind11/tests/test_iostream.cpp index e916150..1be0655 100644 --- a/pybind11/tests/test_iostream.cpp +++ b/pybind11/tests/test_iostream.cpp @@ -7,10 +7,15 @@ BSD-style license that can be found in the LICENSE file. */ +#if defined(_MSC_VER) && _MSC_VER < 1910 // VS 2015's MSVC +# pragma warning(disable: 4702) // unreachable code in system header (xatomic.h(382)) +#endif #include #include "pybind11_tests.h" +#include #include +#include void noisy_function(std::string msg, bool flush) { @@ -25,6 +30,40 @@ void noisy_funct_dual(std::string msg, std::string emsg) { std::cerr << emsg; } +// object to manage C++ thread +// simply repeatedly write to std::cerr until stopped +// redirect is called at some point to test the safety of scoped_estream_redirect +struct TestThread { + TestThread() : t_{nullptr}, stop_{false} { + auto thread_f = [this] { + while (!stop_) { + std::cout << "x" << std::flush; + std::this_thread::sleep_for(std::chrono::microseconds(50)); + } }; + t_ = new std::thread(std::move(thread_f)); + } + + ~TestThread() { + delete t_; + } + + void stop() { stop_ = true; } + + void join() { + py::gil_scoped_release gil_lock; + t_->join(); + } + + void sleep() { + py::gil_scoped_release gil_lock; + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + + std::thread * t_; + std::atomic stop_; +}; + + TEST_SUBMODULE(iostream, m) { add_ostream_redirect(m); @@ -70,4 +109,10 @@ TEST_SUBMODULE(iostream, m) { std::cout << msg << std::flush; std::cerr << emsg << std::flush; }); + + py::class_(m, "TestThread") + .def(py::init<>()) + .def("stop", &TestThread::stop) + .def("join", &TestThread::join) + .def("sleep", &TestThread::sleep); } diff --git a/pybind11/tests/test_iostream.py b/pybind11/tests/test_iostream.py index 506db42..6d493be 100644 --- a/pybind11/tests/test_iostream.py +++ b/pybind11/tests/test_iostream.py @@ -216,3 +216,26 @@ def test_redirect_both(capfd): assert stderr == "" assert stream.getvalue() == msg assert stream2.getvalue() == msg2 + + +def test_threading(): + with m.ostream_redirect(stdout=True, stderr=False): + # start some threads + threads = [] + + # start some threads + for _j in range(20): + threads.append(m.TestThread()) + + # give the threads some time to fail + threads[0].sleep() + + # stop all the threads + for t in threads: + t.stop() + + for t in threads: + t.join() + + # if a thread segfaults, we don't get here + assert True diff --git a/pybind11/tests/test_local_bindings.py b/pybind11/tests/test_local_bindings.py index d23c467..a38564b 100644 --- a/pybind11/tests/test_local_bindings.py +++ b/pybind11/tests/test_local_bindings.py @@ -193,7 +193,7 @@ def test_stl_caster_vs_stl_bind(msg): v2 = [1, 2, 3] assert m.load_vector_via_caster(v2) == 6 with pytest.raises(TypeError) as excinfo: - cm.load_vector_via_binding(v2) == 6 + cm.load_vector_via_binding(v2) assert ( msg(excinfo.value) == """ diff --git a/pybind11/tests/test_methods_and_attributes.cpp b/pybind11/tests/test_methods_and_attributes.cpp index 6a2cfb6..f99909b 100644 --- a/pybind11/tests/test_methods_and_attributes.cpp +++ b/pybind11/tests/test_methods_and_attributes.cpp @@ -322,22 +322,24 @@ TEST_SUBMODULE(methods_and_attributes, m) { m.def("should_fail", [](int, UnregisteredType) {}, py::arg(), py::arg() = UnregisteredType()); }); + // [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works. + // test_accepts_none py::class_>(m, "NoneTester") .def(py::init<>()); - m.def("no_none1", &none1, py::arg().none(false)); - m.def("no_none2", &none2, py::arg().none(false)); - m.def("no_none3", &none3, py::arg().none(false)); - m.def("no_none4", &none4, py::arg().none(false)); - m.def("no_none5", &none5, py::arg().none(false)); + m.def("no_none1", &none1, py::arg{}.none(false)); + m.def("no_none2", &none2, py::arg{}.none(false)); + m.def("no_none3", &none3, py::arg{}.none(false)); + m.def("no_none4", &none4, py::arg{}.none(false)); + m.def("no_none5", &none5, py::arg{}.none(false)); m.def("ok_none1", &none1); - m.def("ok_none2", &none2, py::arg().none(true)); + m.def("ok_none2", &none2, py::arg{}.none(true)); m.def("ok_none3", &none3); - m.def("ok_none4", &none4, py::arg().none(true)); + m.def("ok_none4", &none4, py::arg{}.none(true)); m.def("ok_none5", &none5); - m.def("no_none_kwarg", &none2, py::arg("a").none(false)); - m.def("no_none_kwarg_kw_only", &none2, py::kw_only(), py::arg("a").none(false)); + m.def("no_none_kwarg", &none2, "a"_a.none(false)); + m.def("no_none_kwarg_kw_only", &none2, py::kw_only(), "a"_a.none(false)); // test_str_issue // Issue #283: __str__ called on uninitialized instance when constructor arguments invalid diff --git a/pybind11/tests/test_modules.py b/pybind11/tests/test_modules.py index 5630ccf..3390031 100644 --- a/pybind11/tests/test_modules.py +++ b/pybind11/tests/test_modules.py @@ -75,3 +75,16 @@ def test_duplicate_registration(): """Registering two things with the same name""" assert m.duplicate_registration() == [] + + +def test_builtin_key_type(): + """Test that all the keys in the builtin modules have type str. + + Previous versions of pybind11 would add a unicode key in python 2. + """ + if hasattr(__builtins__, "keys"): + keys = __builtins__.keys() + else: # this is to make pypy happy since builtins is different there. + keys = __builtins__.__dict__.keys() + + assert {type(k) for k in keys} == {str} diff --git a/pybind11/tests/test_numpy_array.cpp b/pybind11/tests/test_numpy_array.cpp index a84de77..dca7145 100644 --- a/pybind11/tests/test_numpy_array.cpp +++ b/pybind11/tests/test_numpy_array.cpp @@ -258,9 +258,11 @@ TEST_SUBMODULE(numpy_array, sm) { sm.def("overloaded2", [](py::array_t>) { return "float complex"; }); sm.def("overloaded2", [](py::array_t) { return "float"; }); + // [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works. + // Only accept the exact types: - sm.def("overloaded3", [](py::array_t) { return "int"; }, py::arg().noconvert()); - sm.def("overloaded3", [](py::array_t) { return "double"; }, py::arg().noconvert()); + sm.def("overloaded3", [](py::array_t) { return "int"; }, py::arg{}.noconvert()); + sm.def("overloaded3", [](py::array_t) { return "double"; }, py::arg{}.noconvert()); // Make sure we don't do unsafe coercion (e.g. float to int) when not using forcecast, but // rather that float gets converted via the safe (conversion to double) overload: @@ -284,7 +286,7 @@ TEST_SUBMODULE(numpy_array, sm) { for (py::ssize_t i = 0; i < r.shape(0); i++) for (py::ssize_t j = 0; j < r.shape(1); j++) r(i, j) += v; - }, py::arg().noconvert(), py::arg()); + }, py::arg{}.noconvert(), py::arg()); sm.def("proxy_init3", [](double start) { py::array_t a({ 3, 3, 3 }); @@ -338,7 +340,7 @@ TEST_SUBMODULE(numpy_array, sm) { for (py::ssize_t i = 0; i < r.shape(0); i++) for (py::ssize_t j = 0; j < r.shape(1); j++) r(i, j) += v; - }, py::arg().noconvert(), py::arg()); + }, py::arg{}.noconvert(), py::arg()); sm.def("proxy_init3_dyn", [](double start) { py::array_t a({ 3, 3, 3 }); auto r = a.mutable_unchecked(); @@ -419,20 +421,20 @@ TEST_SUBMODULE(numpy_array, sm) { py::arg("a")); sm.def("accept_double_noconvert", [](py::array_t) {}, - py::arg("a").noconvert()); + "a"_a.noconvert()); sm.def("accept_double_forcecast_noconvert", [](py::array_t) {}, - py::arg("a").noconvert()); + "a"_a.noconvert()); sm.def("accept_double_c_style_noconvert", [](py::array_t) {}, - py::arg("a").noconvert()); + "a"_a.noconvert()); sm.def("accept_double_c_style_forcecast_noconvert", [](py::array_t) {}, - py::arg("a").noconvert()); + "a"_a.noconvert()); sm.def("accept_double_f_style_noconvert", [](py::array_t) {}, - py::arg("a").noconvert()); + "a"_a.noconvert()); sm.def("accept_double_f_style_forcecast_noconvert", [](py::array_t) {}, - py::arg("a").noconvert()); + "a"_a.noconvert()); } diff --git a/pybind11/tests/test_pickling.cpp b/pybind11/tests/test_pickling.cpp index 9dc63bd..1a48595 100644 --- a/pybind11/tests/test_pickling.cpp +++ b/pybind11/tests/test_pickling.cpp @@ -31,7 +31,8 @@ TEST_SUBMODULE(pickling, m) { using Pickleable::Pickleable; }; - py::class_(m, "Pickleable") + py::class_ pyPickleable(m, "Pickleable"); + pyPickleable .def(py::init()) .def("value", &Pickleable::value) .def("extra1", &Pickleable::extra1) @@ -43,17 +44,20 @@ TEST_SUBMODULE(pickling, m) { .def("__getstate__", [](const Pickleable &p) { /* Return a tuple that fully encodes the state of the object */ return py::make_tuple(p.value(), p.extra1(), p.extra2()); - }) - .def("__setstate__", [](Pickleable &p, py::tuple t) { - if (t.size() != 3) - throw std::runtime_error("Invalid state!"); - /* Invoke the constructor (need to use in-place version) */ - new (&p) Pickleable(t[0].cast()); - - /* Assign any additional state */ - p.setExtra1(t[1].cast()); - p.setExtra2(t[2].cast()); }); + ignoreOldStyleInitWarnings([&pyPickleable]() { + pyPickleable + .def("__setstate__", [](Pickleable &p, py::tuple t) { + if (t.size() != 3) + throw std::runtime_error("Invalid state!"); + /* Invoke the constructor (need to use in-place version) */ + new (&p) Pickleable(t[0].cast()); + + /* Assign any additional state */ + p.setExtra1(t[1].cast()); + p.setExtra2(t[2].cast()); + }); + }); py::class_(m, "PickleableNew") .def(py::init()) @@ -87,27 +91,31 @@ TEST_SUBMODULE(pickling, m) { using PickleableWithDict::PickleableWithDict; }; - py::class_(m, "PickleableWithDict", py::dynamic_attr()) + py::class_ pyPickleableWithDict(m, "PickleableWithDict", py::dynamic_attr()); + pyPickleableWithDict .def(py::init()) .def_readwrite("value", &PickleableWithDict::value) .def_readwrite("extra", &PickleableWithDict::extra) .def("__getstate__", [](py::object self) { /* Also include __dict__ in state */ return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__")); - }) - .def("__setstate__", [](py::object self, py::tuple t) { - if (t.size() != 3) - throw std::runtime_error("Invalid state!"); - /* Cast and construct */ - auto& p = self.cast(); - new (&p) PickleableWithDict(t[0].cast()); - - /* Assign C++ state */ - p.extra = t[1].cast(); - - /* Assign Python state */ - self.attr("__dict__") = t[2]; }); + ignoreOldStyleInitWarnings([&pyPickleableWithDict]() { + pyPickleableWithDict + .def("__setstate__", [](py::object self, py::tuple t) { + if (t.size() != 3) + throw std::runtime_error("Invalid state!"); + /* Cast and construct */ + auto& p = self.cast(); + new (&p) PickleableWithDict(t[0].cast()); + + /* Assign C++ state */ + p.extra = t[1].cast(); + + /* Assign Python state */ + self.attr("__dict__") = t[2]; + }); + }); py::class_(m, "PickleableWithDictNew") .def(py::init()) diff --git a/pybind11/tests/test_pytypes.cpp b/pybind11/tests/test_pytypes.cpp index 113cf5c..709611d 100644 --- a/pybind11/tests/test_pytypes.cpp +++ b/pybind11/tests/test_pytypes.cpp @@ -254,15 +254,18 @@ TEST_SUBMODULE(pytypes, m) { m.def("convert_to_pybind11_str", [](py::object o) { return py::str(o); }); - m.def("nonconverting_constructor", [](std::string type, py::object value) -> py::object { + m.def("nonconverting_constructor", [](std::string type, py::object value, bool move) -> py::object { if (type == "bytes") { - return py::bytes(value); + return move ? py::bytes(std::move(value)) : py::bytes(value); } else if (type == "none") { - return py::none(value); + return move ? py::none(std::move(value)) : py::none(value); } else if (type == "ellipsis") { - return py::ellipsis(value); + return move ? py::ellipsis(std::move(value)) : py::ellipsis(value); + } + else if (type == "type") { + return move ? py::type(std::move(value)) : py::type(value); } throw std::runtime_error("Invalid type"); }); diff --git a/pybind11/tests/test_pytypes.py b/pybind11/tests/test_pytypes.py index 9e5c302..b1509a0 100644 --- a/pybind11/tests/test_pytypes.py +++ b/pybind11/tests/test_pytypes.py @@ -268,14 +268,16 @@ def test_non_converting_constructors(): ("bytes", range(10)), ("none", 42), ("ellipsis", 42), + ("type", 42), ] for t, v in non_converting_test_cases: - with pytest.raises(TypeError) as excinfo: - m.nonconverting_constructor(t, v) - expected_error = "Object of type '{}' is not an instance of '{}'".format( - type(v).__name__, t - ) - assert str(excinfo.value) == expected_error + for move in [True, False]: + with pytest.raises(TypeError) as excinfo: + m.nonconverting_constructor(t, v, move) + expected_error = "Object of type '{}' is not an instance of '{}'".format( + type(v).__name__, t + ) + assert str(excinfo.value) == expected_error def test_pybind11_str_raw_str(): diff --git a/pybind11/tests/test_smart_ptr.cpp b/pybind11/tests/test_smart_ptr.cpp index 60c2e69..59996ed 100644 --- a/pybind11/tests/test_smart_ptr.cpp +++ b/pybind11/tests/test_smart_ptr.cpp @@ -8,8 +8,8 @@ BSD-style license that can be found in the LICENSE file. */ -#if defined(_MSC_VER) && _MSC_VER < 1910 -# pragma warning(disable: 4702) // unreachable code in system header +#if defined(_MSC_VER) && _MSC_VER < 1910 // VS 2015's MSVC +# pragma warning(disable: 4702) // unreachable code in system header (xatomic.h(382)) #endif #include "pybind11_tests.h" @@ -176,33 +176,63 @@ TEST_SUBMODULE(smart_ptr, m) { // test_unique_nodelete // Object with a private destructor + class MyObject4; + static std::unordered_set myobject4_instances; class MyObject4 { public: - MyObject4(int value) : value{value} { print_created(this); } + MyObject4(int value) : value{value} { + print_created(this); + myobject4_instances.insert(this); + } int value; + + static void cleanupAllInstances() { + auto tmp = std::move(myobject4_instances); + myobject4_instances.clear(); + for (auto o : tmp) + delete o; + } private: - ~MyObject4() { print_destroyed(this); } + ~MyObject4() { + myobject4_instances.erase(this); + print_destroyed(this); + } }; py::class_>(m, "MyObject4") .def(py::init()) - .def_readwrite("value", &MyObject4::value); + .def_readwrite("value", &MyObject4::value) + .def_static("cleanup_all_instances", &MyObject4::cleanupAllInstances); // test_unique_deleter // Object with std::unique_ptr where D is not matching the base class // Object with a protected destructor + class MyObject4a; + static std::unordered_set myobject4a_instances; class MyObject4a { public: MyObject4a(int i) { value = i; print_created(this); + myobject4a_instances.insert(this); }; int value; + + static void cleanupAllInstances() { + auto tmp = std::move(myobject4a_instances); + myobject4a_instances.clear(); + for (auto o : tmp) + delete o; + } protected: - virtual ~MyObject4a() { print_destroyed(this); } + virtual ~MyObject4a() { + myobject4a_instances.erase(this); + print_destroyed(this); + } }; py::class_>(m, "MyObject4a") .def(py::init()) - .def_readwrite("value", &MyObject4a::value); + .def_readwrite("value", &MyObject4a::value) + .def_static("cleanup_all_instances", &MyObject4a::cleanupAllInstances); // Object derived but with public destructor and no Deleter in default holder class MyObject4b : public MyObject4a { diff --git a/pybind11/tests/test_smart_ptr.py b/pybind11/tests/test_smart_ptr.py index c55bffb..85f61a3 100644 --- a/pybind11/tests/test_smart_ptr.py +++ b/pybind11/tests/test_smart_ptr.py @@ -125,7 +125,9 @@ def test_unique_nodelete(): cstats = ConstructorStats.get(m.MyObject4) assert cstats.alive() == 1 del o - assert cstats.alive() == 1 # Leak, but that's intentional + assert cstats.alive() == 1 + m.MyObject4.cleanup_all_instances() + assert cstats.alive() == 0 def test_unique_nodelete4a(): @@ -134,19 +136,25 @@ def test_unique_nodelete4a(): cstats = ConstructorStats.get(m.MyObject4a) assert cstats.alive() == 1 del o - assert cstats.alive() == 1 # Leak, but that's intentional + assert cstats.alive() == 1 + m.MyObject4a.cleanup_all_instances() + assert cstats.alive() == 0 def test_unique_deleter(): + m.MyObject4a(0) o = m.MyObject4b(23) assert o.value == 23 cstats4a = ConstructorStats.get(m.MyObject4a) - assert cstats4a.alive() == 2 # Two because of previous test + assert cstats4a.alive() == 2 cstats4b = ConstructorStats.get(m.MyObject4b) assert cstats4b.alive() == 1 del o - assert cstats4a.alive() == 1 # Should now only be one leftover from previous test + assert cstats4a.alive() == 1 # Should now only be one leftover assert cstats4b.alive() == 0 # Should be deleted + m.MyObject4a.cleanup_all_instances() + assert cstats4a.alive() == 0 + assert cstats4b.alive() == 0 def test_large_holder(): diff --git a/pybind11/tests/test_stl_binders.cpp b/pybind11/tests/test_stl_binders.cpp index 1c0df98..c791477 100644 --- a/pybind11/tests/test_stl_binders.cpp +++ b/pybind11/tests/test_stl_binders.cpp @@ -86,13 +86,13 @@ TEST_SUBMODULE(stl_binders, m) { // test_noncopyable_containers py::bind_vector>(m, "VectorENC"); - m.def("get_vnc", &one_to_n>, py::return_value_policy::reference); + m.def("get_vnc", &one_to_n>); py::bind_vector>(m, "DequeENC"); - m.def("get_dnc", &one_to_n>, py::return_value_policy::reference); + m.def("get_dnc", &one_to_n>); py::bind_map>(m, "MapENC"); - m.def("get_mnc", ×_ten>, py::return_value_policy::reference); + m.def("get_mnc", ×_ten>); py::bind_map>(m, "UmapENC"); - m.def("get_umnc", ×_ten>, py::return_value_policy::reference); + m.def("get_umnc", ×_ten>); // Issue #1885: binding nested std::map> with E non-copyable py::bind_map>>(m, "MapVecENC"); m.def("get_nvnc", [](int n) @@ -102,11 +102,11 @@ TEST_SUBMODULE(stl_binders, m) { for (int j = 1; j <= n; j++) (*m)[i].emplace_back(j); return m; - }, py::return_value_policy::reference); + }); py::bind_map>>(m, "MapMapENC"); - m.def("get_nmnc", ×_hundred>>, py::return_value_policy::reference); + m.def("get_nmnc", ×_hundred>>); py::bind_map>>(m, "UmapUmapENC"); - m.def("get_numnc", ×_hundred>>, py::return_value_policy::reference); + m.def("get_numnc", ×_hundred>>); // test_vector_buffer py::bind_vector>(m, "VectorUChar", py::buffer_protocol()); diff --git a/pybind11/tests/test_virtual_functions.py b/pybind11/tests/test_virtual_functions.py index ae19930..f7d3bd1 100644 --- a/pybind11/tests/test_virtual_functions.py +++ b/pybind11/tests/test_virtual_functions.py @@ -251,8 +251,7 @@ def dispatch(self): == 'Tried to call pure virtual function "Base::dispatch"' ) - p = PyClass1() - return m.dispatch_issue_go(p) + return m.dispatch_issue_go(PyClass1()) b = PyClass2() assert m.dispatch_issue_go(b) == "Yay.." diff --git a/pybind11/tests/valgrind-numpy-scipy.supp b/pybind11/tests/valgrind-numpy-scipy.supp new file mode 100644 index 0000000..60e9f47 --- /dev/null +++ b/pybind11/tests/valgrind-numpy-scipy.supp @@ -0,0 +1,118 @@ +# Valgrind suppression file for NumPy & SciPy errors and leaks in pybind11 tests + +{ + Leaks when importing NumPy + Memcheck:Leak + fun:malloc + fun:_PyMem_RawMalloc + fun:PyObject_Malloc + fun:_PyObject_GC_Alloc + fun:_PyObject_GC_Malloc + fun:_PyObject_GC_NewVar + fun:tuple_alloc + fun:PyTuple_Pack + ... + fun:__pyx_pymod_exec_* +} + +{ + Leaks when importing NumPy (bis) + Memcheck:Leak + fun:malloc + fun:_PyMem_RawMalloc + fun:PyObject_Malloc + fun:_PyObject_New + fun:PyCode_NewWithPosOnlyArgs + fun:PyCode_New + ... + fun:__pyx_pymod_exec_* +} + +{ + Leaks when importing NumPy (tris) + Memcheck:Leak + fun:malloc + fun:_PyMem_RawMalloc + fun:PyObject_Malloc + fun:_PyObject_GC_Alloc + fun:_PyObject_GC_Malloc + fun:_PyObject_GC_NewVar + fun:tuple_alloc + fun:_PyTuple_FromArray + fun:_PyObject_MakeTpCall + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:function_code_fastcall + fun:_PyFunction_Vectorcall +} + +{ + Leaks when importing NumPy (quater) + Memcheck:Leak + fun:malloc + fun:_PyMem_RawMalloc + fun:PyObject_Malloc + fun:_PyObject_GC_Alloc + fun:_PyObject_GC_Malloc + fun:_PyObject_GC_NewVar + fun:tuple_alloc + fun:PyTuple_New + fun:r_object + fun:r_object + fun:r_object + fun:r_object +} + +{ + Leaks when importing NumPy (quinquies) + Memcheck:Leak + fun:malloc + fun:_PyMem_RawMalloc + fun:PyObject_Malloc + fun:_PyObject_GC_Alloc + fun:_PyObject_GC_Malloc + fun:_PyObject_GC_NewVar + fun:tuple_alloc + fun:PyTuple_New + fun:dictiter_iternextitem + fun:list_extend + fun:_PyList_Extend + fun:PySequence_List +} + +{ + Leak when importing scipy.fft + Memcheck:Leak + fun:_Znwm + fun:PyInit_pypocketfft + fun:_PyImport_LoadDynamicModuleWithSpec + fun:_imp_create_dynamic_impl.constprop.3 + fun:_imp_create_dynamic + fun:cfunction_vectorcall_FASTCALL + fun:PyVectorcall_Call + fun:_PyObject_Call + fun:PyObject_Call + fun:do_call_core + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_EvalCode +} + +{ + NumPy leaks when spawning a subprocess + Memcheck:Leak + fun:malloc + ... + fun:_buffer_get_info + fun:array_getbuffer + fun:PyObject_GetBuffer + fun:__Pyx__GetBufferAndValidate* + fun:__pyx_f_5numpy_6random_13bit_generator_12SeedSequence_mix_entropy + fun:__pyx_pw_5numpy_6random_13bit_generator_12SeedSequence_1__init__ + fun:type_call + fun:__Pyx__PyObject_CallOneArg + fun:__pyx_pw_5numpy_6random_13bit_generator_12BitGenerator_1__init__ +} diff --git a/pybind11/tests/valgrind-python.supp b/pybind11/tests/valgrind-python.supp new file mode 100644 index 0000000..1dd04fa --- /dev/null +++ b/pybind11/tests/valgrind-python.supp @@ -0,0 +1,135 @@ +# Valgrind suppression file for CPython errors and leaks in pybind11 tests + +# Taken verbatim from https://github.com/python/cpython/blob/3.9/Misc/valgrind-python.supp#L266-L272 +{ + Uninitialised byte(s) false alarm, see bpo-35561 + Memcheck:Param + epoll_ctl(event) + fun:epoll_ctl + fun:pyepoll_internal_ctl +} + +{ + Python leaks when spawning a subprocess + Memcheck:Leak + fun:malloc + fun:_PyMem_RawMalloc + fun:PyMem_RawMalloc + fun:PyThread_allocate_lock + fun:_PyEval_InitState + fun:PyInterpreterState_New + ... + fun:pyinit_core* + fun:Py_InitializeFromConfig + fun:pymain_init + fun:pymain_main +} + +{ + Python leaks when spawning a subprocess + Memcheck:Leak + fun:malloc + fun:_PyMem_RawMalloc + fun:_PyMem_DebugRawAlloc + fun:_PyMem_DebugRawMalloc + fun:PyMem_RawMalloc + fun:PyThread_allocate_lock + fun:_PyRuntimeState_Init_impl + fun:_PyRuntimeState_Init + fun:_PyRuntime_Initialize + fun:pymain_init + fun:pymain_main + fun:Py_BytesMain +} + +{ + Python leaks when spawning a subprocess + Memcheck:Leak + fun:malloc + fun:_PyMem_RawMalloc + fun:PyMem_RawMalloc + fun:PyThread_allocate_lock + fun:_PyImport_AcquireLock + fun:_imp_acquire_lock_impl* + fun:_imp_acquire_lock + fun:cfunction_vectorcall_NOARGS + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:function_code_fastcall +} + +{ + Python leaks when spawning a subprocess + Memcheck:Leak + fun:malloc + fun:_PyMem_RawMalloc + fun:PyMem_RawMalloc + fun:PyThread_allocate_lock + fun:newlockobject + ... + fun:cfunction_vectorcall_NOARGS + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:function_code_fastcall + fun:_PyFunction_Vectorcall +} + +{ + Python leaks when spawning a subprocess + Memcheck:Leak + fun:malloc + fun:_PyMem_RawMalloc + fun:PyMem_RawMalloc + fun:PyThread_allocate_lock + fun:rlock_new + fun:type_call + fun:_PyObject_Call + fun:PyObject_Call + fun:do_call_core + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_EvalCode + fun:_PyFunction_Vectorcall +} + +# Not really CPython-specific, see link +{ + dlopen leak (https://stackoverflow.com/questions/1542457/memory-leak-reported-by-valgrind-in-dlopen) + Memcheck:Leak + fun:malloc + ... + fun:dl_open_worker + fun:_dl_catch_exception + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_exception + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.2.5 + fun:_PyImport_FindSharedFuncptr + fun:_PyImport_LoadDynamicModuleWithSpec +} + +# Not really CPython-specific, see link +{ + dlopen leak (https://stackoverflow.com/questions/1542457/memory-leak-reported-by-valgrind-in-dlopen) + Memcheck:Leak + fun:malloc + ... + fun:dl_open_worker + fun:_dl_catch_exception + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_exception + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.2.5 + fun:_PyImport_FindSharedFuncptr + fun:_PyImport_LoadDynamicModuleWithSpec +} diff --git a/pybind11/tools/FindEigen3.cmake b/pybind11/tools/FindEigen3.cmake index 98ab43d..83625d9 100644 --- a/pybind11/tools/FindEigen3.cmake +++ b/pybind11/tools/FindEigen3.cmake @@ -64,6 +64,9 @@ if(EIGEN3_INCLUDE_DIR) set(EIGEN3_FOUND ${EIGEN3_VERSION_OK}) else(EIGEN3_INCLUDE_DIR) + if(NOT DEFINED KDE4_INCLUDE_DIR) + set(KDE4_INCLUDE_DIR "") + endif() find_path( EIGEN3_INCLUDE_DIR diff --git a/pybind11/tools/FindPythonLibsNew.cmake b/pybind11/tools/FindPythonLibsNew.cmake index b712591..3605aeb 100644 --- a/pybind11/tools/FindPythonLibsNew.cmake +++ b/pybind11/tools/FindPythonLibsNew.cmake @@ -57,6 +57,8 @@ endif() if(PythonLibsNew_FIND_QUIETLY) set(_pythonlibs_quiet QUIET) +else() + set(_pythonlibs_quiet "") endif() if(PythonLibsNew_FIND_REQUIRED) diff --git a/pybind11/tools/make_changelog.py b/pybind11/tools/make_changelog.py new file mode 100755 index 0000000..609ce2f --- /dev/null +++ b/pybind11/tools/make_changelog.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import re + +import ghapi.all + +from rich import print +from rich.syntax import Syntax + + +ENTRY = re.compile( + r""" + Suggested \s changelog \s entry: + .* + ```rst + \s* + (.*?) + \s* + ``` +""", + re.DOTALL | re.VERBOSE, +) + +print() + + +api = ghapi.all.GhApi(owner="pybind", repo="pybind11") + +issues = api.issues.list_for_repo(labels="needs changelog", state="closed") +missing = [] + +for issue in issues: + changelog = ENTRY.findall(issue.body) + if changelog: + (msg,) = changelog + if not msg.startswith("* "): + msg = "* " + msg + if not msg.endswith("."): + msg += "." + + msg += f"\n `#{issue.number} <{issue.html_url}>`_" + + print(Syntax(msg, "rst", theme="ansi_light")) + print() + + else: + missing.append(issue) + +if missing: + print() + print("[blue]" + "-" * 30) + print() + + for issue in missing: + print(f"[red bold]Missing:[/red bold][red] {issue.title}") + print(f"[red] {issue.html_url}\n") + + print("[bold]Template:\n") + msg = "## Suggested changelog entry:\n\n```rst\n\n```" + print(Syntax(msg, "md", theme="ansi_light")) + +print() diff --git a/pybind11/tools/pybind11Config.cmake.in b/pybind11/tools/pybind11Config.cmake.in index 9808f3d..9921aeb 100644 --- a/pybind11/tools/pybind11Config.cmake.in +++ b/pybind11/tools/pybind11Config.cmake.in @@ -87,7 +87,7 @@ you can either use the basic targets, or use the FindPython tools: target_link_libraries(MyModule2 pybind11::headers) set_target_properties(MyModule2 PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON - CXX__VISIBILITY_PRESET ON + CXX_VISIBILITY_PRESET ON VISIBLITY_INLINES_HIDDEN ON) If you build targets yourself, you may be interested in stripping the output diff --git a/pybind11/tools/pybind11NewTools.cmake b/pybind11/tools/pybind11NewTools.cmake index 357cc61..18da8be 100644 --- a/pybind11/tools/pybind11NewTools.cmake +++ b/pybind11/tools/pybind11NewTools.cmake @@ -12,6 +12,8 @@ get_property( if(pybind11_FIND_QUIETLY) set(_pybind11_quiet QUIET) +else() + set(_pybind11_quiet "") endif() if(CMAKE_VERSION VERSION_LESS 3.12) @@ -127,10 +129,20 @@ endif() # Check on every access - since Python2 and Python3 could have been used - do nothing in that case. if(DEFINED ${_Python}_INCLUDE_DIRS) + # Only add Python for build - must be added during the import for config + # since it has to be re-discovered. + # + # This needs to be a target to be included after the local pybind11 + # directory, just in case there there is an installed pybind11 sitting + # next to Python's includes. It also ensures Python is a SYSTEM library. + add_library(pybind11::python_headers INTERFACE IMPORTED) + set_property( + TARGET pybind11::python_headers PROPERTY INTERFACE_INCLUDE_DIRECTORIES + "$") set_property( TARGET pybind11::pybind11 APPEND - PROPERTY INTERFACE_INCLUDE_DIRECTORIES $) + PROPERTY INTERFACE_LINK_LIBRARIES pybind11::python_headers) set(pybind11_INCLUDE_DIRS "${pybind11_INCLUDE_DIR}" "${${_Python}_INCLUDE_DIRS}" CACHE INTERNAL "Directories where pybind11 and possibly Python headers are located") @@ -144,11 +156,11 @@ if(DEFINED ${_Python}_VERSION AND ${_Python}_VERSION VERSION_LESS 3) endif() # In CMake 3.18+, you can find these separately, so include an if -if(TARGET ${_Python}::${_Python}) +if(TARGET ${_Python}::Python) set_property( TARGET pybind11::embed APPEND - PROPERTY INTERFACE_LINK_LIBRARIES ${_Python}::${_Python}) + PROPERTY INTERFACE_LINK_LIBRARIES ${_Python}::Python) endif() # CMake 3.15+ has this @@ -170,27 +182,27 @@ function(pybind11_add_module target_name) cmake_parse_arguments(PARSE_ARGV 1 ARG "STATIC;SHARED;MODULE;THIN_LTO;OPT_SIZE;NO_EXTRAS;WITHOUT_SOABI" "" "") - if(ARG_ADD_LIBRARY_STATIC) - set(type STATIC) - elseif(ARG_ADD_LIBRARY_SHARED) - set(type SHARED) + if(ARG_STATIC) + set(lib_type STATIC) + elseif(ARG_SHARED) + set(lib_type SHARED) else() - set(type MODULE) + set(lib_type MODULE) endif() if("${_Python}" STREQUAL "Python") - python_add_library(${target_name} ${type} ${ARG_UNPARSED_ARGUMENTS}) + python_add_library(${target_name} ${lib_type} ${ARG_UNPARSED_ARGUMENTS}) elseif("${_Python}" STREQUAL "Python3") - python3_add_library(${target_name} ${type} ${ARG_UNPARSED_ARGUMENTS}) + python3_add_library(${target_name} ${lib_type} ${ARG_UNPARSED_ARGUMENTS}) elseif("${_Python}" STREQUAL "Python2") - python2_add_library(${target_name} ${type} ${ARG_UNPARSED_ARGUMENTS}) + python2_add_library(${target_name} ${lib_type} ${ARG_UNPARSED_ARGUMENTS}) else() message(FATAL_ERROR "Cannot detect FindPython version: ${_Python}") endif() target_link_libraries(${target_name} PRIVATE pybind11::headers) - if(type STREQUAL "MODULE") + if(lib_type STREQUAL "MODULE") target_link_libraries(${target_name} PRIVATE pybind11::module) else() target_link_libraries(${target_name} PRIVATE pybind11::embed) @@ -204,12 +216,21 @@ function(pybind11_add_module target_name) target_link_libraries(${target_name} PRIVATE pybind11::python2_no_register) endif() - set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden" - CUDA_VISIBILITY_PRESET "hidden") + # -fvisibility=hidden is required to allow multiple modules compiled against + # different pybind versions to work properly, and for some features (e.g. + # py::module_local). We force it on everything inside the `pybind11` + # namespace; also turning it on for a pybind module compilation here avoids + # potential warnings or issues from having mixed hidden/non-hidden types. + if(NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET) + set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden") + endif() + + if(NOT DEFINED CMAKE_CUDA_VISIBILITY_PRESET) + set_target_properties(${target_name} PROPERTIES CUDA_VISIBILITY_PRESET "hidden") + endif() # If we don't pass a WITH_SOABI or WITHOUT_SOABI, use our own default handling of extensions - if("${type}" STREQUAL "MODULE" AND (NOT ARG_WITHOUT_SOABI OR NOT "WITH_SOABI" IN_LIST - ARG_UNPARSED_ARGUMENTS)) + if(NOT ARG_WITHOUT_SOABI OR NOT "WITH_SOABI" IN_LIST ARG_UNPARSED_ARGUMENTS) pybind11_extension(${target_name}) endif() diff --git a/pybind11/tools/pybind11Tools.cmake b/pybind11/tools/pybind11Tools.cmake index 23cff98..3231353 100644 --- a/pybind11/tools/pybind11Tools.cmake +++ b/pybind11/tools/pybind11Tools.cmake @@ -10,6 +10,8 @@ include(CMakeParseArguments) if(pybind11_FIND_QUIETLY) set(_pybind11_quiet QUIET) +else() + set(_pybind11_quiet "") endif() # If this is the first run, PYTHON_VERSION can stand in for PYBIND11_PYTHON_VERSION @@ -22,16 +24,21 @@ if(NOT DEFINED PYBIND11_PYTHON_VERSION AND DEFINED PYTHON_VERSION) CACHE STRING "Python version to use for compiling modules") unset(PYTHON_VERSION) unset(PYTHON_VERSION CACHE) -else() - # If this is set as a normal variable, promote it, otherwise, make an empty cache variable. +elseif(DEFINED PYBIND11_PYTHON_VERSION) + # If this is set as a normal variable, promote it set(PYBIND11_PYTHON_VERSION "${PYBIND11_PYTHON_VERSION}" CACHE STRING "Python version to use for compiling modules") +else() + # Make an empty cache variable. + set(PYBIND11_PYTHON_VERSION + "" + CACHE STRING "Python version to use for compiling modules") endif() # A user can set versions manually too set(Python_ADDITIONAL_VERSIONS - "3.9;3.8;3.7;3.6;3.5;3.4" + "3.10;3.9;3.8;3.7;3.6;3.5;3.4" CACHE INTERNAL "") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") @@ -81,11 +88,19 @@ if(PYBIND11_MASTER_PROJECT) endif() endif() -# Only add Python for build - must be added during the import for config since it has to be re-discovered. +# Only add Python for build - must be added during the import for config since +# it has to be re-discovered. +# +# This needs to be an target to it is included after the local pybind11 +# directory, just in case there are multiple versions of pybind11, we want the +# one we expect. +add_library(pybind11::python_headers INTERFACE IMPORTED) +set_property(TARGET pybind11::python_headers PROPERTY INTERFACE_INCLUDE_DIRECTORIES + "$") set_property( TARGET pybind11::pybind11 APPEND - PROPERTY INTERFACE_INCLUDE_DIRECTORIES $) + PROPERTY INTERFACE_LINK_LIBRARIES pybind11::python_headers) set(pybind11_INCLUDE_DIRS "${pybind11_INCLUDE_DIR}" "${PYTHON_INCLUDE_DIRS}" @@ -166,8 +181,13 @@ function(pybind11_add_module target_name) # py::module_local). We force it on everything inside the `pybind11` # namespace; also turning it on for a pybind module compilation here avoids # potential warnings or issues from having mixed hidden/non-hidden types. - set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden" - CUDA_VISIBILITY_PRESET "hidden") + if(NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET) + set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden") + endif() + + if(NOT DEFINED CMAKE_CUDA_VISIBILITY_PRESET) + set_target_properties(${target_name} PROPERTIES CUDA_VISIBILITY_PRESET "hidden") + endif() if(ARG_NO_EXTRAS) return()