diff --git a/.appveyor.yml b/.appveyor.yml index 8fbb726108..b33a4ccf4e 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,64 +1,32 @@ version: 1.0.{build} image: -- Visual Studio 2017 - Visual Studio 2015 test: off skip_branch_with_pr: true build: parallel: true platform: -- x64 - x86 environment: matrix: - PYTHON: 36 - CPP: 14 CONFIG: Debug - PYTHON: 27 - CPP: 14 CONFIG: Debug - - CONDA: 36 - CPP: latest - CONFIG: Release -matrix: - exclude: - - image: Visual Studio 2015 - platform: x86 - - image: Visual Studio 2015 - CPP: latest - - image: Visual Studio 2017 - CPP: latest - platform: x86 install: - ps: | - if ($env:PLATFORM -eq "x64") { $env:CMAKE_ARCH = "x64" } - if ($env:APPVEYOR_JOB_NAME -like "*Visual Studio 2017*") { - $env:CMAKE_GENERATOR = "Visual Studio 15 2017" - $env:CMAKE_INCLUDE_PATH = "C:\Libraries\boost_1_64_0" - $env:CXXFLAGS = "-permissive-" - } else { - $env:CMAKE_GENERATOR = "Visual Studio 14 2015" - } - if ($env:PYTHON) { - 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 - } elseif ($env:CONDA) { - if ($env:CONDA -eq "27") { $env:CONDA = "" } - if ($env:PLATFORM -eq "x64") { $env:CONDA = "$env:CONDA-x64" } - $env:PATH = "C:\Miniconda$env:CONDA\;C:\Miniconda$env:CONDA\Scripts\;$env:PATH" - $env:PYTHONHOME = "C:\Miniconda$env:CONDA" - conda --version - conda install -y -q pytest numpy scipy - } + $env:CMAKE_GENERATOR = "Visual Studio 14 2015" + 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 - ps: | Start-FileDownload 'http://bitbucket.org/eigen/eigen/get/3.3.3.zip' 7z x 3.3.3.zip -y > $null $env:CMAKE_INCLUDE_PATH = "eigen-eigen-67e894c6cd8f;$env:CMAKE_INCLUDE_PATH" build_script: - cmake -G "%CMAKE_GENERATOR%" -A "%CMAKE_ARCH%" - -DPYBIND11_CPP_STANDARD=/std:c++%CPP% + -DCMAKE_CXX_STANDARD=14 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DCMAKE_SUPPRESS_REGENERATION=1 @@ -66,5 +34,4 @@ build_script: - set MSBuildLogger="C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" - cmake --build . --config %CONFIG% --target pytest -- /m /v:m /logger:%MSBuildLogger% - cmake --build . --config %CONFIG% --target cpptest -- /m /v:m /logger:%MSBuildLogger% -- if "%CPP%"=="latest" (cmake --build . --config %CONFIG% --target test_cmake_build -- /m /v:m /logger:%MSBuildLogger%) on_failure: if exist "tests\test_cmake_build" type tests\test_cmake_build\*.log* diff --git a/.cmake-format.yaml b/.cmake-format.yaml new file mode 100644 index 0000000000..a2a69f3f89 --- /dev/null +++ b/.cmake-format.yaml @@ -0,0 +1,73 @@ +parse: + additional_commands: + pybind11_add_module: + flags: + - THIN_LTO + - MODULE + - SHARED + - NO_EXTRAS + - EXCLUDE_FROM_ALL + - SYSTEM + +format: + line_width: 99 + tab_size: 2 + + # If an argument group contains more than this many sub-groups + # (parg or kwarg groups) then force it to a vertical layout. + max_subgroups_hwrap: 2 + + # If a positional argument group contains more than this many + # arguments, then force it to a vertical layout. + max_pargs_hwrap: 6 + + # If a cmdline positional group consumes more than this many + # lines without nesting, then invalidate the layout (and nest) + max_rows_cmdline: 2 + separate_ctrl_name_with_space: false + separate_fn_name_with_space: false + dangle_parens: false + + # If the trailing parenthesis must be 'dangled' on its on + # 'line, then align it to this reference: `prefix`: the start' + # 'of the statement, `prefix-indent`: the start of the' + # 'statement, plus one indentation level, `child`: align to' + # the column of the arguments + dangle_align: prefix + # If the statement spelling length (including space and + # parenthesis) is smaller than this amount, then force reject + # nested layouts. + min_prefix_chars: 4 + + # If the statement spelling length (including space and + # parenthesis) is larger than the tab width by more than this + # amount, then force reject un-nested layouts. + max_prefix_chars: 10 + + # If a candidate layout is wrapped horizontally but it exceeds + # this many lines, then reject the layout. + max_lines_hwrap: 2 + + line_ending: unix + + # Format command names consistently as 'lower' or 'upper' case + command_case: canonical + + # Format keywords consistently as 'lower' or 'upper' case + # unchanged is valid too + keyword_case: 'upper' + + # A list of command names which should always be wrapped + always_wrap: [] + + # If true, the argument lists which are known to be sortable + # will be sorted lexicographically + enable_sort: true + + # If true, the parsers may infer whether or not an argument + # list is sortable (without annotation). + autosort: false + +# Causes a few issues - can be solved later, possibly. +markup: + enable_markup: false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ba983112a..762fafca2f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,10 +8,329 @@ on: - drake jobs: - # Will be replaced by part of upstream's CI. - placeholder: - name: placeholder + standard: + strategy: + fail-fast: false + matrix: + # runs-on: [ubuntu-latest, windows-latest, macos-latest] + runs-on: [ubuntu-latest, macos-latest] + arch: [x64] + max-cxx-std: [17] + python: + # - 2.7 + # - 3.5 + - 3.7 + - 3.8 + - 3.9-dev + # - pypy2 + # - pypy3 + + include: + - runs-on: ubuntu-latest + python: 3.6 + arch: x64 + max-cxx-std: 17 + - runs-on: macos-latest + python: 3.7 + arch: x64 + max-cxx-std: 17 + # - runs-on: windows-2016 + # python: 3.7 + # arch: x86 + # max-cxx-std: 14 + + exclude: + # # Currently 32bit only, and we build 64bit + # - runs-on: windows-latest + # python: pypy2 + # arch: x64 + # max-cxx-std: 17 + # - runs-on: windows-latest + # python: pypy3 + # arch: x64 + # max-cxx-std: 17 + + # Currently can't build due to warning, fixed in CPython > 3.9b5 + - runs-on: macos-latest + python: 3.9-dev + arch: x64 + max-cxx-std: 17 + + # # Currently broken on embed_test + # - runs-on: windows-latest + # python: 3.8 + # arch: x64 + # max-cxx-std: 17 + # - runs-on: windows-latest + # python: 3.9-dev + # arch: x64 + # max-cxx-std: 17 + + name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • ${{ matrix.arch }}" + runs-on: ${{ matrix.runs-on }} + + steps: + - uses: actions/checkout@v2 + + - name: Setup Python ${{ matrix.python }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + architecture: ${{ matrix.arch }} + + - name: Cache wheels + if: startsWith(runner.os, 'macOS') + uses: actions/cache@v2 + with: + # This path is specific to macOS - we really only need it for PyPy NumPy wheels + # See https://github.com/actions/cache/blob/master/examples.md#python---pip + # for ways to do this more generally + path: ~/Library/Caches/pip + # Look to see if there is a cache hit for the corresponding requirements file + key: ${{ runner.os }}-pip-${{ matrix.python }}-${{ matrix.arch }}-${{ hashFiles('tests/requirements.txt') }} + + - name: Prepare env + run: python -m pip install -r tests/requirements.txt + + - name: Configure C++11 + shell: bash + run: > + cmake -S . -B build + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DDOWNLOAD_EIGEN=ON + -DCMAKE_CXX_STANDARD=11 + -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)") + + - name: Build C++11 + run: cmake --build build -j 2 + + - name: Python tests C++11 + run: cmake --build build --target pytest -j 2 + + - name: C++11 tests + run: cmake --build build --target cpptest -j 2 + + - name: Interface test C++11 + run: cmake --build build --target test_cmake_build + + - name: Configure C++${{ matrix.max-cxx-std }} + shell: bash + run: > + cmake -S . -B build2 + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DDOWNLOAD_EIGEN=ON + -DCMAKE_CXX_STANDARD=${{ matrix.max-cxx-std }} + -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)") + + - name: Build C++${{ matrix.max-cxx-std }} + run: cmake --build build2 -j 2 + + - name: Python tests C++${{ matrix.max-cxx-std }} + run: cmake --build build2 --target pytest + + - name: C++${{ matrix.max-cxx-std }} tests + run: cmake --build build2 --target cpptest + + - name: Interface test C++${{ matrix.max-cxx-std }} + run: cmake --build build2 --target test_cmake_build + + clang: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + clang: + # - 3.6 + # - 3.7 + # - 3.9 + # - 5 + # - 7 + - 9 + - dev + + name: "🐍 3 • Clang ${{ matrix.clang }} • x64" + container: "silkeh/clang:${{ matrix.clang }}" + steps: - - name: checkout - uses: actions/checkout@v2 + - uses: actions/checkout@v2 + + - name: Add wget and python3 + run: apt-get update && apt-get install -y python3-dev python3-numpy python3-pytest libeigen3-dev + + - name: Configure + shell: bash + run: > + cmake -S . -B build + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") + + - name: Build + run: cmake --build build -j 2 + + - name: Python tests + run: cmake --build build --target pytest + + - name: C++ tests + run: cmake --build build --target cpptest + + - name: Interface test + run: cmake --build build --target test_cmake_build + + gcc: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + gcc: + - 7 + - latest + + name: "🐍 3 • GCC ${{ matrix.gcc }} • x64" + container: "gcc:${{ matrix.gcc }}" + + steps: + - uses: actions/checkout@v1 + + - name: Add Python 3 + run: apt-get update; apt-get install -y python3-dev python3-numpy python3-pytest python3-pip libeigen3-dev + + - name: Update pip + run: python3 -m pip install --upgrade pip + + - name: Setup CMake 3.18 + uses: jwlawson/actions-setup-cmake@v1.3 + with: + cmake-version: 3.18 + + - name: Configure + shell: bash + run: > + cmake -S . -B build + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DCMAKE_CXX_STANDARD=11 + -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") + + - name: Build + run: cmake --build build -j 2 + + - name: Python tests + run: cmake --build build --target pytest + + - name: C++ tests + run: cmake --build build --target cpptest + + - name: Interface test + run: cmake --build build --target test_cmake_build + + # centos: + # runs-on: ubuntu-latest + # strategy: + # fail-fast: false + # matrix: + # centos: + # - 7 # GCC 4.8 + # - 8 + + # name: "🐍 3 • CentOS ${{ matrix.centos }} • x64" + # container: "centos:${{ matrix.centos }}" + + # steps: + # - uses: actions/checkout@v2 + + # - name: Add Python 3 + # run: yum update -y && yum install -y python3-devel gcc-c++ make git + + # - name: Update pip + # run: python3 -m pip install --upgrade pip + + # - name: Install dependencies + # run: python3 -m pip install cmake -r tests/requirements.txt + + # - name: Configure + # shell: bash + # run: > + # cmake -S . -B build + # -DPYBIND11_WERROR=ON + # -DDOWNLOAD_CATCH=ON + # -DDOWNLOAD_EIGEN=ON + # -DCMAKE_CXX_STANDARD=11 + # -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") + + # - name: Build + # run: cmake --build build -j 2 + + # - name: Python tests + # run: cmake --build build --target pytest + + # - name: C++ tests + # run: cmake --build build --target cpptest + + # - name: Interface test + # run: cmake --build build --target test_cmake_build + + install-classic: + name: "🐍 3.5 • Debian • x86 • Install" + runs-on: ubuntu-latest + container: i386/debian:stretch + + steps: + - uses: actions/checkout@v1 + + - name: Install requirements + run: | + apt-get update + apt-get install -y git make cmake g++ libeigen3-dev python3-dev python3-pip python3-pytest + + - name: Configure for install + run: cmake -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") -DPYBIND11_INSTALL=1 -DPYBIND11_TEST=0 . + + - name: Make and install + run: make install + + - name: Copy tests to new directory + run: cp -a tests /pybind11-tests + + - name: Make a new test directory + run: mkdir /build-tests + + - name: Configure tests + run: cmake -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") ../pybind11-tests -DPYBIND11_WERROR=ON + working-directory: /build-tests + + - name: Run tests + run: make pytest -j 2 + working-directory: /build-tests + + + doxygen: + name: "Documentation build test" + runs-on: ubuntu-latest + container: alpine:3.12 + + steps: + - uses: actions/checkout@v2 + + - name: Install requirements + run: apk add doxygen python3-dev + + - name: Ensure pip + run: python3 -m ensurepip + + - name: Install python docs requirements + run: python3 -m pip install "sphinx<3" sphinx_rtd_theme breathe==4.13.1 pytest setuptools + + - name: Build docs + run: python3 -m sphinx -W -b html docs docs/.build + + - name: Make SDist + run: python3 setup.py sdist + + - name: Compare Dists (headers only) + run: | + python3 -m pip install --user -U ./dist/* + installed=$(python3 -c "import pybind11; print(pybind11.get_include(True) + '/pybind11')") + diff -rq $installed ./include/pybind11 diff --git a/.github/workflows/configure.yml b/.github/workflows/configure.yml new file mode 100644 index 0000000000..96904341de --- /dev/null +++ b/.github/workflows/configure.yml @@ -0,0 +1,78 @@ +name: Configure + +on: + workflow_dispatch: + pull_request: + push: + branches: + - master + - stable + - v* + +jobs: + cmake: + strategy: + fail-fast: false + matrix: + python: + - 2.7 + - 3.8 + + name: CMake ${{ matrix.cmake }} Python ${{ matrix.python }} on ubuntu + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Setup Python ${{ matrix.python }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + + - name: Prepare env + run: python -m pip install -r tests/requirements.txt + + - name: Make build directories + run: | + mkdir build3.7 + mkdir build3.11 + mkdir build3.18 + + - name: Setup CMake 3.7 + uses: jwlawson/actions-setup-cmake@v1.3 + with: + cmake-version: 3.7 + + - name: Configure 3.7 + working-directory: build3.7 + run: > + cmake .. + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)") + + - name: Setup CMake 3.11 + uses: jwlawson/actions-setup-cmake@v1.3 + with: + cmake-version: 3.11 + + - name: Configure 3.11 + working-directory: build3.11 + run: > + cmake .. + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)") + + - name: Setup CMake 3.18 + uses: jwlawson/actions-setup-cmake@v1.3 + with: + cmake-version: 3.18 + + - name: Configure 3.18 + working-directory: build3.18 + run: > + cmake .. + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)") diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml new file mode 100644 index 0000000000..e92f96e6ef --- /dev/null +++ b/.github/workflows/format.yml @@ -0,0 +1,19 @@ +name: Format + +on: + workflow_dispatch: + pull_request: + push: + branches: + - master + - stable + - "v*" + +jobs: + pre-commit: + name: Format + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - uses: pre-commit/action@v2.0.0 diff --git a/.gitignore b/.gitignore index 244bbcaa7a..54a1f92601 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ CMakeCache.txt CMakeFiles Makefile cmake_install.cmake +cmake_uninstall.cmake .DS_Store *.so *.pyd @@ -32,8 +33,8 @@ MANIFEST .DS_Store /dist /build -/cmake/ .cache/ sosize-*.txt pybind11Config*.cmake pybind11Targets.cmake +/*env* diff --git a/.gitmodules b/.gitmodules index d063a8e89d..4d698f93f8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "tools/clang"] path = tools/clang - url = ../../wjakob/clang-cindex-python3 + url = ../../wjakob/clang-cindex-python3.git diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000..7fb3a13b70 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,42 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.1.0 + hooks: + - id: check-added-large-files + - id: check-case-conflict + - id: check-merge-conflict + - id: check-symlinks + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + - id: mixed-line-ending + - id: requirements-txt-fixer + - id: trailing-whitespace + - id: fix-encoding-pragma + +- repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.1.9 + hooks: + - id: remove-tabs + +- repo: https://gitlab.com/pycqa/flake8 + rev: 3.8.3 + hooks: + - id: flake8 + additional_dependencies: [flake8-bugbear, pep8-naming] + exclude: ^(docs/.*|tools/.*)$ + +- repo: https://github.com/cheshirekow/cmake-format-precommit + rev: v0.6.10 + hooks: + - id: cmake-format + additional_dependencies: [pyyaml] + +- repo: local + hooks: + - id: check-style + name: Classic check-style + language: system + types: + - c++ + entry: ./tools/check-style.sh diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index cd5c80a032..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,336 +0,0 @@ -language: cpp -matrix: - include: - # This config does a few things: - # - Checks C++ and Python code styles (check-style.sh and flake8). - # - Makes sure sphinx can build the docs without any errors or warnings. - # - Tests setup.py sdist and install (all header files should be present). - # - Makes sure that everything still works without optional deps (numpy/scipy/eigen) and - # also tests the automatic discovery functions in CMake (Python version, C++ standard). - - os: linux - dist: xenial # Necessary to run doxygen 1.8.15 - name: Style, docs, and pip - cache: false - before_install: - - pyenv global $(pyenv whence 2to3) # activate all python versions - - PY_CMD=python3 - - $PY_CMD -m pip install --user --upgrade pip wheel setuptools - install: - # breathe 4.14 doesn't work with bit fields. See https://github.com/michaeljones/breathe/issues/462 - # Latest breathe + Sphinx causes warnings and errors out - - $PY_CMD -m pip install --user --upgrade "sphinx<3" sphinx_rtd_theme breathe==4.13.1 flake8 pep8-naming pytest - - curl -fsSL https://sourceforge.net/projects/doxygen/files/rel-1.8.15/doxygen-1.8.15.linux.bin.tar.gz/download | tar xz - - export PATH="$PWD/doxygen-1.8.15/bin:$PATH" - script: - - tools/check-style.sh - - flake8 - # TODO(eric.cousineau): Re-enable doc build at some point once `breathe` is unbroken. - # - $PY_CMD -m sphinx -W -b html docs docs/.build - - | - # Make sure setup.py distributes and installs all the headers - set -ex - $PY_CMD setup.py sdist - $PY_CMD -m pip install --user -U ./dist/* - installed=$($PY_CMD -c "import pybind11; print(pybind11.get_include(True) + '/pybind11')") - diff -rq $installed ./include/pybind11 - - | - # Barebones build - set -ex - cmake -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DPYTHON_EXECUTABLE=$(which $PY_CMD) . - make pytest -j 2 && make cpptest -j 2 - # The following are regular test configurations, including optional dependencies. - # With regard to each other they differ in Python version, C++ standard and compiler. - - os: linux - dist: trusty - name: Python 2.7, c++11, gcc 4.8 - env: PYTHON=2.7 CPP=11 GCC=4.8 - addons: - apt: - packages: - - cmake=2.\* - - cmake-data=2.\* - - os: linux - dist: trusty - name: Python 3.6, c++11, gcc 4.8 - env: PYTHON=3.6 CPP=11 GCC=4.8 - addons: - apt: - sources: - - deadsnakes - packages: - - python3.6-dev - - python3.6-venv - - cmake=2.\* - - cmake-data=2.\* - - os: linux - dist: trusty - env: PYTHON=2.7 CPP=14 GCC=6 CMAKE=1 - name: Python 2.7, c++14, gcc 6, CMake test - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-6 - - os: linux - dist: trusty - name: Python 3.5, c++14, gcc 6, Debug build - # N.B. `ensurepip` could be installed transitively by `python3.5-venv`, but - # seems to have apt conflicts (at least for Trusty). Use Docker instead. - services: docker - env: DOCKER=debian:stretch PYTHON=3.5 CPP=14 GCC=6 DEBUG=1 - - os: linux - dist: xenial - env: PYTHON=3.6 CPP=17 GCC=7 - name: Python 3.6, c++17, gcc 7 - addons: - apt: - sources: - - deadsnakes - - ubuntu-toolchain-r-test - packages: - - g++-7 - - python3.6-dev - - python3.6-venv - - os: linux - dist: xenial - env: PYTHON=3.6 CPP=17 CLANG=7 - name: Python 3.6, c++17, Clang 7 - addons: - apt: - sources: - - deadsnakes - - llvm-toolchain-xenial-7 - packages: - - python3.6-dev - - python3.6-venv - - clang-7 - - libclang-7-dev - - llvm-7-dev - - lld-7 - - libc++-7-dev - - libc++abi-7-dev # Why is this necessary??? - - os: linux - dist: xenial - env: PYTHON=3.8 CPP=17 GCC=7 - name: Python 3.8, c++17, gcc 7 - addons: - apt: - sources: - - deadsnakes - - ubuntu-toolchain-r-test - packages: - - g++-7 - - python3.8-dev - - python3.8-venv - - os: linux - dist: xenial - env: PYTHON=3.9 CPP=17 GCC=7 - name: Python 3.9 beta, c++17, gcc 7 (w/o numpy/scipy) # TODO: update build name when the numpy/scipy wheels become available - addons: - apt: - sources: - - deadsnakes - - ubuntu-toolchain-r-test - packages: - - g++-7 - - python3.9-dev - - python3.9-venv - # Currently there are no numpy/scipy wheels available for python3.9 - # TODO: remove next install and script clause when the wheels become available - install: - - $PY_CMD -m pip install --user --upgrade pytest - script: - - | - set -ex - # Barebones build - cmake -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DPYTHON_EXECUTABLE=$(which $PY_CMD) . - make pytest -j 2 && make cpptest -j 2 - - os: osx - name: Python 2.7, c++14, AppleClang 7.3, CMake test - osx_image: xcode7.3 - env: PYTHON=2.7 CPP=14 CLANG CMAKE=1 - - os: osx - name: Python 3.7, c++14, AppleClang 9, Debug build - osx_image: xcode9.4 - env: PYTHON=3.7 CPP=14 CLANG DEBUG=1 - # Test a PyPy 2.7 build - - os: linux - dist: trusty - env: PYPY=7.3.1 PYTHON=2.7 CPP=11 GCC=4.8 - name: PyPy 7.3, Python 2.7, c++11, gcc 4.8 - addons: - apt: - packages: - - libblas-dev - - liblapack-dev - - gfortran - - os: linux - dist: xenial - env: PYPY=7.3.1 PYTHON=3.6 CPP=11 GCC=5 - name: PyPy 7.3, Python 3.6, c++11, gcc 5 - addons: - apt: - packages: - - libblas-dev - - liblapack-dev - - gfortran - - g++-5 - # Build in 32-bit mode and tests against the CMake-installed version - - os: linux - dist: trusty - services: docker - env: DOCKER=i386/debian:stretch PYTHON=3.5 CPP=14 GCC=6 INSTALL=1 - name: Python 3.5, c++14, gcc 6, 32-bit - script: - - | - # Consolidated 32-bit Docker Build + Install - set -ex - $SCRIPT_RUN_PREFIX sh -c " - set -ex - cmake ${CMAKE_EXTRA_ARGS} -DPYBIND11_INSTALL=1 -DPYBIND11_TEST=0 . - make install - cp -a tests /pybind11-tests - mkdir /build-tests && cd /build-tests - cmake ../pybind11-tests ${CMAKE_EXTRA_ARGS} -DPYBIND11_WERROR=ON - make pytest -j 2" - set +ex - allow_failures: - - name: PyPy 7.3, Python 2.7, c++11, gcc 4.8 - - name: PyPy 7.3, Python 3.6, c++11, gcc 5 - - name: Python 3.9 beta, c++17, gcc 7 (w/o numpy/scipy) -cache: - directories: - - $HOME/.local/bin - - $HOME/.local/lib - - $HOME/.local/include - - $HOME/Library/Python -before_install: -- | - # Configure build variables - set -ex - if [ "$TRAVIS_OS_NAME" = "linux" ]; then - if [ -n "$CLANG" ]; then - export CXX=clang++-$CLANG CC=clang-$CLANG - EXTRA_PACKAGES+=" clang-$CLANG llvm-$CLANG-dev" - else - if [ -z "$GCC" ]; then GCC=4.8 - else EXTRA_PACKAGES+=" g++-$GCC" - fi - export CXX=g++-$GCC CC=gcc-$GCC - fi - elif [ "$TRAVIS_OS_NAME" = "osx" ]; then - export CXX=clang++ CC=clang; - fi - if [ -n "$CPP" ]; then CPP=-std=c++$CPP; fi - if [ "${PYTHON:0:1}" = "3" ]; then PY=3; fi - if [ -n "$DEBUG" ]; then CMAKE_EXTRA_ARGS+=" -DCMAKE_BUILD_TYPE=Debug"; fi - set +ex -- | - # Initialize environment - set -ex - if [ -n "$DOCKER" ]; then - docker pull $DOCKER - - containerid=$(docker run --detach --tty \ - --volume="$PWD":/pybind11 --workdir=/pybind11 \ - --env="CC=$CC" --env="CXX=$CXX" --env="DEBIAN_FRONTEND=$DEBIAN_FRONTEND" \ - --env=GCC_COLORS=\ \ - $DOCKER) - SCRIPT_RUN_PREFIX="docker exec --tty $containerid" - $SCRIPT_RUN_PREFIX sh -c 'for s in 0 15; do sleep $s; apt-get update && apt-get -qy dist-upgrade && break; done' - else - if [ -n "$PYPY" ]; then - curl -fSL https://bitbucket.org/pypy/pypy/downloads/pypy$PYTHON-v$PYPY-linux64.tar.bz2 | tar xj - PY_CMD=$(echo `pwd`/pypy$PYTHON-v$PYPY-linux64/bin/pypy$PY) - CMAKE_EXTRA_ARGS+=" -DPYTHON_EXECUTABLE:FILEPATH=$PY_CMD" - else - PY_CMD=python$PYTHON - if [ "$TRAVIS_OS_NAME" = "osx" ]; then - if [ "$PY" = "3" ]; then - brew update && brew unlink python@2 && brew upgrade python - else - curl -fsSL https://bootstrap.pypa.io/get-pip.py | $PY_CMD - --user - fi - fi - fi - if [ "$PY" = 3 ] || [ -n "$PYPY" ]; then - $PY_CMD -m ensurepip --user - fi - $PY_CMD --version - $PY_CMD -m pip install --user --upgrade pip wheel - fi - set +ex -install: -- | - # Install dependencies - set -ex - cmake --version - if [ -n "$DOCKER" ]; then - if [ -n "$DEBUG" ]; then - PY_DEBUG="python$PYTHON-dbg python$PY-scipy-dbg" - CMAKE_EXTRA_ARGS+=" -DPYTHON_EXECUTABLE=/usr/bin/python${PYTHON}dm" - fi - $SCRIPT_RUN_PREFIX sh -c "for s in 0 15; do sleep \$s; \ - apt-get -qy --no-install-recommends install \ - $PY_DEBUG python$PYTHON-dev python$PY-pytest python$PY-scipy \ - libeigen3-dev libboost-dev cmake make ${EXTRA_PACKAGES} && break; done" - else - - if [ "$CLANG" = "7" ]; then - export CXXFLAGS="-stdlib=libc++" - fi - - export NPY_NUM_BUILD_JOBS=2 - echo "Installing pytest, numpy, scipy..." - local PIP_CMD="" - if [ -n "$PYPY" ]; then - # For expediency, install only versions that are available on the extra index. - travis_wait 30 \ - $PY_CMD -m pip install --user --upgrade --extra-index-url https://antocuni.github.io/pypy-wheels/manylinux2010 \ - numpy scipy - $PY_CMD -m pip install --user --upgrade pytest - else - $PY_CMD -m pip install --user --upgrade pytest numpy scipy - fi - echo "done." - - mkdir eigen - curl -fsSL https://bitbucket.org/eigen/eigen/get/3.3.4.tar.bz2 | \ - tar --extract -j --directory=eigen --strip-components=1 - export CMAKE_INCLUDE_PATH="${CMAKE_INCLUDE_PATH:+$CMAKE_INCLUDE_PATH:}$PWD/eigen" - fi - set +ex -script: -- | - # CMake Configuration - set -ex - $SCRIPT_RUN_PREFIX cmake ${CMAKE_EXTRA_ARGS} \ - -DPYBIND11_PYTHON_VERSION=$PYTHON \ - -DPYBIND11_CPP_STANDARD=$CPP \ - -DPYBIND11_WERROR=${WERROR:-ON} \ - -DDOWNLOAD_CATCH=${DOWNLOAD_CATCH:-ON} \ - . - set +ex -- | - # pytest - set -ex - $SCRIPT_RUN_PREFIX make pytest -j 2 VERBOSE=1 - set +ex -- | - # cpptest - set -ex - $SCRIPT_RUN_PREFIX make cpptest -j 2 - set +ex -- | - # CMake Build Interface - set -ex - if [ -n "$CMAKE" ]; then $SCRIPT_RUN_PREFIX make test_cmake_build; fi - set +ex -after_failure: cat tests/test_cmake_build/*.log* -after_script: -- | - # Cleanup (Docker) - set -ex - if [ -n "$DOCKER" ]; then docker stop "$containerid"; docker rm "$containerid"; fi - set +ex diff --git a/CMakeLists.txt b/CMakeLists.txt index 85ecd9028f..7aa8ed72c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,153 +5,271 @@ # All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.7) -if (POLICY CMP0048) - # cmake warns if loaded from a min-3.0-required parent dir, so silence the warning: - cmake_policy(SET CMP0048 NEW) +# VERSION 3.7...3.18, but some versions of MCVS have a patched CMake 3.11 +# that do not work properly with this syntax, so using the following workaround: +if(${CMAKE_VERSION} VERSION_LESS 3.18) + cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) +else() + cmake_policy(VERSION 3.18) endif() -# CMake versions < 3.4.0 do not support try_compile/pthread checks without C as active language. -if(CMAKE_VERSION VERSION_LESS 3.4.0) - project(pybind11) -else() - project(pybind11 CXX) +# Extract project version from source +file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/pybind11/detail/common.h" + pybind11_version_defines REGEX "#define PYBIND11_VERSION_(MAJOR|MINOR|PATCH) ") + +foreach(ver ${pybind11_version_defines}) + if(ver MATCHES [[#define PYBIND11_VERSION_(MAJOR|MINOR|PATCH) +([^ ]+)$]]) + set(PYBIND11_VERSION_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}") + endif() +endforeach() + +if(PYBIND11_VERSION_PATCH MATCHES [[([a-zA-Z]+)]]) + set(PYBIND11_VERSION_TYPE "${CMAKE_MATCH_1}") endif() +string(REGEX MATCH "[0-9]+" PYBIND11_VERSION_PATCH "${PYBIND11_VERSION_PATCH}") + +project( + pybind11 + LANGUAGES CXX + VERSION "${PYBIND11_VERSION_MAJOR}.${PYBIND11_VERSION_MINOR}.${PYBIND11_VERSION_PATCH}") + +# Standard includes +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) +include(CMakeDependentOption) + +message(STATUS "pybind11 v${pybind11_VERSION} ${PYBIND11_VERSION_TYPE}") # Check if pybind11 is being used directly or via add_subdirectory -set(PYBIND11_MASTER_PROJECT OFF) -if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) +if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) set(PYBIND11_MASTER_PROJECT ON) + message(STATUS "CMake ${CMAKE_VERSION}") + + if(CMAKE_CXX_STANDARD) + set(CMAKE_CXX_EXTENSIONS OFF) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + endif() +else() + set(PYBIND11_MASTER_PROJECT OFF) + set(pybind11_system SYSTEM) endif() option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT}) -option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT}) +option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT}) +option(PYBIND11_CLASSIC_LTO "Use the classic LTO flag algorithm, even on CMake 3.9+" OFF) +cmake_dependent_option( + USE_PYTHON_INCLUDE_DIR + "Install pybind11 headers in Python include directory instead of default installation prefix" + OFF "PYBIND11_INSTALL" OFF) + +# NB: when adding a header don't forget to also add it to setup.py +set(PYBIND11_HEADERS + include/pybind11/detail/class.h + include/pybind11/detail/common.h + include/pybind11/detail/descr.h + include/pybind11/detail/init.h + include/pybind11/detail/internals.h + include/pybind11/detail/typeid.h + include/pybind11/attr.h + include/pybind11/buffer_info.h + include/pybind11/cast.h + include/pybind11/chrono.h + include/pybind11/common.h + include/pybind11/complex.h + include/pybind11/options.h + include/pybind11/eigen.h + include/pybind11/embed.h + include/pybind11/eval.h + include/pybind11/iostream.h + include/pybind11/functional.h + include/pybind11/numpy.h + include/pybind11/operators.h + include/pybind11/pybind11.h + include/pybind11/pytypes.h + include/pybind11/stl.h + include/pybind11/stl_bind.h) -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/tools") +# Compare with grep and warn if mismatched +if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12) + file( + GLOB_RECURSE _pybind11_header_check + LIST_DIRECTORIES false + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + CONFIGURE_DEPENDS "include/pybind11/*.h") + set(_pybind11_here_only ${PYBIND11_HEADERS}) + set(_pybind11_disk_only ${_pybind11_header_check}) + list(REMOVE_ITEM _pybind11_here_only ${_pybind11_header_check}) + list(REMOVE_ITEM _pybind11_disk_only ${PYBIND11_HEADERS}) + if(_pybind11_here_only) + message(AUTHOR_WARNING "PYBIND11_HEADERS has extra files:" ${_pybind11_here_only}) + endif() + if(_pybind11_disk_only) + message(AUTHOR_WARNING "PYBIND11_HEADERS is missing files:" ${_pybind11_disk_only}) + endif() +endif() +# CMake 3.12 added list(TRANSFORM PREPEND +# But we can't use it yet +string(REPLACE "include/" "${CMAKE_CURRENT_SOURCE_DIR}/include/" PYBIND11_HEADERS + "${PYBIND11_HEADERS}") + +# Classic mode + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tools") include(pybind11Tools) # Cache variables so pybind11_add_module can be used in parent projects -set(PYBIND11_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/include" CACHE INTERNAL "") -set(PYTHON_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS} CACHE INTERNAL "") -set(PYTHON_LIBRARIES ${PYTHON_LIBRARIES} CACHE INTERNAL "") -set(PYTHON_MODULE_PREFIX ${PYTHON_MODULE_PREFIX} CACHE INTERNAL "") -set(PYTHON_MODULE_EXTENSION ${PYTHON_MODULE_EXTENSION} CACHE INTERNAL "") -set(PYTHON_VERSION_MAJOR ${PYTHON_VERSION_MAJOR} CACHE INTERNAL "") -set(PYTHON_VERSION_MINOR ${PYTHON_VERSION_MINOR} CACHE INTERNAL "") +set(PYBIND11_INCLUDE_DIR + "${CMAKE_CURRENT_LIST_DIR}/include" + CACHE INTERNAL "") +set(PYTHON_INCLUDE_DIRS + ${PYTHON_INCLUDE_DIRS} + CACHE INTERNAL "") +set(PYTHON_LIBRARIES + ${PYTHON_LIBRARIES} + CACHE INTERNAL "") +set(PYTHON_MODULE_PREFIX + ${PYTHON_MODULE_PREFIX} + CACHE INTERNAL "") +set(PYTHON_MODULE_EXTENSION + ${PYTHON_MODULE_EXTENSION} + CACHE INTERNAL "") +set(PYTHON_VERSION_MAJOR + ${PYTHON_VERSION_MAJOR} + CACHE INTERNAL "") +set(PYTHON_VERSION_MINOR + ${PYTHON_VERSION_MINOR} + CACHE INTERNAL "") +set(PYTHON_IS_DEBUG + "${PYTHON_IS_DEBUG}" + CACHE INTERNAL "") -# NB: when adding a header don't forget to also add it to setup.py -set(PYBIND11_HEADERS - include/pybind11/detail/class.h - include/pybind11/detail/common.h - include/pybind11/detail/descr.h - include/pybind11/detail/init.h - include/pybind11/detail/internals.h - include/pybind11/detail/typeid.h - include/pybind11/attr.h - include/pybind11/buffer_info.h - include/pybind11/cast.h - include/pybind11/chrono.h - include/pybind11/common.h - include/pybind11/complex.h - include/pybind11/options.h - include/pybind11/eigen.h - include/pybind11/embed.h - include/pybind11/eval.h - include/pybind11/functional.h - include/pybind11/numpy.h - include/pybind11/operators.h - include/pybind11/pybind11.h - include/pybind11/pytypes.h - include/pybind11/stl.h - include/pybind11/stl_bind.h -) -string(REPLACE "include/" "${CMAKE_CURRENT_SOURCE_DIR}/include/" - PYBIND11_HEADERS "${PYBIND11_HEADERS}") - -if (PYBIND11_TEST) +if(PYBIND11_TEST OR (BUILD_TESTING AND PYBIND11_MASTER_PROJECT)) add_subdirectory(tests) endif() -include(GNUInstallDirs) -include(CMakePackageConfigHelpers) +if(USE_PYTHON_INCLUDE_DIR) + file(RELATIVE_PATH CMAKE_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_PREFIX} ${PYTHON_INCLUDE_DIRS}) +endif() -# extract project version from source -file(STRINGS "${PYBIND11_INCLUDE_DIR}/pybind11/detail/common.h" pybind11_version_defines - REGEX "#define PYBIND11_VERSION_(MAJOR|MINOR|PATCH) ") -foreach(ver ${pybind11_version_defines}) - if (ver MATCHES "#define PYBIND11_VERSION_(MAJOR|MINOR|PATCH) +([^ ]+)$") - set(PYBIND11_VERSION_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}" CACHE INTERNAL "") - endif() -endforeach() -set(${PROJECT_NAME}_VERSION ${PYBIND11_VERSION_MAJOR}.${PYBIND11_VERSION_MINOR}.${PYBIND11_VERSION_PATCH}) -message(STATUS "pybind11 v${${PROJECT_NAME}_VERSION}") +# Note: when creating targets, you cannot use if statements at configure time - +# you need generator expressions, because those will be placed in the target file. +# You can also place ifs *in* the Config.in, but not here. -option (USE_PYTHON_INCLUDE_DIR "Install pybind11 headers in Python include directory instead of default installation prefix" OFF) -if (USE_PYTHON_INCLUDE_DIR) - file(RELATIVE_PATH CMAKE_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_PREFIX} ${PYTHON_INCLUDE_DIRS}) +# Build an interface library target: +add_library(pybind11 INTERFACE) +add_library(pybind11::pybind11 ALIAS pybind11) # to match exported target + +target_include_directories( + pybind11 ${pybind11_system} INTERFACE $ + $) +# Only add Python for build - must be added during the import for config since it has to be re-discovered. +target_include_directories(pybind11 SYSTEM INTERFACE $) + +if(CMAKE_VERSION VERSION_LESS 3.13) + target_compile_features(pybind11 INTERFACE cxx_inheriting_constructors cxx_user_literals + cxx_right_angle_brackets) +else() + # This was added in CMake 3.8, but we are keeping a consistent breaking + # point for the config file at 3.13. A config generated by CMake 3.13+ + # can only be read in 3.13+ due to the SHELL usage later, so this is safe to do. + target_compile_features(pybind11 INTERFACE cxx_std_11) endif() -if(NOT (CMAKE_VERSION VERSION_LESS 3.0)) # CMake >= 3.0 - # Build an interface library target: - add_library(pybind11 INTERFACE) - add_library(pybind11::pybind11 ALIAS pybind11) # to match exported target - target_include_directories(pybind11 INTERFACE $ - $ - $) - target_compile_options(pybind11 INTERFACE $) - - add_library(module INTERFACE) - add_library(pybind11::module ALIAS module) - if(NOT MSVC) - target_compile_options(module INTERFACE -fvisibility=hidden) - endif() - target_link_libraries(module INTERFACE pybind11::pybind11) - if(WIN32 OR CYGWIN) - target_link_libraries(module INTERFACE $) - elseif(APPLE) - target_link_libraries(module INTERFACE "-undefined dynamic_lookup") - endif() +add_library(module INTERFACE) +add_library(pybind11::module ALIAS module) + +target_link_libraries(module INTERFACE pybind11::pybind11) - add_library(embed INTERFACE) - add_library(pybind11::embed ALIAS embed) - target_link_libraries(embed INTERFACE pybind11::pybind11 $) +# See https://github.com/Kitware/CMake/blob/master/Modules/CMakePlatformId.h.in for platform IDs +# Note: CMake 3.15 allows $ +target_link_libraries( + module + INTERFACE + "$<$,$>:$>") + +if(CMAKE_VERSION VERSION_LESS 3.13) + target_link_libraries(module INTERFACE "$<$:-undefined dynamic_lookup>") +else() + # SHELL (3.12+) forces this to remain together, and link_options was added in 3.13+ + # This is safer, because you are ensured the deduplication pass in CMake will not consider + # these separate and remove one but not the other. + target_link_options(module INTERFACE "$<$:SHELL:-undefined dynamic_lookup>") endif() -if (PYBIND11_INSTALL) +# Workaround for Python 2.7 and C++17 (C++14 as a warning) incompatibility +# This adds the flags -Wno-register and -Wno-deprecated-register if the compiler +# is Clang 3.9+ or AppleClang and the compile language is CXX, or /wd5033 for MSVC (all languages, +# since MSVC didn't recognize COMPILE_LANGUAGE until CMake 3.11+). +set(clang_4plus + "$,$,3.9>>>") +set(no_register "$>") +set(cxx_no_register "$,${no_register}>") +set(msvc "$") +target_compile_options( + pybind11 INTERFACE "$<${cxx_no_register}:-Wno-register;-Wno-deprecated-register>" + "$<${msvc}:/wd5033>") + +add_library(embed INTERFACE) +add_library(pybind11::embed ALIAS embed) +target_link_libraries(embed INTERFACE pybind11::pybind11 $) + +if(PYBIND11_INSTALL) install(DIRECTORY ${PYBIND11_INCLUDE_DIR}/pybind11 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) # GNUInstallDirs "DATADIR" wrong here; CMake search path wants "share". - set(PYBIND11_CMAKECONFIG_INSTALL_DIR "share/cmake/${PROJECT_NAME}" CACHE STRING "install path for pybind11Config.cmake") - - configure_package_config_file(tools/${PROJECT_NAME}Config.cmake.in - "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" - INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) - # Remove CMAKE_SIZEOF_VOID_P from ConfigVersion.cmake since the library does - # not depend on architecture specific settings or libraries. - set(_PYBIND11_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P}) - unset(CMAKE_SIZEOF_VOID_P) - write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake - VERSION ${${PROJECT_NAME}_VERSION} - COMPATIBILITY AnyNewerVersion) - set(CMAKE_SIZEOF_VOID_P ${_PYBIND11_CMAKE_SIZEOF_VOID_P}) - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake - tools/FindPythonLibsNew.cmake - tools/pybind11Tools.cmake - DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) - - if(NOT (CMAKE_VERSION VERSION_LESS 3.0)) - if(NOT PYBIND11_EXPORT_NAME) - set(PYBIND11_EXPORT_NAME "${PROJECT_NAME}Targets") - endif() - - install(TARGETS pybind11 module embed - EXPORT "${PYBIND11_EXPORT_NAME}") - if(PYBIND11_MASTER_PROJECT) - install(EXPORT "${PYBIND11_EXPORT_NAME}" - NAMESPACE "${PROJECT_NAME}::" - DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) - endif() + set(PYBIND11_CMAKECONFIG_INSTALL_DIR + "share/cmake/${PROJECT_NAME}" + CACHE STRING "install path for pybind11Config.cmake") + + configure_package_config_file( + tools/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) + + if(CMAKE_VERSION VERSION_LESS 3.14) + # Remove CMAKE_SIZEOF_VOID_P from ConfigVersion.cmake since the library does + # not depend on architecture specific settings or libraries. + set(_PYBIND11_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P}) + unset(CMAKE_SIZEOF_VOID_P) + + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion) + + set(CMAKE_SIZEOF_VOID_P ${_PYBIND11_CMAKE_SIZEOF_VOID_P}) + else() + # CMake 3.14+ natively supports header-only libraries + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion ARCH_INDEPENDENT) + endif() + + install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + tools/FindPythonLibsNew.cmake tools/pybind11Tools.cmake + DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) + + if(NOT PYBIND11_EXPORT_NAME) + set(PYBIND11_EXPORT_NAME "${PROJECT_NAME}Targets") + endif() + + install(TARGETS pybind11 module embed EXPORT "${PYBIND11_EXPORT_NAME}") + + install( + EXPORT "${PYBIND11_EXPORT_NAME}" + NAMESPACE "pybind11::" + DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) + + # Uninstall target + if(PYBIND11_MASTER_PROJECT) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) + + add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P + ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) endif() endif() diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 01596d94f3..9dd3bd6f6c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,11 +27,15 @@ adhere to the following rules to make the process as smooth as possible: do add value by themselves. * Add tests for any new functionality and run the test suite (``make pytest``) to ensure that no existing features break. -* Please run ``flake8`` and ``tools/check-style.sh`` to check your code matches - the project style. (Note that ``check-style.sh`` requires ``gawk``.) +* Please run [``pre-commit``][pre-commit] to check your code matches the + project style. (Note that ``gawk`` is required.) Use `pre-commit run + --all-files` before committing (or use installed-mode, check pre-commit docs) + to verify your code passes before pushing to save time. * This project has a strong focus on providing general solutions using a minimal amount of code, thus small pull requests are greatly preferred. +[pre-commit]: https://pre-commit.com + ### Licensing of contributions pybind11 is provided under a BSD-style license that can be found in the diff --git a/README.md b/README.md index 492ee9f4a0..ebc57a95f1 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Drake Fork: Hide upstream CI badges. [![Documentation Status](https://readthedocs.org/projects/pybind11/badge/?version=master)](http://pybind11.readthedocs.org/en/master/?badge=master) [![Documentation Status](https://readthedocs.org/projects/pybind11/badge/?version=stable)](http://pybind11.readthedocs.org/en/stable/?badge=stable) [![Gitter chat](https://img.shields.io/gitter/room/gitterHQ/gitter.svg)](https://gitter.im/pybind/Lobby) -[![Build Status](https://travis-ci.org/pybind/pybind11.svg?branch=master)](https://travis-ci.org/pybind/pybind11) +[![CI](https://github.com/pybind/pybind11/workflows/CI/badge.svg)](https://github.com/pybind/pybind11/actions) [![Build status](https://ci.appveyor.com/api/projects/status/riaj54pn4h08xy40?svg=true)](https://ci.appveyor.com/project/wjakob/pybind11) --> diff --git a/README_DRAKE.md b/README_DRAKE.md index 2011b841b3..1cde820bb7 100644 --- a/README_DRAKE.md +++ b/README_DRAKE.md @@ -40,28 +40,28 @@ source code (e.g. no whitespace reflowing), and try to stay relatively close to ## Continuous Integration -For simplicity, these checks are copied from upstream's CI which uses Travis -CI as part of GitHub's Checks. They test: +For simplicity, these checks are copied from upstream's CI which uses GitHub +Actions as part of GitHub's checks. They test: -* Ubuntu and macOS (Windows tests disabled) -* C++11, C++14, and C++17 +* Code formatting (Python, C++, CMake) +* Latest Ubuntu and macOS images + * **drake specific**: Windows and centOS disabled +* C++11 and C++17 * Release and debug builds -* GCC 4.8, 6, and 7 -* clang 7 -* Apple clang 7.3 and 9 -* 64bit and 32bit -* CPython and PyPy (though PyPy is partially supported on this fork) -* Python 2.7, 3.5, 3.6, 3.7, and 3.8 - -To see builds, see [this fork's Travis CI page](https://travis-ci.com/RobotLocomotion/pybind11/branches). - -Windows testing (with AppVeyor) is disabled for this repository. +* GCC and clang +* 64bit + * **drake specific**: no 32bit since Windows is disabled +* CPython + * **drake difference**: PyPy is disabled +* Python 3.6, 3.7, and 3.8 + * **drake difference**: Python 2.7 and 3.5 are disabled ### Quick Testing +For quickly testing Drake modifications on the platform's Python 3 interpreter: + mkdir build && cd build cmake .. \ - -DPYTHON_EXECUTABLE=$(which python3) \ -DPYBIND11_TEST_OVERRIDE='test_builtin_casters.cpp;test_class.cpp;test_eigen.cpp;test_multiple_inheritance.cpp;test_ownership_transfer.cpp;test_smart_ptr.cpp' make -j 4 pytest diff --git a/docs/Doxyfile b/docs/Doxyfile index 1b9d1297c5..24ece0d8db 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -18,3 +18,5 @@ ALIASES += "endrst=\endverbatim" QUIET = YES WARNINGS = YES WARN_IF_UNDOCUMENTED = NO +PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS \ + PY_MAJOR_VERSION=3 diff --git a/docs/advanced/cast/index.rst b/docs/advanced/cast/index.rst index 54c10570b1..724585c920 100644 --- a/docs/advanced/cast/index.rst +++ b/docs/advanced/cast/index.rst @@ -39,4 +39,3 @@ the last case of the above list. chrono eigen custom - diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index f630952f9b..b2d963dc4f 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -82,7 +82,7 @@ helper class that is defined as follows: The macro :c:macro:`PYBIND11_OVERLOAD_PURE` should be used for pure virtual functions, and :c:macro:`PYBIND11_OVERLOAD` should be used for functions which have -a default implementation. There are also two alternate macros +a default implementation. There are also two alternate macros :c:macro:`PYBIND11_OVERLOAD_PURE_NAME` and :c:macro:`PYBIND11_OVERLOAD_NAME` which take a string-valued name argument between the *Parent class* and *Name of the function* slots, which defines the name of function in Python. This is required @@ -1104,7 +1104,7 @@ Binding final classes Some classes may not be appropriate to inherit from. In C++11, classes can use the ``final`` specifier to ensure that a class cannot be inherited from. -The ``py::is_final`` attribute can be used to ensure that Python classes +The ``py::is_final`` attribute can be used to ensure that Python classes cannot inherit from a specified type. The underlying C++ type does not need to be declared final. @@ -1312,7 +1312,7 @@ Normally, if you keep the object alive in Python, then no additional instrumenta >>> cat = Cat() >>> c.add(cat) # This object lives in both Python and C++. >>> c.release().go(2) - meow! meow! + meow! meow! However, if you pass an instance that Python later wishes to destroy, without :class:`py::wrapper`, we would get an error that ``go`` is not implented, as the `Cat` portion would have been destroyed and no longer visible for the trampoline. With the wrapper, ``pybind11`` will intercept this event and keep the Python portion alive: @@ -1321,6 +1321,6 @@ as the `Cat` portion would have been destroyed and no longer visible for the tra >>> c.add(Cat()) >>> c.release().go(2) - meow! meow! + meow! meow! Note that both the C++ and Python portion of ``cat`` will be destroyed once ``cage`` is destroyed. diff --git a/docs/advanced/misc.rst b/docs/advanced/misc.rst index 5b38ec7598..7798462df7 100644 --- a/docs/advanced/misc.rst +++ b/docs/advanced/misc.rst @@ -218,7 +218,7 @@ collected: Both approaches also expose a potentially dangerous ``_cleanup`` attribute in Python, which may be undesirable from an API standpoint (a premature explicit -call from Python might lead to undefined behavior). Yet another approach that +call from Python might lead to undefined behavior). Yet another approach that avoids this issue involves weak reference with a cleanup callback: .. code-block:: cpp @@ -283,9 +283,9 @@ work, it is important that all lines are indented consistently, i.e.: ---------- )mydelimiter"); -By default, pybind11 automatically generates and prepends a signature to the docstring of a function +By default, pybind11 automatically generates and prepends a signature to the docstring of a function registered with ``module::def()`` and ``class_::def()``. Sometimes this -behavior is not desirable, because you want to provide your own signature or remove +behavior is not desirable, because you want to provide your own signature or remove the docstring completely to exclude the function from the Sphinx documentation. The class ``options`` allows you to selectively suppress auto-generated signatures: @@ -298,8 +298,8 @@ The class ``options`` allows you to selectively suppress auto-generated signatur m.def("add", [](int a, int b) { return a + b; }, "A function which adds two numbers"); } -Note that changes to the settings affect only function bindings created during the -lifetime of the ``options`` instance. When it goes out of scope at the end of the module's init function, +Note that changes to the settings affect only function bindings created during the +lifetime of the ``options`` instance. When it goes out of scope at the end of the module's init function, the default settings are restored to prevent unwanted side effects. .. [#f4] http://www.sphinx-doc.org diff --git a/docs/advanced/pycpp/numpy.rst b/docs/advanced/pycpp/numpy.rst index 458f99e978..f1941392fa 100644 --- a/docs/advanced/pycpp/numpy.rst +++ b/docs/advanced/pycpp/numpy.rst @@ -67,7 +67,7 @@ specification. To create a C++ function that can take a Python buffer object as an argument, simply use the type ``py::buffer`` as one of its arguments. Buffers can exist in a great variety of configurations, hence some safety checks are usually -necessary in the function body. Below, you can see an basic example on how to +necessary in the function body. Below, you can see a basic example on how to define a custom constructor for the Eigen double precision matrix (``Eigen::MatrixXd``) type, which supports initialization from compatible buffer objects (e.g. a NumPy matrix). @@ -81,7 +81,7 @@ buffer objects (e.g. a NumPy matrix). constexpr bool rowMajor = Matrix::Flags & Eigen::RowMajorBit; py::class_(m, "Matrix", py::buffer_protocol()) - .def("__init__", [](Matrix &m, py::buffer b) { + .def("__init__", [](py::buffer b) { typedef Eigen::Stride Strides; /* Request a buffer descriptor from Python */ @@ -101,7 +101,7 @@ buffer objects (e.g. a NumPy matrix). auto map = Eigen::Map( static_cast(info.ptr), info.shape[0], info.shape[1], strides); - new (&m) Matrix(map); + return Matrix(m); }); For reference, the ``def_buffer()`` call for this Eigen data type should look @@ -384,3 +384,45 @@ operation on the C++ side: py::array a = /* A NumPy array */; py::array b = a[py::make_tuple(0, py::ellipsis(), 0)]; + +Memory view +=========== + +For a case when we simply want to provide a direct accessor to C/C++ buffer +without a concrete class object, we can return a ``memoryview`` object. Suppose +we wish to expose a ``memoryview`` for 2x4 uint8_t array, we can do the +following: + +.. code-block:: cpp + + const uint8_t buffer[] = { + 0, 1, 2, 3, + 4, 5, 6, 7 + }; + m.def("get_memoryview2d", []() { + return py::memoryview::from_buffer( + buffer, // buffer pointer + { 2, 4 }, // shape (rows, cols) + { sizeof(uint8_t) * 4, sizeof(uint8_t) } // strides in bytes + ); + }) + +This approach is meant for providing a ``memoryview`` for a C/C++ buffer not +managed by Python. The user is responsible for managing the lifetime of the +buffer. Using a ``memoryview`` created in this way after deleting the buffer in +C++ side results in undefined behavior. + +We can also use ``memoryview::from_memory`` for a simple 1D contiguous buffer: + +.. code-block:: cpp + + m.def("get_memoryview1d", []() { + return py::memoryview::from_memory( + buffer, // buffer pointer + sizeof(uint8_t) * 8 // buffer size + ); + }) + +.. note:: + + ``memoryview::from_memory`` is not available in Python 2. diff --git a/docs/advanced/pycpp/object.rst b/docs/advanced/pycpp/object.rst index 117131edcb..19a226a876 100644 --- a/docs/advanced/pycpp/object.rst +++ b/docs/advanced/pycpp/object.rst @@ -60,7 +60,7 @@ This example obtains a reference to the Python ``Decimal`` class. Calling Python functions ======================== -It is also possible to call Python classes, functions and methods +It is also possible to call Python classes, functions and methods via ``operator()``. .. code-block:: cpp @@ -75,7 +75,7 @@ via ``operator()``. py::object makedirs = os.attr("makedirs"); makedirs("/tmp/path/to/somewhere"); -One can convert the result obtained from Python to a pure C++ version +One can convert the result obtained from Python to a pure C++ version if a ``py::class_`` or type conversion is defined. .. code-block:: cpp @@ -99,8 +99,8 @@ Python method. py::print(py::str(exp_pi)); In the example above ``pi.attr("exp")`` is a *bound method*: it will always call -the method for that same instance of the class. Alternately one can create an -*unbound method* via the Python class (instead of instance) and pass the ``self`` +the method for that same instance of the class. Alternately one can create an +*unbound method* via the Python class (instead of instance) and pass the ``self`` object explicitly, followed by other arguments. .. code-block:: cpp diff --git a/docs/benchmark.py b/docs/benchmark.py index 6dc0604ea9..023477212e 100644 --- a/docs/benchmark.py +++ b/docs/benchmark.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import random import os import time diff --git a/docs/benchmark.rst b/docs/benchmark.rst index 59d533df94..02c2ccde7d 100644 --- a/docs/benchmark.rst +++ b/docs/benchmark.rst @@ -93,5 +93,3 @@ favor. .. only:: latex .. image:: pybind11_vs_boost_python2.png - - diff --git a/docs/compiling.rst b/docs/compiling.rst index f3b6332c9f..07f93e7cc5 100644 --- a/docs/compiling.rst +++ b/docs/compiling.rst @@ -33,7 +33,7 @@ extension module can be created with just a few lines of code: .. code-block:: cmake - cmake_minimum_required(VERSION 2.8.12) + cmake_minimum_required(VERSION 3.7) project(example) add_subdirectory(pybind11) @@ -59,7 +59,7 @@ function with the following signature: .. code-block:: cmake pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] - [NO_EXTRAS] [SYSTEM] [THIN_LTO] source1 [source2 ...]) + [NO_EXTRAS] [THIN_LTO] source1 [source2 ...]) This function behaves very much like CMake's builtin ``add_library`` (in fact, it's a wrapper function around that command). It will add a library target @@ -86,10 +86,6 @@ latter optimizations are never applied in ``Debug`` mode. If ``NO_EXTRAS`` is given, they will always be disabled, even in ``Release`` mode. However, this will result in code bloat and is generally not recommended. -By default, pybind11 and Python headers will be included with ``-I``. In order -to include pybind11 as system library, e.g. to avoid warnings in downstream -code with warn-levels outside of pybind11's scope, set the option ``SYSTEM``. - As stated above, LTO is enabled by default. Some newer compilers also support different flavors of LTO such as `ThinLTO`_. Setting ``THIN_LTO`` will cause the function to prefer this flavor if available. The function falls back to @@ -100,25 +96,22 @@ regular LTO if ``-flto=thin`` is not available. Configuration variables ----------------------- -By default, pybind11 will compile modules with the C++14 standard, if available -on the target compiler, falling back to C++11 if C++14 support is not -available. Note, however, that this default is subject to change: future -pybind11 releases are expected to migrate to newer C++ standards as they become -available. To override this, the standard flag can be given explicitly in -`CMAKE_CXX_STANDARD `_: +By default, pybind11 will compile modules with the compiler default or the +minimum standard required by pybind11, whichever is higher. You can set the +standard explicitly with +`CMAKE_CXX_STANDARD `_: .. code-block:: cmake - # Use just one of these: - set(CMAKE_CXX_STANDARD 11) - set(CMAKE_CXX_STANDARD 14) - set(CMAKE_CXX_STANDARD 17) # Experimental C++17 support + set(CMAKE_CXX_STANDARD 14) # or 11, 14, 17, 20 + set(CMAKE_CXX_STANDARD_REQUIRED ON) # optional, ensure standard is supported + set(CMAKE_CXX_EXTENSIONS OFF) # optional, keep compiler extensionsn off - add_subdirectory(pybind11) # or find_package(pybind11) -Note that this and all other configuration variables must be set **before** the -call to ``add_subdirectory`` or ``find_package``. The variables can also be set -when calling CMake from the command line using the ``-D=`` flag. +The variables can also be set when calling CMake from the command line using +the ``-D=`` flag. You can also manually set ``CXX_STANDARD`` +on a target or use ``target_compile_features`` on your targets - anything that +CMake supports. The target Python version can be selected by setting ``PYBIND11_PYTHON_VERSION`` or an exact Python installation can be specified with ``PYTHON_EXECUTABLE``. @@ -127,8 +120,12 @@ For example: .. code-block:: bash cmake -DPYBIND11_PYTHON_VERSION=3.6 .. - # or - cmake -DPYTHON_EXECUTABLE=path/to/python .. + + # Another method: + cmake -DPYTHON_EXECUTABLE=/path/to/python .. + + # This often is a good way to get the current Python, works in environments: + cmake -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") .. find_package vs. add_subdirectory --------------------------------- @@ -139,7 +136,7 @@ See the `Config file`_ docstring for details of relevant CMake variables. .. code-block:: cmake - cmake_minimum_required(VERSION 2.8.12) + cmake_minimum_required(VERSION 3.7) project(example) find_package(pybind11 REQUIRED) @@ -151,12 +148,19 @@ the pybind11 repository : .. code-block:: bash + # Classic CMake cd pybind11 mkdir build cd build cmake .. make install + # CMake 3.15+ + cd pybind11 + cmake -S . -B build + cmake --build build -j 2 # Build on 2 cores + cmake --install build + Once detected, the aforementioned ``pybind11_add_module`` can be employed as before. The function usage and configuration variables are identical no matter if pybind11 is added as a subdirectory or found as an installed package. You @@ -171,13 +175,13 @@ Advanced: interface library target When using a version of CMake greater than 3.0, pybind11 can additionally be used as a special *interface library* . The target ``pybind11::module`` is available with pybind11 headers, Python headers and libraries as needed, -and C++ compile definitions attached. This target is suitable for linking +and C++ compile features attached. This target is suitable for linking to an independently constructed (through ``add_library``, not ``pybind11_add_module``) target in the consuming project. .. code-block:: cmake - cmake_minimum_required(VERSION 3.0) + cmake_minimum_required(VERSION 3.7) project(example) find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11) @@ -201,10 +205,16 @@ to an independently constructed (through ``add_library``, not Studio (``/bigobj``). The :ref:`FAQ ` contains an explanation on why these are needed. -.. note:: + If you want to add these in yourself, you can use: + + .. code-block:: cmake + + set(CMAKE_CXX_VISIBILITY_PRESET hidden) + set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) # CMake 3.9+ required - ``pybind11_add_module`` will use this interface library target in addition - to the above compiler flags if using a version of CMake greater than 3.0. + or set the corresponding property (without the ``CMAKE_``) on the targets + manually. Embedding the Python interpreter -------------------------------- @@ -218,7 +228,7 @@ information about usage in C++, see :doc:`/advanced/embedding`. .. code-block:: cmake - cmake_minimum_required(VERSION 3.0) + cmake_minimum_required(VERSION 3.7) project(example) find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11) diff --git a/docs/conf.py b/docs/conf.py index 585987ec6d..0946f30e2e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -130,8 +130,8 @@ else: html_context = { 'css_files': [ - '//media.readthedocs.org/css/sphinx_rtd_theme.css', - '//media.readthedocs.org/css/readthedocs-doc-embed.css', + '//media.readthedocs.org/css/sphinx_rtd_theme.css', + '//media.readthedocs.org/css/readthedocs-doc-embed.css', '_static/theme_overrides.css' ] } @@ -238,7 +238,7 @@ #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. -'preamble': '\DeclareUnicodeCharacter{00A0}{}', +'preamble': r'\DeclareUnicodeCharacter{00A0}{}', # Latex figure (float) alignment #'figure_align': 'htbp', diff --git a/docs/faq.rst b/docs/faq.rst index 4d491fb87f..b68562910a 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -5,7 +5,7 @@ Frequently asked questions =========================================================== 1. Make sure that the name specified in PYBIND11_MODULE is identical to the -filename of the extension library (without prefixes such as .so) +filename of the extension library (without suffixes such as .so) 2. If the above did not fix the issue, you are likely using an incompatible version of Python (for instance, the extension library was compiled against diff --git a/docs/limitations.rst b/docs/limitations.rst index a1a4f1affa..59474f82fd 100644 --- a/docs/limitations.rst +++ b/docs/limitations.rst @@ -17,4 +17,3 @@ These features could be implemented but would lead to a significant increase in complexity. I've decided to draw the line here to keep this project simple and compact. Users who absolutely require these features are encouraged to fork pybind11. - diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index 95301fb350..0809a0353a 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -12,7 +12,7 @@ #include "cast.h" -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) /// \addtogroup annotations /// @{ @@ -113,7 +113,7 @@ struct call_guard { /// @} annotations -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) /* Forward declarations */ enum op_id : int; enum op_type : int; @@ -527,5 +527,5 @@ constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { return named == 0 || (self + named + has_args + has_kwargs) == nargs; } -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/buffer_info.h b/include/pybind11/buffer_info.h index 1f4115a1fa..8349a46b8b 100644 --- a/include/pybind11/buffer_info.h +++ b/include/pybind11/buffer_info.h @@ -11,7 +11,7 @@ #include "detail/common.h" -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) /// Information record describing a Python buffer object struct buffer_info { @@ -54,7 +54,7 @@ struct buffer_info { explicit buffer_info(Py_buffer *view, bool ownview = true) : buffer_info(view->buf, view->itemsize, view->format, view->ndim, {view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}, view->readonly) { - this->view = view; + this->m_view = view; this->ownview = ownview; } @@ -73,16 +73,18 @@ struct buffer_info { ndim = rhs.ndim; shape = std::move(rhs.shape); strides = std::move(rhs.strides); - std::swap(view, rhs.view); + std::swap(m_view, rhs.m_view); std::swap(ownview, rhs.ownview); readonly = rhs.readonly; return *this; } ~buffer_info() { - if (view && ownview) { PyBuffer_Release(view); delete view; } + if (m_view && ownview) { PyBuffer_Release(m_view); delete m_view; } } + Py_buffer *view() const { return m_view; } + Py_buffer *&view() { return m_view; } private: struct private_ctr_tag { }; @@ -90,11 +92,11 @@ struct buffer_info { detail::any_container &&shape_in, detail::any_container &&strides_in, bool readonly) : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) { } - Py_buffer *view = nullptr; + Py_buffer *m_view = nullptr; bool ownview = false; }; -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) template struct compare_buffer_info { static bool compare(const buffer_info& b) { @@ -110,5 +112,5 @@ template struct compare_buffer_info struct is_copy_assignable struct is_copy_assignable> : all_of, is_copy_assignable> {}; -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) // polymorphic_type_hook::get(src, tinfo) determines whether the object pointed // to by `src` actually is an instance of some class derived from `itype`. @@ -968,7 +968,7 @@ struct polymorphic_type_hook_base struct polymorphic_type_hook : public polymorphic_type_hook_base {}; -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) /// Generic type caster for objects stored on the heap template class type_caster_base : public type_caster_generic { @@ -1537,6 +1537,17 @@ template class Tuple, typename... Ts> class tuple_caster return cast_impl(std::forward(src), policy, parent, indices{}); } + // copied from the PYBIND11_TYPE_CASTER macro + template + static handle cast(T *src, return_value_policy policy, handle parent) { + if (!src) return none().release(); + if (policy == return_value_policy::take_ownership) { + auto h = cast(std::move(*src), policy, parent); delete src; return h; + } else { + return cast(*src, policy, parent); + } + } + static constexpr auto name = _("Tuple[") + concat(make_caster::name...) + _("]"); template using cast_op_type = type; @@ -1882,6 +1893,7 @@ template <> struct handle_type_name { static constexpr auto name = _(PYBI template <> struct handle_type_name { static constexpr auto name = _("int"); }; template <> struct handle_type_name { static constexpr auto name = _("Iterable"); }; template <> struct handle_type_name { static constexpr auto name = _("Iterator"); }; +template <> struct handle_type_name { static constexpr auto name = _("None"); }; template <> struct handle_type_name { static constexpr auto name = _("*args"); }; template <> struct handle_type_name { static constexpr auto name = _("**kwargs"); }; @@ -1984,7 +1996,7 @@ template make_caster load_type(const handle &handle) { return conv; } -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) // pytype -> C++ type template ::value, int> = 0> @@ -2001,13 +2013,16 @@ T cast(const handle &handle) { return T(reinterpret_borrow(handle)); } // C++ type -> py::object template ::value, int> = 0> -object cast(const T &value, return_value_policy policy = return_value_policy::automatic_reference, +object cast(T &&value, return_value_policy policy = return_value_policy::automatic_reference, handle parent = handle()) { + using no_ref_T = typename std::remove_reference::type; if (policy == return_value_policy::automatic) - policy = std::is_pointer::value ? return_value_policy::take_ownership : return_value_policy::copy; + policy = std::is_pointer::value ? return_value_policy::take_ownership : + std::is_lvalue_reference::value ? return_value_policy::copy : return_value_policy::move; else if (policy == return_value_policy::automatic_reference) - policy = std::is_pointer::value ? return_value_policy::reference : return_value_policy::copy; - return reinterpret_steal(detail::make_caster::cast(value, policy, parent)); + policy = std::is_pointer::value ? return_value_policy::reference : + std::is_lvalue_reference::value ? return_value_policy::copy : return_value_policy::move; + return reinterpret_steal(detail::make_caster::cast(std::forward(value), policy, parent)); } template T handle::cast() const { return pybind11::cast(*this); } @@ -2025,14 +2040,6 @@ detail::enable_if_t< detail::make_caster::cast(std::move(value), return_value_policy::take_ownership, no_parent)); } -template -detail::enable_if_t< - std::is_rvalue_reference::value && !std::is_pointer::value && !detail::is_pyobject>::value, object> - cast(T&& value) { - // Have to use `pybind11::move` because some compilers might try to bind `move` to `std::move`... - return pybind11::move(std::move(value)); -} - template detail::enable_if_t::value, T> move(object &&obj) { if (obj.ref_count() > 1) @@ -2072,7 +2079,7 @@ template T object::cast() && { return pybind11::cast(std::move(* template <> inline void object::cast() const & { return; } template <> inline void object::cast() && { return; } -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) // Declared in pytypes.h: template ::value, int>> @@ -2099,7 +2106,7 @@ template enable_if_t::value, T pybind11_fail("Internal error: cast_safe fallback invoked"); } template <> inline void cast_safe(object &&) {} -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) template tuple make_tuple() { return tuple(0); } @@ -2207,7 +2214,7 @@ inline namespace literals { constexpr arg operator"" _a(const char *name, size_t) { return arg(name); } } -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) // forward declaration (definition in attr.h) struct function_record; @@ -2478,7 +2485,7 @@ object object_api::call(Args &&...args) const { return operator()(std::forward(args)...); } -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) #define PYBIND11_MAKE_OPAQUE(...) \ namespace pybind11 { namespace detail { \ @@ -2489,4 +2496,4 @@ NAMESPACE_END(detail) /// typedef, e.g.: `PYBIND11_OVERLOAD(PYBIND11_TYPE(ReturnType), PYBIND11_TYPE(Parent), f, arg)` #define PYBIND11_TYPE(...) __VA_ARGS__ -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/chrono.h b/include/pybind11/chrono.h index ea777e6965..6b9ab9b82a 100644 --- a/include/pybind11/chrono.h +++ b/include/pybind11/chrono.h @@ -27,8 +27,8 @@ #define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) #endif -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) template class duration_caster { public: @@ -180,5 +180,5 @@ template class type_caster> { }; -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/complex.h b/include/pybind11/complex.h index 3f89638571..f8327eb373 100644 --- a/include/pybind11/complex.h +++ b/include/pybind11/complex.h @@ -17,7 +17,7 @@ # undef I #endif -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) template struct format_descriptor, detail::enable_if_t::value>> { static constexpr const char c = format_descriptor::c; @@ -32,7 +32,7 @@ template constexpr const char format_descriptor< #endif -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) template struct is_fmt_numeric, detail::enable_if_t::value>> { static constexpr bool value = true; @@ -61,5 +61,5 @@ template class type_caster> { PYBIND11_TYPE_CASTER(std::complex, _("complex")); }; -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/detail/class.h b/include/pybind11/detail/class.h index d6add10418..c03b32acab 100644 --- a/include/pybind11/detail/class.h +++ b/include/pybind11/detail/class.h @@ -12,10 +12,10 @@ #include "../attr.h" #include "../options.h" -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) -#if PY_VERSION_HEX >= 0x03030000 +#if PY_VERSION_HEX >= 0x03030000 && !defined(PYPY_VERSION) # define PYBIND11_BUILTIN_QUALNAME # define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) #else @@ -475,7 +475,7 @@ extern "C" inline int pybind11_clear(PyObject *self) { /// Give instances of this type a `__dict__` and opt into garbage collection. inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { auto type = &heap_type->ht_type; -#if defined(PYPY_VERSION) +#if defined(PYPY_VERSION) && (PYPY_VERSION_NUM < 0x06000000) pybind11_fail(std::string(type->tp_name) + ": dynamic attributes are " "currently not supported in " "conjunction with PyPy!"); @@ -664,5 +664,5 @@ inline PyObject* make_new_python_type(const type_record &rec) { return (PyObject *) type; } -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 3cc73417e6..9747611507 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -9,12 +9,12 @@ #pragma once -#if !defined(NAMESPACE_BEGIN) -# define NAMESPACE_BEGIN(name) namespace name { -#endif -#if !defined(NAMESPACE_END) -# define NAMESPACE_END(name) } -#endif +#define PYBIND11_VERSION_MAJOR 2 +#define PYBIND11_VERSION_MINOR 6 +#define PYBIND11_VERSION_PATCH dev0 + +#define PYBIND11_NAMESPACE_BEGIN(name) namespace name { +#define PYBIND11_NAMESPACE_END(name) } // Robust support for some features and loading modules compiled against different pybind versions // requires forcing hidden visibility on pybind code, so we enforce this by setting the attribute on @@ -100,10 +100,6 @@ # define PYBIND11_MAYBE_UNUSED __attribute__ ((__unused__)) #endif -#define PYBIND11_VERSION_MAJOR 2 -#define PYBIND11_VERSION_MINOR 5 -#define PYBIND11_VERSION_PATCH dev1 - /* Don't let Python.h #define (v)snprintf as macro because they are implemented properly in Visual Studio since 2015. */ #if defined(_MSC_VER) && _MSC_VER >= 1900 @@ -325,7 +321,7 @@ extern "C" { void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable) -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) using ssize_t = Py_ssize_t; using size_t = std::size_t; @@ -384,7 +380,7 @@ enum class return_value_policy : uint8_t { class object; -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) inline static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); } @@ -661,7 +657,7 @@ template constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); } #endif -NAMESPACE_BEGIN(constexpr_impl) +PYBIND11_NAMESPACE_BEGIN(constexpr_impl) /// Implementation details for constexpr functions constexpr int first(int i) { return i; } template @@ -670,7 +666,7 @@ constexpr int first(int i, T v, Ts... vs) { return v ? i : first(i + 1, vs...); constexpr int last(int /*i*/, int result) { return result; } template constexpr int last(int i, int result, T v, Ts... vs) { return last(i + 1, v ? i : result, vs...); } -NAMESPACE_END(constexpr_impl) +PYBIND11_NAMESPACE_END(constexpr_impl) /// Return the index of the first type in Ts which satisfies Predicate. Returns sizeof...(Ts) if /// none match. @@ -781,10 +777,10 @@ inline void ignore_unused(const int *) { } #define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (((PATTERN), void()), ...) #else using expand_side_effects = bool[]; -#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) pybind11::detail::expand_side_effects{ ((PATTERN), void(), false)..., false } +#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (void)pybind11::detail::expand_side_effects{ ((PATTERN), void(), false)..., false } #endif -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) /// C++ bindings of builtin Python exceptions class builtin_exception : public std::runtime_error { @@ -816,7 +812,7 @@ PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used in template struct format_descriptor { }; -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) // Returns the index of the given type in the type char array below, and in the list in numpy.h // The order here is: bool; 8 ints ((signed,unsigned)x(8,16,32,64)bits); float,double,long double; // complex float,double,long double. Note that the long double types only participate when long @@ -829,7 +825,7 @@ template struct is_fmt_numeric std::is_integral::value ? detail::log2(sizeof(T))*2 + std::is_unsigned::value : 8 + ( std::is_same::value ? 1 : std::is_same::value ? 2 : 0)); }; -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) template struct format_descriptor::value>> { static constexpr const char c = "?bBhHiIqQfdg"[detail::is_fmt_numeric::index]; @@ -854,7 +850,7 @@ struct error_scope { /// Dummy destructor wrapper that can be used to expose classes with a private destructor struct nodelete { template void operator()(T*) { } }; -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) template struct overload_cast_impl { constexpr overload_cast_impl() {} // MSVC 2015 needs this @@ -871,7 +867,7 @@ struct overload_cast_impl { constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept -> decltype(pmf) { return pmf; } }; -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) // overload_cast requires variable templates: C++14 #if defined(PYBIND11_CPP14) @@ -896,7 +892,7 @@ template struct overload_cast { }; #endif // overload_cast -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) // Adaptor for converting arbitrary container arguments into a vector; implicitly convertible from // any standard container (or C-style array) supporting std::begin/std::end, any singleton @@ -935,8 +931,8 @@ class any_container { const std::vector *operator->() const { return &v; } }; -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/detail/descr.h b/include/pybind11/detail/descr.h index 8d404e5346..92720cd562 100644 --- a/include/pybind11/detail/descr.h +++ b/include/pybind11/detail/descr.h @@ -11,8 +11,8 @@ #include "common.h" -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) #if !defined(_MSC_VER) # define PYBIND11_DESCR_CONSTEXPR static constexpr @@ -96,5 +96,5 @@ constexpr descr type_descr(const descr &descr) { return _("{") + descr + _("}"); } -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/detail/init.h b/include/pybind11/detail/init.h index acfe00bdb7..35b95bcfae 100644 --- a/include/pybind11/detail/init.h +++ b/include/pybind11/detail/init.h @@ -11,8 +11,8 @@ #include "class.h" -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) template <> class type_caster { @@ -30,7 +30,7 @@ class type_caster { value_and_holder *value = nullptr; }; -NAMESPACE_BEGIN(initimpl) +PYBIND11_NAMESPACE_BEGIN(initimpl) inline void no_nullptr(void *ptr) { if (!ptr) throw type_error("pybind11::init(): factory function returned nullptr"); @@ -330,6 +330,6 @@ struct pickle_factory { } }; -NAMESPACE_END(initimpl) -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) +PYBIND11_NAMESPACE_END(initimpl) +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(pybind11) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 75f6cfc24b..e57cdad030 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -11,8 +11,8 @@ #include "../pytypes.h" -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) // Forward declarations inline PyTypeObject *make_static_property_type(); inline PyTypeObject *make_default_metaclass(); @@ -275,7 +275,10 @@ PYBIND11_NOINLINE inline internals &get_internals() { auto *&internals_ptr = *internals_pp; internals_ptr = new internals(); #if defined(WITH_THREAD) - PyEval_InitThreads(); + + #if PY_VERSION_HEX < 0x03090000 + PyEval_InitThreads(); + #endif PyThreadState *tstate = PyThreadState_Get(); #if PY_VERSION_HEX >= 0x03070000 internals_ptr->tstate = PyThread_tss_alloc(); @@ -316,7 +319,7 @@ const char *c_str(Args &&...args) { return strings.front().c_str(); } -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) /// Returns a named pointer that is shared among all extension modules (using the same /// pybind11 version) running in the current interpreter. Names starting with underscores @@ -348,4 +351,4 @@ T &get_or_create_shared_data(const std::string &name) { return *ptr; } -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/detail/typeid.h b/include/pybind11/detail/typeid.h index 9c8a4fc69a..148889ffef 100644 --- a/include/pybind11/detail/typeid.h +++ b/include/pybind11/detail/typeid.h @@ -18,8 +18,8 @@ #include "common.h" -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) /// Erase all occurrences of a substring inline void erase_all(std::string &string, const std::string &search) { for (size_t pos = 0;;) { @@ -43,7 +43,7 @@ PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { #endif detail::erase_all(name, "pybind11::"); } -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) /// Return a string representation of a C++ type template static std::string type_id() { @@ -52,4 +52,4 @@ template static std::string type_id() { return name; } -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/eigen.h b/include/pybind11/eigen.h index 21b6fbd094..4c8f8b4667 100644 --- a/include/pybind11/eigen.h +++ b/include/pybind11/eigen.h @@ -41,14 +41,14 @@ // of matrices seems highly undesirable. static_assert(EIGEN_VERSION_AT_LEAST(3,2,7), "Eigen support in pybind11 requires Eigen >= 3.2.7"); -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) // Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides: using EigenDStride = Eigen::Stride; template using EigenDRef = Eigen::Ref; template using EigenDMap = Eigen::Map; -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) #if EIGEN_VERSION_AT_LEAST(3,3,0) using EigenIndex = Eigen::Index; @@ -768,8 +768,8 @@ struct type_caster::value>> { + npy_format_descriptor::name + _("]")); }; -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) #if defined(__GNUG__) || defined(__clang__) # pragma GCC diagnostic pop diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index f814c783e7..eae86c714c 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -61,13 +61,14 @@ } \ } \ PYBIND11_EMBEDDED_MODULE_IMPL(name) \ - pybind11::detail::embedded_module name(PYBIND11_TOSTRING(name), \ + pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name) \ + (PYBIND11_TOSTRING(name), \ PYBIND11_CONCAT(pybind11_init_impl_, name)); \ void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable) -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) /// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks. struct embedded_module { @@ -86,7 +87,7 @@ struct embedded_module { } }; -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) /** \rst Initialize the Python interpreter. No other pybind11 or CPython API functions can be @@ -199,4 +200,4 @@ class scoped_interpreter { bool is_valid = true; }; -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/eval.h b/include/pybind11/eval.h index ea85ba1dbe..ba82cf42ae 100644 --- a/include/pybind11/eval.h +++ b/include/pybind11/eval.h @@ -13,7 +13,7 @@ #include "pybind11.h" -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) enum eval_mode { /// Evaluate a string containing an isolated expression @@ -66,6 +66,20 @@ void exec(const char (&s)[N], object global = globals(), object local = object() eval(s, global, local); } +#if defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x3000000 +template +object eval_file(str, object, object) { + pybind11_fail("eval_file not supported in PyPy3. Use eval"); +} +template +object eval_file(str, object) { + pybind11_fail("eval_file not supported in PyPy3. Use eval"); +} +template +object eval_file(str) { + pybind11_fail("eval_file not supported in PyPy3. Use eval"); +} +#else template object eval_file(str fname, object global = globals(), object local = object()) { if (!local) @@ -113,5 +127,6 @@ object eval_file(str fname, object global = globals(), object local = object()) throw error_already_set(); return reinterpret_steal(result); } +#endif -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/functional.h b/include/pybind11/functional.h index f8bda64831..57b6cd210f 100644 --- a/include/pybind11/functional.h +++ b/include/pybind11/functional.h @@ -12,8 +12,8 @@ #include "pybind11.h" #include -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) template struct type_caster> { @@ -97,5 +97,5 @@ struct type_caster> { + make_caster::name + _("]")); }; -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/iostream.h b/include/pybind11/iostream.h index c43b7c93a6..eaf92dfa49 100644 --- a/include/pybind11/iostream.h +++ b/include/pybind11/iostream.h @@ -17,8 +17,8 @@ #include #include -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) // Buffer that writes to Python instead of C++ class pythonbuf : public std::streambuf { @@ -72,7 +72,7 @@ class pythonbuf : public std::streambuf { } }; -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) /** \rst @@ -144,7 +144,7 @@ class scoped_estream_redirect : public scoped_ostream_redirect { }; -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) // Class to redirect output as a context manager. C++ backend. class OstreamRedirect { @@ -170,7 +170,7 @@ class OstreamRedirect { } }; -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) /** \rst This is a helper function to add a C++ redirect context manager to Python @@ -206,4 +206,4 @@ inline class_ add_ostream_redirect(module m, std::strin .def("__exit__", [](detail::OstreamRedirect &self_, args) { self_.exit(); }); } -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 54210cfba6..1ddd59dc44 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -35,11 +35,11 @@ upon the library user. */ static_assert(sizeof(ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t"); -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) class array; // Forward declaration -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) template <> struct handle_type_name { static constexpr auto name = _("numpy.ndarray"); }; @@ -181,8 +181,9 @@ struct npy_api { unsigned int (*PyArray_GetNDArrayCFeatureVersion_)(); PyObject *(*PyArray_DescrFromType_)(int); PyObject *(*PyArray_NewFromDescr_) - (PyTypeObject *, PyObject *, int, Py_intptr_t *, - Py_intptr_t *, void *, int, PyObject *); + (PyTypeObject *, PyObject *, int, Py_intptr_t const *, + Py_intptr_t const *, void *, int, PyObject *); + // Unused. Not removed because that affects ABI of the class. PyObject *(*PyArray_DescrNewFromType_)(int); int (*PyArray_CopyInto_)(PyObject *, PyObject *); PyObject *(*PyArray_NewCopy_)(PyObject *, int); @@ -193,9 +194,10 @@ struct npy_api { PyObject *(*PyArray_FromAny_) (PyObject *, PyObject *, int, int, int, PyObject *); int (*PyArray_DescrConverter_) (PyObject *, PyObject **); bool (*PyArray_EquivTypes_) (PyObject *, PyObject *); - int (*PyArray_GetArrayParamsFromObject_)(PyObject *, PyObject *, char, PyObject **, int *, - Py_ssize_t *, PyObject **, PyObject *); + int (*PyArray_GetArrayParamsFromObject_)(PyObject *, PyObject *, unsigned char, PyObject **, int *, + Py_intptr_t *, PyObject **, PyObject *); PyObject *(*PyArray_Squeeze_)(PyObject *); + // Unused. Not removed because that affects ABI of the class. int (*PyArray_SetBaseObject_)(PyObject *, PyObject *); PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int); private: @@ -211,7 +213,7 @@ struct npy_api { API_PyArray_CopyInto = 82, API_PyArray_NewCopy = 85, API_PyArray_NewFromDescr = 94, - API_PyArray_DescrNewFromType = 9, + API_PyArray_DescrNewFromType = 96, API_PyArray_DescrConverter = 174, API_PyArray_EquivTypes = 182, API_PyArray_GetArrayParamsFromObject = 278, @@ -442,7 +444,7 @@ struct type_caster> { template struct type_caster> : type_caster> {}; -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) class dtype : public object { public: @@ -548,7 +550,7 @@ class array : public buffer { forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_ }; - array() : array({{0}}, static_cast(nullptr)) {} + array() : array(0, static_cast(nullptr)) {} using ShapeContainer = detail::any_container; using StridesContainer = detail::any_container; @@ -866,7 +868,7 @@ template class array_t : public ExtraFlags & f_style ? f_strides(*shape, itemsize()) : c_strides(*shape, itemsize()), ptr, base) { } - explicit array_t(size_t count, const T *ptr = nullptr, handle base = handle()) + explicit array_t(ssize_t count, const T *ptr = nullptr, handle base = handle()) : array({count}, {}, ptr, base) { } constexpr ssize_t itemsize() const { @@ -979,7 +981,7 @@ struct format_descriptor::is_array> } }; -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) template struct pyobject_caster> { using type = array_t; @@ -1626,7 +1628,7 @@ template struct handle_type_name> { static constexpr auto name = _("numpy.ndarray[") + npy_format_descriptor::name + _("]"); }; -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) // Vanilla pointer vectorizer: template @@ -1656,7 +1658,7 @@ Helper vectorize(Return (Class::*f)(Args...) const) { return Helper(std::mem_fn(f)); } -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) #if defined(_MSC_VER) #pragma warning(pop) diff --git a/include/pybind11/operators.h b/include/pybind11/operators.h index 293d5abd29..086cb4cfd8 100644 --- a/include/pybind11/operators.h +++ b/include/pybind11/operators.h @@ -18,8 +18,8 @@ # pragma warning(disable: 4127) // warning C4127: Conditional expression is constant #endif -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) /// Enumeration with all supported operator types enum op_id : int { @@ -160,13 +160,13 @@ PYBIND11_UNARY_OPERATOR(float, float_, (double) l) #undef PYBIND11_BINARY_OPERATOR #undef PYBIND11_INPLACE_OPERATOR #undef PYBIND11_UNARY_OPERATOR -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) using detail::self; // Add named operators so that they are accessible via `py::`. using detail::hash; -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) #if defined(_MSC_VER) # pragma warning(pop) diff --git a/include/pybind11/options.h b/include/pybind11/options.h index cc1e1f6f0f..d74db1c68d 100644 --- a/include/pybind11/options.h +++ b/include/pybind11/options.h @@ -11,7 +11,7 @@ #include "detail/common.h" -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) class options { public: @@ -62,4 +62,4 @@ class options { state previous_state; }; -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 462f8f906c..b4382fc006 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -50,7 +50,7 @@ # include #endif -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) template void unused(Args&&...) {} @@ -231,7 +231,7 @@ class cpp_function : public function { if (a.descr) a.descr = strdup(a.descr); else if (a.value) - a.descr = strdup(a.value.attr("__repr__")().cast().c_str()); + a.descr = strdup(repr(a.value).cast().c_str()); } rec->is_constructor = !strcmp(rec->name, "__init__") || !strcmp(rec->name, "__setstate__"); @@ -912,7 +912,7 @@ inline dict globals() { return reinterpret_borrow(p ? p : module::import("__main__").attr("__dict__").ptr()); } -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) /// Generic support for creating new Python heap types class generic_type : public object { template friend class class_; @@ -1052,7 +1052,14 @@ inline void call_operator_delete(void *p, size_t s, size_t a) { #endif } -NAMESPACE_END(detail) +inline void add_class_method(object& cls, const char *name_, const cpp_function &cf) { + cls.attr(cf.name()) = cf; + if (strcmp(name_, "__eq__") == 0 && !cls.attr("__dict__").contains("__hash__")) { + cls.attr("__hash__") = none(); + } +} + +PYBIND11_NAMESPACE_END(detail) /// Given a pointer to a member function, cast it to its `Derived` version. /// Forward everything else unchanged. @@ -1479,7 +1486,7 @@ class class_ : public detail::generic_type { class_ &def(const char *name_, Func&& f, const Extra&... extra) { cpp_function cf(method_adaptor(std::forward(f)), name(name_), is_method(*this), sibling(getattr(*this, name_, none())), extra...); - attr(cf.name()) = cf; + add_class_method(*this, name_, cf); return *this; } @@ -1783,6 +1790,13 @@ class class_ : public detail::generic_type { /// Deallocates an instance; via holder, if constructed; otherwise via operator delete. static void dealloc(detail::value_and_holder &v_h) { + // We could be deallocating because we are cleaning up after a Python exception. + // If so, the Python error indicator will be set. We need to clear that before + // running the destructor, in case the destructor code calls more Python. + // If we don't, the Python API will exit with an exception, and pybind11 will + // throw error_already_set from the C++ destructor which is forbidden and triggers + // std::terminate(). + error_scope scope; if (v_h.holder_constructed()) { v_h.holder().~holder_type(); v_h.set_holder_constructed(false); @@ -1827,7 +1841,7 @@ detail::initimpl::pickle_factory pickle(GetState &&g, SetSta return {std::forward(g), std::forward(s)}; } -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) struct enum_base { enum_base(handle base, handle parent) : m_base(base), m_parent(parent) { } @@ -1978,7 +1992,7 @@ struct enum_base { handle m_parent; }; -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) /// Binds C++ enumerations and enumeration classes to Python template class enum_ : public class_ { @@ -2030,7 +2044,7 @@ template class enum_ : public class_ { detail::enum_base m_base; }; -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) inline void keep_alive_impl(handle nurse, handle patient) { @@ -2100,7 +2114,7 @@ struct iterator_state { bool first_or_done; }; -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) /// Makes a python iterator from a first and past-the-end C++ InputIterator. template exception &get_exception_object() { static exception ex; return ex; } -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) /** * Registers a Python exception in `m` of the given `name` and installs an exception translator to @@ -2265,7 +2279,7 @@ exception ®ister_exception(handle scope, return ex; } -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) { auto strings = tuple(args.size()); for (size_t i = 0; i < args.size(); ++i) { @@ -2296,7 +2310,7 @@ PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) { if (kwargs.contains("flush") && kwargs["flush"].cast()) file.attr("flush")(); } -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) template void print(Args &&...args) { @@ -2608,7 +2622,7 @@ template function get_overload(const T *this_ptr, const char *name) { #define PYBIND11_OVERLOAD_PURE(ret_type, cname, fn, ...) \ PYBIND11_OVERLOAD_PURE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) # pragma warning(pop) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index cabab0c09c..f1d8e96cdf 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -15,14 +15,14 @@ #include #include -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) /* A few forward declarations */ class handle; class object; class str; class iterator; struct arg; struct arg_v; -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) class args_proxy; inline bool isinstance_generic(handle obj, const std::type_info &tp); @@ -159,7 +159,7 @@ class object_api : public pyobject_tag { bool rich_compare(object_api const &other, int value) const; }; -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) /** \rst Holds a reference to a Python object (no reference counting) @@ -312,9 +312,9 @@ template T reinterpret_borrow(handle h) { return {h, object::borrow \endrst */ template T reinterpret_steal(handle h) { return {h, object::stolen_t{}}; } -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) inline std::string error_string(); -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) /// Fetch and hold an error which was already set in Python. An instance of this is typically /// thrown to propagate python-side errors back through C++ which can either be caught manually or @@ -371,7 +371,7 @@ bool isinstance(handle obj) { return T::check_(obj); } template ::value, int> = 0> bool isinstance(handle obj) { return detail::isinstance_generic(obj, typeid(T)); } -template <> inline bool isinstance(handle obj) = delete; +template <> inline bool isinstance(handle) = delete; template <> inline bool isinstance(handle obj) { return obj.ptr() != nullptr; } /// \ingroup python_builtins @@ -447,7 +447,7 @@ inline ssize_t hash(handle obj) { /// @} python_builtins -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) inline handle get_function(handle value) { if (value) { #if PY_MAJOR_VERSION >= 3 @@ -521,7 +521,7 @@ class accessor : public object_api> { mutable object cache; }; -NAMESPACE_BEGIN(accessor_policies) +PYBIND11_NAMESPACE_BEGIN(accessor_policies) struct obj_attr { using key_type = object; static object get(handle obj, handle key) { return getattr(obj, key); } @@ -598,7 +598,7 @@ struct tuple_item { } } }; -NAMESPACE_END(accessor_policies) +PYBIND11_NAMESPACE_END(accessor_policies) /// STL iterator template used for tuple, list, sequence and dict template @@ -639,7 +639,7 @@ class generic_iterator : public Policy { friend bool operator<=(const It &a, const It &b) { return !(a > b); } }; -NAMESPACE_BEGIN(iterator_policies) +PYBIND11_NAMESPACE_BEGIN(iterator_policies) /// Quick proxy class needed to implement ``operator->`` for iterators which can't return pointers template struct arrow_proxy { @@ -712,7 +712,7 @@ class dict_readonly { PyObject *key = nullptr, *value = nullptr; ssize_t pos = -1; }; -NAMESPACE_END(iterator_policies) +PYBIND11_NAMESPACE_END(iterator_policies) #if !defined(PYPY_VERSION) using tuple_iterator = generic_iterator; @@ -771,7 +771,7 @@ class simple_collector; template class unpacking_collector; -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) // TODO: After the deprecated constructors are removed, this macro can be simplified by // inheriting ctors: `using Parent::Parent`. It's not an option right now because @@ -934,8 +934,8 @@ class str : public object { /// Return string representation -- always returns a new reference, even if already a str static PyObject *raw_str(PyObject *op) { PyObject *str_value = PyObject_Str(op); -#if PY_MAJOR_VERSION < 3 if (!str_value) throw error_already_set(); +#if PY_MAJOR_VERSION < 3 PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); Py_XDECREF(str_value); str_value = unicode; #endif @@ -1046,7 +1046,7 @@ class bool_ : public object { } }; -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) // Converts a value to the given unsigned type. If an error occurs, you get back (Unsigned) -1; // otherwise you get back the unsigned long or unsigned long long value cast to (Unsigned). // (The distinction is critically important when casting a returned -1 error value to some other @@ -1066,7 +1066,7 @@ Unsigned as_unsigned(PyObject *o) { return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; } } -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) class int_ : public object { public: @@ -1337,35 +1337,139 @@ class buffer : public object { class memoryview : public object { public: - explicit memoryview(const buffer_info& info) { - static Py_buffer buf { }; - // Py_buffer uses signed sizes, strides and shape!.. - static std::vector py_strides { }; - static std::vector py_shape { }; - buf.buf = info.ptr; - buf.itemsize = info.itemsize; - buf.format = const_cast(info.format.c_str()); - buf.ndim = (int) info.ndim; - buf.len = info.size; - py_strides.clear(); - py_shape.clear(); - for (size_t i = 0; i < (size_t) info.ndim; ++i) { - py_strides.push_back(info.strides[i]); - py_shape.push_back(info.shape[i]); - } - buf.strides = py_strides.data(); - buf.shape = py_shape.data(); - buf.suboffsets = nullptr; - buf.readonly = info.readonly; - buf.internal = nullptr; + PYBIND11_OBJECT_CVT(memoryview, object, PyMemoryView_Check, PyMemoryView_FromObject) + + /** \rst + Creates ``memoryview`` from ``buffer_info``. - m_ptr = PyMemoryView_FromBuffer(&buf); + ``buffer_info`` must be created from ``buffer::request()``. Otherwise + throws an exception. + + For creating a ``memoryview`` from objects that support buffer protocol, + use ``memoryview(const object& obj)`` instead of this constructor. + \endrst */ + explicit memoryview(const buffer_info& info) { + if (!info.view()) + pybind11_fail("Prohibited to create memoryview without Py_buffer"); + // Note: PyMemoryView_FromBuffer never increments obj reference. + m_ptr = (info.view()->obj) ? + PyMemoryView_FromObject(info.view()->obj) : + PyMemoryView_FromBuffer(info.view()); if (!m_ptr) pybind11_fail("Unable to create memoryview from buffer descriptor"); } - PYBIND11_OBJECT_CVT(memoryview, object, PyMemoryView_Check, PyMemoryView_FromObject) + /** \rst + Creates ``memoryview`` from static buffer. + + This method is meant for providing a ``memoryview`` for C/C++ buffer not + managed by Python. The caller is responsible for managing the lifetime + of ``ptr`` and ``format``, which MUST outlive the memoryview constructed + here. + + See also: Python C API documentation for `PyMemoryView_FromBuffer`_. + + .. _PyMemoryView_FromBuffer: https://docs.python.org/c-api/memoryview.html#c.PyMemoryView_FromBuffer + + :param ptr: Pointer to the buffer. + :param itemsize: Byte size of an element. + :param format: Pointer to the null-terminated format string. For + homogeneous Buffers, this should be set to + ``format_descriptor::value``. + :param shape: Shape of the tensor (1 entry per dimension). + :param strides: Number of bytes between adjacent entries (for each + per dimension). + :param readonly: Flag to indicate if the underlying storage may be + written to. + \endrst */ + static memoryview from_buffer( + void *ptr, ssize_t itemsize, const char *format, + detail::any_container shape, + detail::any_container strides, bool readonly = false); + + static memoryview from_buffer( + const void *ptr, ssize_t itemsize, const char *format, + detail::any_container shape, + detail::any_container strides) { + return memoryview::from_buffer( + const_cast(ptr), itemsize, format, shape, strides, true); + } + + template + static memoryview from_buffer( + T *ptr, detail::any_container shape, + detail::any_container strides, bool readonly = false) { + return memoryview::from_buffer( + reinterpret_cast(ptr), sizeof(T), + format_descriptor::value, shape, strides, readonly); + } + + template + static memoryview from_buffer( + const T *ptr, detail::any_container shape, + detail::any_container strides) { + return memoryview::from_buffer( + const_cast(ptr), shape, strides, true); + } + +#if PY_MAJOR_VERSION >= 3 + /** \rst + Creates ``memoryview`` from static memory. + + This method is meant for providing a ``memoryview`` for C/C++ buffer not + managed by Python. The caller is responsible for managing the lifetime + of ``mem``, which MUST outlive the memoryview constructed here. + + This method is not available in Python 2. + + See also: Python C API documentation for `PyMemoryView_FromBuffer`_. + + .. _PyMemoryView_FromMemory: https://docs.python.org/c-api/memoryview.html#c.PyMemoryView_FromMemory + \endrst */ + static memoryview from_memory(void *mem, ssize_t size, bool readonly = false) { + PyObject* ptr = PyMemoryView_FromMemory( + reinterpret_cast(mem), size, + (readonly) ? PyBUF_READ : PyBUF_WRITE); + if (!ptr) + pybind11_fail("Could not allocate memoryview object!"); + return memoryview(object(ptr, stolen_t{})); + } + + static memoryview from_memory(const void *mem, ssize_t size) { + return memoryview::from_memory(const_cast(mem), size, true); + } +#endif }; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +inline memoryview memoryview::from_buffer( + void *ptr, ssize_t itemsize, const char* format, + detail::any_container shape, + detail::any_container strides, bool readonly) { + size_t ndim = shape->size(); + if (ndim != strides->size()) + pybind11_fail("memoryview: shape length doesn't match strides length"); + ssize_t size = ndim ? 1 : 0; + for (size_t i = 0; i < ndim; ++i) + size *= (*shape)[i]; + Py_buffer view; + view.buf = ptr; + view.obj = nullptr; + view.len = size * itemsize; + view.readonly = static_cast(readonly); + view.itemsize = itemsize; + view.format = const_cast(format); + view.ndim = static_cast(ndim); + view.shape = shape->data(); + view.strides = strides->data(); + view.suboffsets = nullptr; + view.internal = nullptr; + PyObject* obj = PyMemoryView_FromBuffer(&view); + if (!obj) + throw error_already_set(); + return memoryview(object(obj, stolen_t{})); +} +#endif // DOXYGEN_SHOULD_SKIP_THIS /// @} pytypes /// \addtogroup python_builtins @@ -1509,7 +1613,7 @@ class wrapper : public Base { #endif // PY_VERSION_HEX >= 0x03080000 }; -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) template iterator object_api::begin() const { return iter(derived()); } template iterator object_api::end() const { return iterator::sentinel(); } template item_accessor object_api::operator[](handle key) const { @@ -1590,5 +1694,5 @@ PYBIND11_MATH_OPERATOR_BINARY(operator>>=, PyNumber_InPlaceRshift) #undef PYBIND11_MATH_OPERATOR_UNARY #undef PYBIND11_MATH_OPERATOR_BINARY -NAMESPACE_END(detail) -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index 32f8d294ac..6c2bebda87 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -48,8 +48,8 @@ # define PYBIND11_HAS_VARIANT 1 #endif -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) /// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for /// forwarding a container element). Typically used indirect via forwarded_type(), below. @@ -266,7 +266,9 @@ template struct optional_caster { static handle cast(T_ &&src, return_value_policy policy, handle parent) { if (!src) return none().inc_ref(); - policy = return_value_policy_override::policy(policy); + if (!std::is_lvalue_reference::value) { + policy = return_value_policy_override::policy(policy); + } return value_conv::cast(*std::forward(src), policy, parent); } @@ -372,14 +374,14 @@ template struct type_caster> : variant_caster> { }; #endif -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) inline std::ostream &operator<<(std::ostream &os, const handle &obj) { os << (std::string) str(obj); return os; } -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) #if defined(_MSC_VER) #pragma warning(pop) diff --git a/include/pybind11/stl_bind.h b/include/pybind11/stl_bind.h index da233eca99..61b94b6220 100644 --- a/include/pybind11/stl_bind.h +++ b/include/pybind11/stl_bind.h @@ -15,8 +15,8 @@ #include #include -NAMESPACE_BEGIN(PYBIND11_NAMESPACE) -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) /* SFINAE helper class used by 'is_comparable */ template struct container_traits { @@ -413,7 +413,7 @@ vector_buffer(Class_& cl) { template enable_if_t...>::value> vector_buffer(Class_&) {} -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) // // std::vector @@ -511,7 +511,7 @@ class_ bind_vector(handle scope, std::string const &name, A // std::map, std::unordered_map // -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(detail) /* Fallback functions */ template void map_if_insertion_operator(const Args &...) { } @@ -577,7 +577,7 @@ template auto map_if_insertion_operator(Class_ & } -NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(detail) template , typename... Args> class_ bind_map(handle scope, const std::string &name, Args&&... args) { @@ -653,4 +653,4 @@ class_ bind_map(handle scope, const std::string &name, Args&&. return cl; } -NAMESPACE_END(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/pybind11/__init__.py b/pybind11/__init__.py index 4b1de3efaa..5b2f83d5cd 100644 --- a/pybind11/__init__.py +++ b/pybind11/__init__.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from ._version import version_info, __version__ # noqa: F401 imported but unused diff --git a/pybind11/__main__.py b/pybind11/__main__.py index 89b263a8ad..5e393cc8f1 100644 --- a/pybind11/__main__.py +++ b/pybind11/__main__.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from __future__ import print_function import argparse diff --git a/pybind11/_version.py b/pybind11/_version.py index d0eb659bd5..1f2f254ce5 100644 --- a/pybind11/_version.py +++ b/pybind11/_version.py @@ -1,2 +1,3 @@ +# -*- coding: utf-8 -*- version_info = (2, 5, 'dev1') __version__ = '.'.join(map(str, version_info)) diff --git a/setup.py b/setup.py index 668b56a5b0..577a6b6c37 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- # Setup script for PyPI; use CMakeFile.txt to build extension modules diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 553c6259e8..a7a9d94078 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,79 +5,97 @@ # All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.7) -option(PYBIND11_WERROR "Report all warnings as errors" OFF) -set(PYBIND11_TEST_OVERRIDE "" CACHE STRING "Tests from ;-separated list of *.cpp files will be built instead of all tests") +# VERSION 3.7...3.18, but some versions of VS have a patched CMake 3.11 +# that do not work properly with this syntax, so using the following workaround: +if(${CMAKE_VERSION} VERSION_LESS 3.18) + cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) +else() + cmake_policy(VERSION 3.18) +endif() + +# There's no harm in including a project in a project +project(pybind11_tests CXX) -if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) - # We're being loaded directly, i.e. not via add_subdirectory, so make this - # work as its own project and load the pybind11Config to get the tools we need - project(pybind11_tests CXX) +option(PYBIND11_WERROR "Report all warnings as errors" OFF) +option(DOWNLOAD_EIGEN "Download EIGEN (requires CMake 3.11+)" OFF) +set(PYBIND11_TEST_OVERRIDE + "" + CACHE STRING "Tests from ;-separated list of *.cpp files will be built instead of all tests") - find_package(pybind11 REQUIRED CONFIG) +if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + # We're being loaded directly, i.e. not via add_subdirectory, so make this + # work as its own project and load the pybind11Config to get the tools we need + find_package(pybind11 REQUIRED CONFIG) endif() if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting tests build type to MinSizeRel as none was specified") - set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "Choose the type of build." FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" - "MinSizeRel" "RelWithDebInfo") + set(CMAKE_BUILD_TYPE + MinSizeRel + CACHE STRING "Choose the type of build." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" + "RelWithDebInfo") endif() # Full set of test files (you can override these; see below) set(PYBIND11_TEST_FILES - test_async.cpp - test_buffers.cpp - test_builtin_casters.cpp - test_call_policies.cpp - test_callbacks.cpp - test_chrono.cpp - test_class.cpp - test_constants_and_functions.cpp - test_copy_move.cpp - test_docstring_options.cpp - test_eigen.cpp - test_enum.cpp - test_eval.cpp - test_exceptions.cpp - test_factory_constructors.cpp - test_gil_scoped.cpp - test_iostream.cpp - test_kwargs_and_defaults.cpp - test_local_bindings.cpp - test_methods_and_attributes.cpp - test_modules.cpp - test_multiple_inheritance.cpp - test_numpy_array.cpp - test_numpy_dtypes.cpp - test_numpy_vectorize.cpp - test_opaque_types.cpp - test_operator_overloading.cpp - test_ownership_transfer.cpp - test_pickling.cpp - test_pytypes.cpp - test_sequences_and_iterators.cpp - test_smart_ptr.cpp - test_stl.cpp - test_stl_binders.cpp - test_tagbased_polymorphic.cpp - test_union.cpp - test_virtual_functions.cpp -) + test_async.cpp + test_buffers.cpp + test_builtin_casters.cpp + test_call_policies.cpp + test_callbacks.cpp + test_chrono.cpp + test_class.cpp + test_constants_and_functions.cpp + test_copy_move.cpp + test_custom_type_casters.cpp + test_docstring_options.cpp + test_eigen.cpp + test_enum.cpp + test_eval.cpp + test_exceptions.cpp + test_factory_constructors.cpp + test_gil_scoped.cpp + test_iostream.cpp + test_kwargs_and_defaults.cpp + test_local_bindings.cpp + test_methods_and_attributes.cpp + test_modules.cpp + test_multiple_inheritance.cpp + test_numpy_array.cpp + test_numpy_dtypes.cpp + test_numpy_vectorize.cpp + test_opaque_types.cpp + test_operator_overloading.cpp + test_ownership_transfer.cpp + test_pickling.cpp + test_pytypes.cpp + test_sequences_and_iterators.cpp + test_smart_ptr.cpp + test_stl.cpp + test_stl_binders.cpp + test_tagbased_polymorphic.cpp + test_union.cpp + test_virtual_functions.cpp) # Invoking cmake with something like: # cmake -DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_picking.cpp" .. # lets you override the tests that get compiled and run. You can restore to all tests with: # cmake -DPYBIND11_TEST_OVERRIDE= .. -if (PYBIND11_TEST_OVERRIDE) +if(PYBIND11_TEST_OVERRIDE) set(PYBIND11_TEST_FILES ${PYBIND11_TEST_OVERRIDE}) endif() # Skip test_async for Python < 3.5 list(FIND PYBIND11_TEST_FILES test_async.cpp PYBIND11_TEST_FILES_ASYNC_I) -if((PYBIND11_TEST_FILES_ASYNC_I GREATER -1) AND ("${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}" VERSION_LESS 3.5)) - message(STATUS "Skipping test_async because Python version ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR} < 3.5") +if((PYBIND11_TEST_FILES_ASYNC_I GREATER -1) AND ("${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}" + VERSION_LESS 3.5)) + message( + STATUS + "Skipping test_async because Python version ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR} < 3.5" + ) list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_ASYNC_I}) endif() @@ -86,16 +104,10 @@ string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_FILES}") # Contains the set of test files that require pybind11_cross_module_tests to be # built; if none of these are built (i.e. because TEST_OVERRIDE is used and # doesn't include them) the second module doesn't get built. -set(PYBIND11_CROSS_MODULE_TESTS - test_exceptions.py - test_local_bindings.py - test_stl.py - test_stl_binders.py -) +set(PYBIND11_CROSS_MODULE_TESTS test_exceptions.py test_local_bindings.py test_stl.py + test_stl_binders.py) -set(PYBIND11_CROSS_MODULE_GIL_TESTS - test_gil_scoped.py -) +set(PYBIND11_CROSS_MODULE_GIL_TESTS test_gil_scoped.py) # Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but # keep it in PYBIND11_PYTEST_FILES, so that we get the "eigen is not installed" @@ -105,18 +117,40 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) # Try loading via newer Eigen's Eigen3Config first (bypassing tools/FindEigen3.cmake). # Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also # produces a fatal error if loaded from a pre-3.0 cmake. - if (NOT CMAKE_VERSION VERSION_LESS 3.0) + if(DOWNLOAD_EIGEN) + if(CMAKE_VERSION VERSION_LESS 3.11) + message(FATAL_ERROR "CMake 3.11+ required when using DOWNLOAD_EIGEN") + endif() + + set(EIGEN3_VERSION_STRING "3.3.7") + + include(FetchContent) + FetchContent_Declare( + eigen + GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git + GIT_TAG ${EIGEN3_VERSION_STRING}) + + FetchContent_GetProperties(eigen) + if(NOT eigen_POPULATED) + message(STATUS "Downloading Eigen") + FetchContent_Populate(eigen) + endif() + + set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR}) + set(EIGEN3_FOUND TRUE) + else() find_package(Eigen3 3.2.7 QUIET CONFIG) - if (EIGEN3_FOUND) - if (EIGEN3_VERSION_STRING AND NOT EIGEN3_VERSION_STRING VERSION_LESS 3.3.1) - set(PYBIND11_EIGEN_VIA_TARGET 1) + if(EIGEN3_FOUND) + if(EIGEN3_VERSION_STRING AND NOT EIGEN3_VERSION_STRING VERSION_LESS 3.3.1) + set(PYBIND11_EIGEN_VIA_TARGET TRUE) endif() endif() - endif() - if (NOT EIGEN3_FOUND) - # Couldn't load via target, so fall back to allowing module mode finding, which will pick up - # tools/FindEigen3.cmake - find_package(Eigen3 3.2.7 QUIET) + + if(NOT EIGEN3_FOUND) + # Couldn't load via target, so fall back to allowing module mode finding, which will pick up + # tools/FindEigen3.cmake + find_package(Eigen3 3.2.7 QUIET) + endif() endif() if(EIGEN3_FOUND) @@ -129,7 +163,7 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) message(STATUS "Building tests with Eigen v${EIGEN3_VERSION}") else() list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I}) - message(STATUS "Building tests WITHOUT Eigen") + message(STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN on CMake 3.11+ to download") endif() endif() @@ -141,7 +175,8 @@ function(pybind11_enable_warnings target_name) if(MSVC) target_compile_options(${target_name} PRIVATE /W4) elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)") - target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wconversion -Wcast-qual -Wdeprecated) + target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wconversion -Wcast-qual + -Wdeprecated) endif() if(PYBIND11_WERROR) @@ -151,6 +186,17 @@ function(pybind11_enable_warnings target_name) target_compile_options(${target_name} PRIVATE -Werror) endif() endif() + + # Needs to be readded since the ordering requires these to be after the ones above + if(CMAKE_CXX_STANDARD + AND CMAKE_CXX_COMPILER_ID MATCHES "Clang" + AND PYTHON_VERSION VERSION_LESS 3.0) + if(CMAKE_CXX_STANDARD LESS 17) + target_compile_options(${target_name} PUBLIC -Wno-deprecated-register) + else() + target_compile_options(${target_name} PUBLIC -Wno-register) + endif() + endif() endfunction() set(test_targets pybind11_tests) @@ -158,7 +204,7 @@ set(test_targets pybind11_tests) # Build pybind11_cross_module_tests if any test_whatever.py are being built that require it foreach(t ${PYBIND11_CROSS_MODULE_TESTS}) list(FIND PYBIND11_PYTEST_FILES ${t} i) - if (i GREATER -1) + if(i GREATER -1) list(APPEND test_targets pybind11_cross_module_tests) break() endif() @@ -166,7 +212,7 @@ endforeach() foreach(t ${PYBIND11_CROSS_MODULE_GIL_TESTS}) list(FIND PYBIND11_PYTEST_FILES ${t} i) - if (i GREATER -1) + if(i GREATER -1) list(APPEND test_targets cross_module_gil_utils) break() endif() @@ -175,7 +221,7 @@ endforeach() set(testdir ${CMAKE_CURRENT_SOURCE_DIR}) foreach(target ${test_targets}) set(test_files ${PYBIND11_TEST_FILES}) - if(NOT target STREQUAL "pybind11_tests") + if(NOT "${target}" STREQUAL "pybind11_tests") set(test_files "") endif() @@ -188,7 +234,7 @@ foreach(target ${test_targets}) endif() if(EIGEN3_FOUND) - if (PYBIND11_EIGEN_VIA_TARGET) + if(PYBIND11_EIGEN_VIA_TARGET) target_link_libraries(${target} PRIVATE Eigen3::Eigen) else() target_include_directories(${target} PRIVATE ${EIGEN3_INCLUDE_DIR}) @@ -203,18 +249,21 @@ foreach(target ${test_targets}) # Always write the output file directly into the 'tests' directory (even on MSVC) if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) - set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${testdir}) + set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${testdir}") foreach(config ${CMAKE_CONFIGURATION_TYPES}) string(TOUPPER ${config} config) - set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} ${testdir}) + set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} "${testdir}") endforeach() endif() endforeach() # Make sure pytest is found or produce a fatal error if(NOT PYBIND11_PYTEST_FOUND) - execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import pytest; print(pytest.__version__)" - RESULT_VARIABLE pytest_not_found OUTPUT_VARIABLE pytest_version ERROR_QUIET) + execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "import pytest; print(pytest.__version__)" + RESULT_VARIABLE pytest_not_found + OUTPUT_VARIABLE pytest_version + ERROR_QUIET) if(pytest_not_found) message(FATAL_ERROR "Running the tests requires pytest. Please install it manually" " (try: ${PYTHON_EXECUTABLE} -m pip install pytest)") @@ -222,22 +271,25 @@ if(NOT PYBIND11_PYTEST_FOUND) message(FATAL_ERROR "Running the tests requires pytest >= 3.0. Found: ${pytest_version}" "Please update it (try: ${PYTHON_EXECUTABLE} -m pip install -U pytest)") endif() - set(PYBIND11_PYTEST_FOUND TRUE CACHE INTERNAL "") -endif() - -if(CMAKE_VERSION VERSION_LESS 3.2) - set(PYBIND11_USES_TERMINAL "") -else() - set(PYBIND11_USES_TERMINAL "USES_TERMINAL") + set(PYBIND11_PYTEST_FOUND + TRUE + CACHE INTERNAL "") endif() # A single command to compile and run the tests -add_custom_target(pytest COMMAND ${PYTHON_EXECUTABLE} -m pytest ${PYBIND11_PYTEST_FILES} - DEPENDS ${test_targets} WORKING_DIRECTORY ${testdir} ${PYBIND11_USES_TERMINAL}) +add_custom_target( + pytest + COMMAND ${PYTHON_EXECUTABLE} -m pytest ${PYBIND11_PYTEST_FILES} + DEPENDS ${test_targets} + WORKING_DIRECTORY ${testdir} + USES_TERMINAL) if(PYBIND11_TEST_OVERRIDE) - add_custom_command(TARGET pytest POST_BUILD - COMMAND ${CMAKE_COMMAND} -E echo "Note: not all tests run: -DPYBIND11_TEST_OVERRIDE is in effect") + add_custom_command( + TARGET pytest + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo + "Note: not all tests run: -DPYBIND11_TEST_OVERRIDE is in effect") endif() # Add a check target to run all the tests, starting with pytest (we add dependencies to this below) @@ -245,14 +297,18 @@ add_custom_target(check DEPENDS pytest) # The remaining tests only apply when being built as part of the pybind11 project, but not if the # tests are being built independently. -if (NOT PROJECT_NAME STREQUAL "pybind11") +if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) return() endif() # Add a post-build comment to show the primary test suite .so size and, if a previous size, compare it: -add_custom_command(TARGET pybind11_tests POST_BUILD - COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/libsize.py - $ ${CMAKE_CURRENT_BINARY_DIR}/sosize-$.txt) +add_custom_command( + TARGET pybind11_tests + POST_BUILD + COMMAND + ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../tools/libsize.py + $ + ${CMAKE_CURRENT_BINARY_DIR}/sosize-$.txt) # Test embedding the interpreter. Provides the `cpptest` target. add_subdirectory(test_embed) diff --git a/tests/conftest.py b/tests/conftest.py index 57f681c66f..d317c49dbc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """pytest configuration Extends output capture as needed by pybind11: ignore constructors, optional unordered lines. @@ -215,6 +216,11 @@ def pytest_configure(): pytest.requires_eigen_and_scipy = skipif( not have_eigen or not scipy, reason="eigen and/or scipy are not installed") pytest.unsupported_on_pypy = skipif(pypy, reason="unsupported on PyPy") + pytest.bug_in_pypy = pytest.mark.xfail(pypy, reason="bug in PyPy") + pytest.unsupported_on_pypy3 = skipif(pypy and sys.version_info.major >= 3, + reason="unsupported on PyPy3") + pytest.unsupported_on_pypy_lt_6 = skipif(pypy and sys.pypy_version_info[0] < 6, + reason="unsupported on PyPy<6") pytest.unsupported_on_py2 = skipif(sys.version_info.major < 3, reason="unsupported on Python 2.x") pytest.gc_collect = gc_collect diff --git a/tests/constructor_stats.h b/tests/constructor_stats.h index 431e5acef9..abfaf91614 100644 --- a/tests/constructor_stats.h +++ b/tests/constructor_stats.h @@ -273,4 +273,3 @@ template void print_values(T *inst, Values &&...va print_constr_details(inst, ":", values...); track_values(inst, values...); } - diff --git a/tests/pybind11_tests.h b/tests/pybind11_tests.h index 90963a5dea..1e47416270 100644 --- a/tests/pybind11_tests.h +++ b/tests/pybind11_tests.h @@ -53,13 +53,13 @@ class IncType : public UserType { /// Custom cast-only type that casts to a string "rvalue" or "lvalue" depending on the cast context. /// Used to test recursive casters (e.g. std::tuple, stl containers). struct RValueCaster {}; -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(pybind11) +PYBIND11_NAMESPACE_BEGIN(detail) template<> class type_caster { public: PYBIND11_TYPE_CASTER(RValueCaster, _("RValueCaster")); static handle cast(RValueCaster &&, return_value_policy, handle) { return py::str("rvalue").release(); } static handle cast(const RValueCaster &, return_value_policy, handle) { return py::str("lvalue").release(); } }; -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(pybind11) diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 0000000000..c9750e473f --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1,8 @@ +--extra-index-url https://antocuni.github.io/pypy-wheels/manylinux2010/ +numpy==1.16.6; python_version<"3.6" +numpy==1.18.0; platform_python_implementation=="PyPy" and sys_platform=="darwin" and python_version>="3.6" +numpy==1.19.1; (platform_python_implementation!="PyPy" or sys_platform!="darwin") and python_version>="3.6" +pytest==4.6.9; python_version<"3.5" +pytest==5.4.3; python_version>="3.5" +scipy==1.2.3; (platform_python_implementation!="PyPy" or sys_platform!="darwin") and python_version<"3.6" +scipy==1.5.2; (platform_python_implementation!="PyPy" or sys_platform!="darwin") and python_version>="3.6" and python_version<"3.9" diff --git a/tests/test_async.py b/tests/test_async.py index e1c959d602..e9292c9d9c 100644 --- a/tests/test_async.py +++ b/tests/test_async.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import asyncio import pytest from pybind11_tests import async_module as m diff --git a/tests/test_buffers.py b/tests/test_buffers.py index bf7aaed70d..e264311d7c 100644 --- a/tests/test_buffers.py +++ b/tests/test_buffers.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import io import struct import sys diff --git a/tests/test_builtin_casters.cpp b/tests/test_builtin_casters.cpp index 9c0e45549f..c782870b14 100644 --- a/tests/test_builtin_casters.cpp +++ b/tests/test_builtin_casters.cpp @@ -117,12 +117,16 @@ TEST_SUBMODULE(builtin_casters, m) { return std::make_pair(RValueCaster{}, std::make_tuple(RValueCaster{}, std::make_pair(RValueCaster{}, RValueCaster{}))); }); m.def("lvalue_nested", []() -> const decltype(lvnested) & { return lvnested; }); + static std::pair int_string_pair{2, "items"}; + m.def("int_string_pair", []() { return &int_string_pair; }); + // test_builtins_cast_return_none m.def("return_none_string", []() -> std::string * { return nullptr; }); m.def("return_none_char", []() -> const char * { return nullptr; }); m.def("return_none_bool", []() -> bool * { return nullptr; }); m.def("return_none_int", []() -> int * { return nullptr; }); m.def("return_none_float", []() -> float * { return nullptr; }); + m.def("return_none_pair", []() -> std::pair * { return nullptr; }); // test_none_deferred m.def("defer_none_cstring", [](char *) { return false; }); diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py index 3d1470593a..e4a5235c04 100644 --- a/tests/test_builtin_casters.py +++ b/tests/test_builtin_casters.py @@ -1,4 +1,4 @@ -# Python < 3 needs this: coding=utf-8 +# -*- coding: utf-8 -*- import pytest from pybind11_tests import builtin_casters as m @@ -132,24 +132,24 @@ def test_string_view(capture): """Tests support for C++17 string_view arguments and return values""" assert m.string_view_chars("Hi") == [72, 105] assert m.string_view_chars("Hi 🎂") == [72, 105, 32, 0xf0, 0x9f, 0x8e, 0x82] - assert m.string_view16_chars("Hi 🎂") == [72, 105, 32, 0xd83c, 0xdf82] - assert m.string_view32_chars("Hi 🎂") == [72, 105, 32, 127874] + assert m.string_view16_chars(u"Hi 🎂") == [72, 105, 32, 0xd83c, 0xdf82] + assert m.string_view32_chars(u"Hi 🎂") == [72, 105, 32, 127874] if hasattr(m, "has_u8string"): assert m.string_view8_chars("Hi") == [72, 105] - assert m.string_view8_chars("Hi 🎂") == [72, 105, 32, 0xf0, 0x9f, 0x8e, 0x82] + assert m.string_view8_chars(u"Hi 🎂") == [72, 105, 32, 0xf0, 0x9f, 0x8e, 0x82] - assert m.string_view_return() == "utf8 secret 🎂" - assert m.string_view16_return() == "utf16 secret 🎂" - assert m.string_view32_return() == "utf32 secret 🎂" + assert m.string_view_return() == u"utf8 secret 🎂" + assert m.string_view16_return() == u"utf16 secret 🎂" + assert m.string_view32_return() == u"utf32 secret 🎂" if hasattr(m, "has_u8string"): - assert m.string_view8_return() == "utf8 secret 🎂" + assert m.string_view8_return() == u"utf8 secret 🎂" with capture: m.string_view_print("Hi") m.string_view_print("utf8 🎂") - m.string_view16_print("utf16 🎂") - m.string_view32_print("utf32 🎂") - assert capture == """ + m.string_view16_print(u"utf16 🎂") + m.string_view32_print(u"utf32 🎂") + assert capture == u""" Hi 2 utf8 🎂 9 utf16 🎂 8 @@ -158,8 +158,8 @@ def test_string_view(capture): if hasattr(m, "has_u8string"): with capture: m.string_view8_print("Hi") - m.string_view8_print("utf8 🎂") - assert capture == """ + m.string_view8_print(u"utf8 🎂") + assert capture == u""" Hi 2 utf8 🎂 9 """ @@ -167,9 +167,9 @@ def test_string_view(capture): with capture: m.string_view_print("Hi, ascii") m.string_view_print("Hi, utf8 🎂") - m.string_view16_print("Hi, utf16 🎂") - m.string_view32_print("Hi, utf32 🎂") - assert capture == """ + m.string_view16_print(u"Hi, utf16 🎂") + m.string_view32_print(u"Hi, utf32 🎂") + assert capture == u""" Hi, ascii 9 Hi, utf8 🎂 13 Hi, utf16 🎂 12 @@ -178,8 +178,8 @@ def test_string_view(capture): if hasattr(m, "has_u8string"): with capture: m.string_view8_print("Hi, ascii") - m.string_view8_print("Hi, utf8 🎂") - assert capture == """ + m.string_view8_print(u"Hi, utf8 🎂") + assert capture == u""" Hi, ascii 9 Hi, utf8 🎂 13 """ @@ -250,6 +250,8 @@ def test_tuple(doc): assert m.rvalue_nested() == ("rvalue", ("rvalue", ("rvalue", "rvalue"))) assert m.lvalue_nested() == ("lvalue", ("lvalue", ("lvalue", "lvalue"))) + assert m.int_string_pair() == (2, "items") + def test_builtins_cast_return_none(): """Casters produced with PYBIND11_TYPE_CASTER() should convert nullptr to None""" @@ -258,6 +260,7 @@ def test_builtins_cast_return_none(): assert m.return_none_bool() is None assert m.return_none_int() is None assert m.return_none_float() is None + assert m.return_none_pair() is None def test_none_deferred(): diff --git a/tests/test_call_policies.cpp b/tests/test_call_policies.cpp index fd24557834..26c83f81b0 100644 --- a/tests/test_call_policies.cpp +++ b/tests/test_call_policies.cpp @@ -46,6 +46,7 @@ TEST_SUBMODULE(call_policies, m) { class Parent { public: Parent() { py::print("Allocating parent."); } + Parent(const Parent& parent) = default; ~Parent() { py::print("Releasing parent."); } void addChild(Child *) { } Child *returnChild() { return new Child(); } diff --git a/tests/test_call_policies.py b/tests/test_call_policies.py index 7c835599c2..0e3230c573 100644 --- a/tests/test_call_policies.py +++ b/tests/test_call_policies.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import call_policies as m from pybind11_tests import ConstructorStats diff --git a/tests/test_callbacks.py b/tests/test_callbacks.py index 6439c8e72a..d5d0e045d2 100644 --- a/tests/test_callbacks.py +++ b/tests/test_callbacks.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import callbacks as m from threading import Thread diff --git a/tests/test_chrono.py b/tests/test_chrono.py index 55c9544065..f1817e44f6 100644 --- a/tests/test_chrono.py +++ b/tests/test_chrono.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from pybind11_tests import chrono as m import datetime diff --git a/tests/test_class.cpp b/tests/test_class.cpp index 18616fda5a..77dc3bf1a5 100644 --- a/tests/test_class.cpp +++ b/tests/test_class.cpp @@ -227,6 +227,8 @@ TEST_SUBMODULE(class_, m) { static void *operator new(size_t s, void *ptr) { py::print("C placement-new", s); return ptr; } static void operator delete(void *p, size_t s) { py::print("C delete", s); return ::operator delete(p); } virtual ~AliasedHasOpNewDelSize() = default; + AliasedHasOpNewDelSize() = default; + AliasedHasOpNewDelSize(const AliasedHasOpNewDelSize&) = delete; }; struct PyAliasedHasOpNewDelSize : AliasedHasOpNewDelSize { PyAliasedHasOpNewDelSize() = default; @@ -277,6 +279,8 @@ TEST_SUBMODULE(class_, m) { class ProtectedB { public: virtual ~ProtectedB() = default; + ProtectedB() = default; + ProtectedB(const ProtectedB &) = delete; protected: virtual int foo() const { return value; } @@ -376,6 +380,17 @@ TEST_SUBMODULE(class_, m) { struct IsNonFinalFinal {}; py::class_(m, "IsNonFinalFinal", py::is_final()); + struct PyPrintDestructor { + PyPrintDestructor() {} + ~PyPrintDestructor() { + py::print("Print from destructor"); + } + void throw_something() { throw std::runtime_error("error"); } + }; + py::class_(m, "PyPrintDestructor") + .def(py::init<>()) + .def("throw_something", &PyPrintDestructor::throw_something); + // Test #1922 (drake#11424). class ExampleVirt2 { public: @@ -395,7 +410,11 @@ TEST_SUBMODULE(class_, m) { [](const ExampleVirt2& obj) { return obj.get_name(); }); } -template class BreaksBase { public: virtual ~BreaksBase() = default; }; +template class BreaksBase { public: + virtual ~BreaksBase() = default; + BreaksBase() = default; + BreaksBase(const BreaksBase&) = delete; +}; template class BreaksTramp : public BreaksBase {}; // These should all compile just fine: typedef py::class_, std::unique_ptr>, BreaksTramp<1>> DoesntBreak1; diff --git a/tests/test_class.py b/tests/test_class.py index c42e074e15..5054774f4b 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest import weakref @@ -110,7 +111,10 @@ def __init__(self): pass with pytest.raises(TypeError) as exc_info: Python() - assert msg(exc_info.value) == "m.class_.Pet.__init__() must be called when overriding __init__" + expected = ["m.class_.Pet.__init__() must be called when overriding __init__", + "Pet.__init__() must be called when overriding __init__"] # PyPy? + # TODO: fix PyPy error message wrt. tp_name/__qualname__? + assert msg(exc_info.value) in expected # Multiple bases class RabbitHamster(m.Rabbit, m.Hamster): @@ -119,8 +123,9 @@ def __init__(self): with pytest.raises(TypeError) as exc_info: RabbitHamster() - expected = "m.class_.Hamster.__init__() must be called when overriding __init__" - assert msg(exc_info.value) == expected + expected = ["m.class_.Hamster.__init__() must be called when overriding __init__", + "Hamster.__init__() must be called when overriding __init__"] # PyPy + assert msg(exc_info.value) in expected def test_automatic_upcasting(): @@ -331,6 +336,12 @@ class PyNonFinalFinalChild(m.IsNonFinalFinal): assert str(exc_info.value).endswith("is not an acceptable base type") +# https://github.com/pybind/pybind11/issues/1878 +def test_exception_rvalue_abort(): + with pytest.raises(RuntimeError): + m.PyPrintDestructor().throw_something() + + @pytest.mark.skip( reason="Generally reproducible in CPython, Python 3, non-debug, on Linux. " "However, hard to pin this down for CI.") diff --git a/tests/test_cmake_build/CMakeLists.txt b/tests/test_cmake_build/CMakeLists.txt index c9b5fcb2e7..53228f0eb4 100644 --- a/tests/test_cmake_build/CMakeLists.txt +++ b/tests/test_cmake_build/CMakeLists.txt @@ -1,35 +1,40 @@ add_custom_target(test_cmake_build) -if(CMAKE_VERSION VERSION_LESS 3.1) - # 3.0 needed for interface library for subdirectory_target/installed_target - # 3.1 needed for cmake -E env for testing - return() -endif() - -include(CMakeParseArguments) function(pybind11_add_build_test name) cmake_parse_arguments(ARG "INSTALL" "" "" ${ARGN}) - set(build_options "-DCMAKE_PREFIX_PATH=${PROJECT_BINARY_DIR}/mock_install" - "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" - "-DPYTHON_EXECUTABLE:FILEPATH=${PYTHON_EXECUTABLE}" - "-DPYBIND11_CPP_STANDARD=${PYBIND11_CPP_STANDARD}") + set(build_options + "-DCMAKE_PREFIX_PATH=${pybind11_BINARY_DIR}/mock_install" + "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" + "-DPYTHON_EXECUTABLE:FILEPATH=${PYTHON_EXECUTABLE}") + + if(CMAKE_CXX_STANDARD) + list(APPEND build_options "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}") + endif() + if(NOT ARG_INSTALL) - list(APPEND build_options "-DPYBIND11_PROJECT_DIR=${PROJECT_SOURCE_DIR}") + list(APPEND build_options "-DPYBIND11_PROJECT_DIR=${pybind11_SOURCE_DIR}") endif() - add_custom_target(test_${name} ${CMAKE_CTEST_COMMAND} - --quiet --output-log ${name}.log - --build-and-test "${CMAKE_CURRENT_SOURCE_DIR}/${name}" - "${CMAKE_CURRENT_BINARY_DIR}/${name}" - --build-config Release + add_custom_target( + test_${name} + ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMAKE_CURRENT_SOURCE_DIR}/${name}" + "${CMAKE_CURRENT_BINARY_DIR}/${name}" + --build-config + Release --build-noclean - --build-generator ${CMAKE_GENERATOR} - $<$:--build-generator-platform> ${CMAKE_GENERATOR_PLATFORM} - --build-makeprogram ${CMAKE_MAKE_PROGRAM} - --build-target check - --build-options ${build_options} - ) + --build-generator + ${CMAKE_GENERATOR} + $<$:--build-generator-platform> + ${CMAKE_GENERATOR_PLATFORM} + --build-makeprogram + ${CMAKE_MAKE_PROGRAM} + --build-target + check + --build-options + ${build_options}) if(ARG_INSTALL) add_dependencies(test_${name} mock_install) endif() @@ -43,10 +48,9 @@ if(NOT ${PYTHON_MODULE_EXTENSION} MATCHES "pypy") endif() if(PYBIND11_INSTALL) - add_custom_target(mock_install ${CMAKE_COMMAND} - "-DCMAKE_INSTALL_PREFIX=${PROJECT_BINARY_DIR}/mock_install" - -P "${PROJECT_BINARY_DIR}/cmake_install.cmake" - ) + add_custom_target( + mock_install ${CMAKE_COMMAND} "-DCMAKE_INSTALL_PREFIX=${pybind11_BINARY_DIR}/mock_install" -P + "${pybind11_BINARY_DIR}/cmake_install.cmake") pybind11_add_build_test(installed_function INSTALL) pybind11_add_build_test(installed_target INSTALL) diff --git a/tests/test_cmake_build/installed_embed/CMakeLists.txt b/tests/test_cmake_build/installed_embed/CMakeLists.txt index f7fc09c219..78855afa26 100644 --- a/tests/test_cmake_build/installed_embed/CMakeLists.txt +++ b/tests/test_cmake_build/installed_embed/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.7) project(test_installed_embed CXX) set(CMAKE_MODULE_PATH "") diff --git a/tests/test_cmake_build/installed_function/CMakeLists.txt b/tests/test_cmake_build/installed_function/CMakeLists.txt index e0c20a8a36..3ad5445e3f 100644 --- a/tests/test_cmake_build/installed_function/CMakeLists.txt +++ b/tests/test_cmake_build/installed_function/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.7) project(test_installed_module CXX) set(CMAKE_MODULE_PATH "") @@ -8,5 +8,12 @@ message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") pybind11_add_module(test_cmake_build SHARED NO_EXTRAS ../main.cpp) -add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$ - ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) +add_custom_target( + check + ${CMAKE_COMMAND} + -E + env + PYTHONPATH=$ + ${PYTHON_EXECUTABLE} + ${PROJECT_SOURCE_DIR}/../test.py + ${PROJECT_NAME}) diff --git a/tests/test_cmake_build/installed_target/CMakeLists.txt b/tests/test_cmake_build/installed_target/CMakeLists.txt index cd3ae6f7d8..348c419cd3 100644 --- a/tests/test_cmake_build/installed_target/CMakeLists.txt +++ b/tests/test_cmake_build/installed_target/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.7) project(test_installed_target CXX) set(CMAKE_MODULE_PATH "") @@ -18,5 +18,12 @@ set_target_properties(test_cmake_build PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX # This may be needed to resolve header conflicts, e.g. between Python release and debug headers. set_target_properties(test_cmake_build PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) -add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$ - ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) +add_custom_target( + check + ${CMAKE_COMMAND} + -E + env + PYTHONPATH=$ + ${PYTHON_EXECUTABLE} + ${PROJECT_SOURCE_DIR}/../test.py + ${PROJECT_NAME}) diff --git a/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt b/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt index 88ba60dd52..eea0eeea3d 100644 --- a/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt +++ b/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt @@ -1,7 +1,9 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.7) project(test_subdirectory_embed CXX) -set(PYBIND11_INSTALL ON CACHE BOOL "") +set(PYBIND11_INSTALL + ON + CACHE BOOL "") set(PYBIND11_EXPORT_NAME test_export) add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) @@ -16,10 +18,10 @@ add_custom_target(check $ ${PROJECT_SOURCE_DIR}/.. add_library(test_embed_lib ../embed.cpp) target_link_libraries(test_embed_lib PRIVATE pybind11::embed) -install(TARGETS test_embed_lib - EXPORT test_export - ARCHIVE DESTINATION bin - LIBRARY DESTINATION lib - RUNTIME DESTINATION lib) -install(EXPORT test_export - DESTINATION lib/cmake/test_export/test_export-Targets.cmake) +install( + TARGETS test_embed_lib + EXPORT test_export + ARCHIVE DESTINATION bin + LIBRARY DESTINATION lib + RUNTIME DESTINATION lib) +install(EXPORT test_export DESTINATION lib/cmake/test_export/test_export-Targets.cmake) diff --git a/tests/test_cmake_build/subdirectory_function/CMakeLists.txt b/tests/test_cmake_build/subdirectory_function/CMakeLists.txt index 278007aebd..e4518044ed 100644 --- a/tests/test_cmake_build/subdirectory_function/CMakeLists.txt +++ b/tests/test_cmake_build/subdirectory_function/CMakeLists.txt @@ -1,8 +1,15 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.7) project(test_subdirectory_module CXX) add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) pybind11_add_module(test_cmake_build THIN_LTO ../main.cpp) -add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$ - ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) +add_custom_target( + check + ${CMAKE_COMMAND} + -E + env + PYTHONPATH=$ + ${PYTHON_EXECUTABLE} + ${PROJECT_SOURCE_DIR}/../test.py + ${PROJECT_NAME}) diff --git a/tests/test_cmake_build/subdirectory_target/CMakeLists.txt b/tests/test_cmake_build/subdirectory_target/CMakeLists.txt index 6b142d62a9..f84140ce04 100644 --- a/tests/test_cmake_build/subdirectory_target/CMakeLists.txt +++ b/tests/test_cmake_build/subdirectory_target/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.7) project(test_subdirectory_target CXX) add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) @@ -11,5 +11,12 @@ target_link_libraries(test_cmake_build PRIVATE pybind11::module) set_target_properties(test_cmake_build PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" SUFFIX "${PYTHON_MODULE_EXTENSION}") -add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$ - ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) +add_custom_target( + check + ${CMAKE_COMMAND} + -E + env + PYTHONPATH=$ + ${PYTHON_EXECUTABLE} + ${PROJECT_SOURCE_DIR}/../test.py + ${PROJECT_NAME}) diff --git a/tests/test_cmake_build/test.py b/tests/test_cmake_build/test.py index 1467a61dc0..87ed5135ff 100644 --- a/tests/test_cmake_build/test.py +++ b/tests/test_cmake_build/test.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import sys import test_cmake_build diff --git a/tests/test_constants_and_functions.py b/tests/test_constants_and_functions.py index 472682d619..36b1aa64b1 100644 --- a/tests/test_constants_and_functions.py +++ b/tests/test_constants_and_functions.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from pybind11_tests import constants_and_functions as m diff --git a/tests/test_copy_move.cpp b/tests/test_copy_move.cpp index 98d5e0a0bd..0f698bdf05 100644 --- a/tests/test_copy_move.cpp +++ b/tests/test_copy_move.cpp @@ -68,8 +68,8 @@ class CopyOnlyInt { int value; }; -NAMESPACE_BEGIN(pybind11) -NAMESPACE_BEGIN(detail) +PYBIND11_NAMESPACE_BEGIN(pybind11) +PYBIND11_NAMESPACE_BEGIN(detail) template <> struct type_caster { PYBIND11_TYPE_CASTER(MoveOnlyInt, _("MoveOnlyInt")); bool load(handle src, bool) { value = MoveOnlyInt(src.cast()); return true; } @@ -97,8 +97,8 @@ template <> struct type_caster { operator CopyOnlyInt&() { return value; } template using cast_op_type = pybind11::detail::cast_op_type; }; -NAMESPACE_END(detail) -NAMESPACE_END(pybind11) +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(pybind11) TEST_SUBMODULE(copy_move_policies, m) { // test_lacking_copy_ctor diff --git a/tests/test_copy_move.py b/tests/test_copy_move.py index 0e671d9696..6b53993a91 100644 --- a/tests/test_copy_move.py +++ b/tests/test_copy_move.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import copy_move_policies as m diff --git a/tests/test_custom_type_casters.cpp b/tests/test_custom_type_casters.cpp new file mode 100644 index 0000000000..9485d3cdb2 --- /dev/null +++ b/tests/test_custom_type_casters.cpp @@ -0,0 +1,125 @@ +/* + tests/test_custom_type_casters.cpp -- tests type_caster + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include "constructor_stats.h" + + +// py::arg/py::arg_v testing: these arguments just record their argument when invoked +class ArgInspector1 { public: std::string arg = "(default arg inspector 1)"; }; +class ArgInspector2 { public: std::string arg = "(default arg inspector 2)"; }; +class ArgAlwaysConverts { }; +namespace pybind11 { namespace detail { +template <> struct type_caster { +public: + PYBIND11_TYPE_CASTER(ArgInspector1, _("ArgInspector1")); + + bool load(handle src, bool convert) { + value.arg = "loading ArgInspector1 argument " + + std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. " + "Argument value = " + (std::string) str(src); + return true; + } + + static handle cast(const ArgInspector1 &src, return_value_policy, handle) { + return str(src.arg).release(); + } +}; +template <> struct type_caster { +public: + PYBIND11_TYPE_CASTER(ArgInspector2, _("ArgInspector2")); + + bool load(handle src, bool convert) { + value.arg = "loading ArgInspector2 argument " + + std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. " + "Argument value = " + (std::string) str(src); + return true; + } + + static handle cast(const ArgInspector2 &src, return_value_policy, handle) { + return str(src.arg).release(); + } +}; +template <> struct type_caster { +public: + PYBIND11_TYPE_CASTER(ArgAlwaysConverts, _("ArgAlwaysConverts")); + + bool load(handle, bool convert) { + return convert; + } + + static handle cast(const ArgAlwaysConverts &, return_value_policy, handle) { + return py::none().release(); + } +}; +}} + +// test_custom_caster_destruction +class DestructionTester { +public: + DestructionTester() { print_default_created(this); } + ~DestructionTester() { print_destroyed(this); } + DestructionTester(const DestructionTester &) { print_copy_created(this); } + DestructionTester(DestructionTester &&) { print_move_created(this); } + DestructionTester &operator=(const DestructionTester &) { print_copy_assigned(this); return *this; } + DestructionTester &operator=(DestructionTester &&) { print_move_assigned(this); return *this; } +}; +namespace pybind11 { namespace detail { +template <> struct type_caster { + PYBIND11_TYPE_CASTER(DestructionTester, _("DestructionTester")); + bool load(handle, bool) { return true; } + + static handle cast(const DestructionTester &, return_value_policy, handle) { + return py::bool_(true).release(); + } +}; +}} + +TEST_SUBMODULE(custom_type_casters, m) { + // test_custom_type_casters + + // test_noconvert_args + // + // Test converting. The ArgAlwaysConverts is just there to make the first no-conversion pass + // fail so that our call always ends up happening via the second dispatch (the one that allows + // some conversion). + class ArgInspector { + public: + ArgInspector1 f(ArgInspector1 a, ArgAlwaysConverts) { return a; } + std::string g(ArgInspector1 a, const ArgInspector1 &b, int c, ArgInspector2 *d, ArgAlwaysConverts) { + return a.arg + "\n" + b.arg + "\n" + std::to_string(c) + "\n" + d->arg; + } + static ArgInspector2 h(ArgInspector2 a, ArgAlwaysConverts) { return a; } + }; + 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()) + ; + 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()); + + 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()); + + // test_custom_caster_destruction + // Test that `take_ownership` works on types with a custom type caster when given a pointer + + // default policy: don't take ownership: + m.def("custom_caster_no_destroy", []() { static auto *dt = new DestructionTester(); return dt; }); + + m.def("custom_caster_destroy", []() { return new DestructionTester(); }, + py::return_value_policy::take_ownership); // Takes ownership: destroy when finished + m.def("custom_caster_destroy_const", []() -> const DestructionTester * { return new DestructionTester(); }, + py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction) + m.def("destruction_tester_cstats", &ConstructorStats::get, py::return_value_policy::reference); +} diff --git a/tests/test_custom_type_casters.py b/tests/test_custom_type_casters.py new file mode 100644 index 0000000000..9475c45168 --- /dev/null +++ b/tests/test_custom_type_casters.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- +import pytest +from pybind11_tests import custom_type_casters as m + + +def test_noconvert_args(msg): + a = m.ArgInspector() + assert msg(a.f("hi")) == """ + loading ArgInspector1 argument WITH conversion allowed. Argument value = hi + """ + assert msg(a.g("this is a", "this is b")) == """ + loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a + loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b + 13 + loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2) + """ # noqa: E501 line too long + assert msg(a.g("this is a", "this is b", 42)) == """ + loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a + loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b + 42 + loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2) + """ # noqa: E501 line too long + assert msg(a.g("this is a", "this is b", 42, "this is d")) == """ + loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a + loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b + 42 + loading ArgInspector2 argument WITH conversion allowed. Argument value = this is d + """ + assert (a.h("arg 1") == + "loading ArgInspector2 argument WITHOUT conversion allowed. Argument value = arg 1") + assert msg(m.arg_inspect_func("A1", "A2")) == """ + loading ArgInspector2 argument WITH conversion allowed. Argument value = A1 + loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = A2 + """ + + assert m.floats_preferred(4) == 2.0 + assert m.floats_only(4.0) == 2.0 + with pytest.raises(TypeError) as excinfo: + m.floats_only(4) + assert msg(excinfo.value) == """ + floats_only(): incompatible function arguments. The following argument types are supported: + 1. (f: float) -> float + + Invoked with: 4 + """ + + assert m.ints_preferred(4) == 2 + assert m.ints_preferred(True) == 0 + with pytest.raises(TypeError) as excinfo: + m.ints_preferred(4.0) + assert msg(excinfo.value) == """ + ints_preferred(): incompatible function arguments. The following argument types are supported: + 1. (i: int) -> int + + Invoked with: 4.0 + """ # noqa: E501 line too long + + assert m.ints_only(4) == 2 + with pytest.raises(TypeError) as excinfo: + m.ints_only(4.0) + assert msg(excinfo.value) == """ + ints_only(): incompatible function arguments. The following argument types are supported: + 1. (i: int) -> int + + Invoked with: 4.0 + """ + + +def test_custom_caster_destruction(): + """Tests that returning a pointer to a type that gets converted with a custom type caster gets + destroyed when the function has py::return_value_policy::take_ownership policy applied.""" + + cstats = m.destruction_tester_cstats() + # This one *doesn't* have take_ownership: the pointer should be used but not destroyed: + z = m.custom_caster_no_destroy() + assert cstats.alive() == 1 and cstats.default_constructions == 1 + assert z + + # take_ownership applied: this constructs a new object, casts it, then destroys it: + z = m.custom_caster_destroy() + assert z + assert cstats.default_constructions == 2 + + # Same, but with a const pointer return (which should *not* inhibit destruction): + z = m.custom_caster_destroy_const() + assert z + assert cstats.default_constructions == 3 + + # Make sure we still only have the original object (from ..._no_destroy()) alive: + assert cstats.alive() == 1 diff --git a/tests/test_docstring_options.py b/tests/test_docstring_options.py index 0dbca609ef..80ade0f158 100644 --- a/tests/test_docstring_options.py +++ b/tests/test_docstring_options.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from pybind11_tests import docstring_options as m diff --git a/tests/test_eigen.py b/tests/test_eigen.py index e2da2d45d4..76191f6b70 100644 --- a/tests/test_eigen.py +++ b/tests/test_eigen.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import ConstructorStats @@ -143,7 +144,7 @@ def test_nonunit_stride_from_python(): counting_3d = np.arange(27.0, dtype=np.float32).reshape((3, 3, 3)) slices = [counting_3d[0, :, :], counting_3d[:, 0, :], counting_3d[:, :, 0]] - for slice_idx, ref_mat in enumerate(slices): + for ref_mat in slices: np.testing.assert_array_equal(m.double_mat_cm(ref_mat), 2.0 * ref_mat) np.testing.assert_array_equal(m.double_mat_rm(ref_mat), 2.0 * ref_mat) @@ -264,7 +265,7 @@ def test_negative_stride_from_python(msg): counting_3d = np.arange(27.0, dtype=np.float32).reshape((3, 3, 3)) counting_3d = counting_3d[::-1, ::-1, ::-1] slices = [counting_3d[0, :, :], counting_3d[:, 0, :], counting_3d[:, :, 0]] - for slice_idx, ref_mat in enumerate(slices): + for ref_mat in slices: np.testing.assert_array_equal(m.double_mat_cm(ref_mat), 2.0 * ref_mat) np.testing.assert_array_equal(m.double_mat_rm(ref_mat), 2.0 * ref_mat) diff --git a/tests/test_embed/CMakeLists.txt b/tests/test_embed/CMakeLists.txt index 8b4f1f843e..25972701fc 100644 --- a/tests/test_embed/CMakeLists.txt +++ b/tests/test_embed/CMakeLists.txt @@ -1,41 +1,40 @@ if(${PYTHON_MODULE_EXTENSION} MATCHES "pypy") - add_custom_target(cpptest) # Dummy target on PyPy. Embedding is not supported. + add_custom_target(cpptest) # Dummy target on PyPy. Embedding is not supported. set(_suppress_unused_variable_warning "${DOWNLOAD_CATCH}") return() endif() -find_package(Catch 1.9.3) +find_package(Catch 2.13.0) if(CATCH_FOUND) message(STATUS "Building interpreter tests using Catch v${CATCH_VERSION}") else() message(STATUS "Catch not detected. Interpreter tests will be skipped. Install Catch headers" - " manually or use `cmake -DDOWNLOAD_CATCH=1` to fetch them automatically.") + " manually or use `cmake -DDOWNLOAD_CATCH=ON` to fetch them automatically.") return() endif() -add_executable(test_embed - catch.cpp - test_interpreter.cpp -) -target_include_directories(test_embed PRIVATE ${CATCH_INCLUDE_DIR}) +add_executable(test_embed catch.cpp test_interpreter.cpp) +target_include_directories(test_embed PRIVATE "${CATCH_INCLUDE_DIR}") pybind11_enable_warnings(test_embed) -if(NOT CMAKE_VERSION VERSION_LESS 3.0) - target_link_libraries(test_embed PRIVATE pybind11::embed) -else() - target_include_directories(test_embed PRIVATE ${PYBIND11_INCLUDE_DIR} ${PYTHON_INCLUDE_DIRS}) - target_compile_options(test_embed PRIVATE ${PYBIND11_CPP_STANDARD}) - target_link_libraries(test_embed PRIVATE ${PYTHON_LIBRARIES}) -endif() +target_link_libraries(test_embed PRIVATE pybind11::embed) find_package(Threads REQUIRED) -target_link_libraries(test_embed PUBLIC ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(test_embed PUBLIC Threads::Threads) -add_custom_target(cpptest COMMAND $ - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +add_custom_target( + cpptest + COMMAND "$" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") pybind11_add_module(external_module THIN_LTO external_module.cpp) -set_target_properties(external_module PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +set_target_properties(external_module PROPERTIES LIBRARY_OUTPUT_DIRECTORY + "${CMAKE_CURRENT_SOURCE_DIR}") +foreach(config ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${config} config) + set_target_properties(external_module PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} + "${CMAKE_CURRENT_SOURCE_DIR}") +endforeach() add_dependencies(cpptest external_module) add_dependencies(check cpptest) diff --git a/tests/test_embed/test_interpreter.py b/tests/test_embed/test_interpreter.py index 26a0479216..6174ede446 100644 --- a/tests/test_embed/test_interpreter.py +++ b/tests/test_embed/test_interpreter.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from widget_module import Widget diff --git a/tests/test_enum.py b/tests/test_enum.py index 7fe9b618d6..bfaa193e9b 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import enums as m diff --git a/tests/test_eval.py b/tests/test_eval.py index bda4ef6bf6..66bec55f8b 100644 --- a/tests/test_eval.py +++ b/tests/test_eval.py @@ -1,4 +1,6 @@ +# -*- coding: utf-8 -*- import os +import pytest from pybind11_tests import eval_ as m @@ -10,8 +12,12 @@ def test_evals(capture): assert m.test_eval() assert m.test_eval_single_statement() + assert m.test_eval_failure() + + +@pytest.unsupported_on_pypy3 +def test_eval_file(): filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py") assert m.test_eval_file(filename) - assert m.test_eval_failure() assert m.test_eval_file_failure() diff --git a/tests/test_eval_call.py b/tests/test_eval_call.py index 53c7e721fe..d42a0a6d30 100644 --- a/tests/test_eval_call.py +++ b/tests/test_eval_call.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # This file is called from 'test_eval.py' if 'call_test2' in locals(): diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index ac2b3603ec..053e7d4a28 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import exceptions as m diff --git a/tests/test_factory_constructors.py b/tests/test_factory_constructors.py index 78a3910ada..49e6f4f331 100644 --- a/tests/test_factory_constructors.py +++ b/tests/test_factory_constructors.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest import re diff --git a/tests/test_gil_scoped.cpp b/tests/test_gil_scoped.cpp index 76c17fdc78..dc9b7ed224 100644 --- a/tests/test_gil_scoped.cpp +++ b/tests/test_gil_scoped.cpp @@ -13,7 +13,9 @@ class VirtClass { public: - virtual ~VirtClass() {} + virtual ~VirtClass() = default; + VirtClass() = default; + VirtClass(const VirtClass&) = delete; virtual void virtual_func() {} virtual void pure_virtual_func() = 0; }; diff --git a/tests/test_gil_scoped.py b/tests/test_gil_scoped.py index 1548337ccc..1307712ad3 100644 --- a/tests/test_gil_scoped.py +++ b/tests/test_gil_scoped.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import multiprocessing import threading from pybind11_tests import gil_scoped as m diff --git a/tests/test_iostream.py b/tests/test_iostream.py index 27095b2705..7ac4fcece0 100644 --- a/tests/test_iostream.py +++ b/tests/test_iostream.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from pybind11_tests import iostream as m import sys diff --git a/tests/test_kwargs_and_defaults.cpp b/tests/test_kwargs_and_defaults.cpp index 8f095fe4a6..64bc2377b2 100644 --- a/tests/test_kwargs_and_defaults.cpp +++ b/tests/test_kwargs_and_defaults.cpp @@ -123,4 +123,9 @@ TEST_SUBMODULE(kwargs_and_defaults, m) { py::class_(m, "KWClass") .def("foo0", &KWClass::foo) .def("foo1", &KWClass::foo, "x"_a, "y"_a); + + // Make sure a class (not an instance) can be used as a default argument. + // The return value doesn't matter, only that the module is importable. + m.def("class_default_argument", [](py::object a) { return py::repr(a); }, + "a"_a = py::module::import("decimal").attr("Decimal")); } diff --git a/tests/test_kwargs_and_defaults.py b/tests/test_kwargs_and_defaults.py index bad6636cb4..dad40dbebf 100644 --- a/tests/test_kwargs_and_defaults.py +++ b/tests/test_kwargs_and_defaults.py @@ -1,6 +1,12 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import kwargs_and_defaults as m +import platform +import sys + +pypy = platform.python_implementation() == "PyPy" + def test_function_signatures(doc): assert doc(m.kw_func0) == "kw_func0(arg0: int, arg1: int) -> str" @@ -145,6 +151,8 @@ def test_keyword_only_args(msg): """ +@pytest.mark.xfail(pypy and sys.version_info < (3, 0), + reason="PyPy2 doesn't seem to double count") def test_args_refcount(): """Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular arguments""" @@ -183,3 +191,5 @@ def test_args_refcount(): # tuple without having to inc_ref the individual elements, but here we can't, hence the extra # refs. assert m.mixed_args_refcount(myval, myval, myval) == (exp3 + 3, exp3 + 3, exp3 + 3) + + assert m.class_default_argument() == "" diff --git a/tests/test_local_bindings.py b/tests/test_local_bindings.py index b380376e2b..913cf0ee5b 100644 --- a/tests/test_local_bindings.py +++ b/tests/test_local_bindings.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import local_bindings as m @@ -152,6 +153,7 @@ def test_internal_locals_differ(): assert m.local_cpp_types_addr() != cm.local_cpp_types_addr() +@pytest.bug_in_pypy def test_stl_caster_vs_stl_bind(msg): """One module uses a generic vector caster from `` while the other exports `std::vector` via `py:bind_vector` and `py::module_local`""" diff --git a/tests/test_methods_and_attributes.cpp b/tests/test_methods_and_attributes.cpp index 1337620941..11d4e7b350 100644 --- a/tests/test_methods_and_attributes.cpp +++ b/tests/test_methods_and_attributes.cpp @@ -21,6 +21,7 @@ class ExampleMandA { ExampleMandA() { print_default_created(this); } ExampleMandA(int value) : value(value) { print_created(this, value); } ExampleMandA(const ExampleMandA &e) : value(e.value) { print_copy_created(this); } + ExampleMandA(std::string&&) {} ExampleMandA(ExampleMandA &&e) : value(e.value) { print_move_created(this); } ~ExampleMandA() { print_destroyed(this); } @@ -43,6 +44,8 @@ class ExampleMandA { void add9(int *other) { value += *other; } // passing by pointer void add10(const int *other) { value += *other; } // passing by const pointer + void consume_str(std::string&&) {} + ExampleMandA self1() { return *this; } // return by value ExampleMandA &self2() { return *this; } // return by reference const ExampleMandA &self3() { return *this; } // return by const reference @@ -105,76 +108,6 @@ struct TestPropRVP { UserType TestPropRVP::sv1(1); UserType TestPropRVP::sv2(1); -// py::arg/py::arg_v testing: these arguments just record their argument when invoked -class ArgInspector1 { public: std::string arg = "(default arg inspector 1)"; }; -class ArgInspector2 { public: std::string arg = "(default arg inspector 2)"; }; -class ArgAlwaysConverts { }; -namespace pybind11 { namespace detail { -template <> struct type_caster { -public: - PYBIND11_TYPE_CASTER(ArgInspector1, _("ArgInspector1")); - - bool load(handle src, bool convert) { - value.arg = "loading ArgInspector1 argument " + - std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. " - "Argument value = " + (std::string) str(src); - return true; - } - - static handle cast(const ArgInspector1 &src, return_value_policy, handle) { - return str(src.arg).release(); - } -}; -template <> struct type_caster { -public: - PYBIND11_TYPE_CASTER(ArgInspector2, _("ArgInspector2")); - - bool load(handle src, bool convert) { - value.arg = "loading ArgInspector2 argument " + - std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. " - "Argument value = " + (std::string) str(src); - return true; - } - - static handle cast(const ArgInspector2 &src, return_value_policy, handle) { - return str(src.arg).release(); - } -}; -template <> struct type_caster { -public: - PYBIND11_TYPE_CASTER(ArgAlwaysConverts, _("ArgAlwaysConverts")); - - bool load(handle, bool convert) { - return convert; - } - - static handle cast(const ArgAlwaysConverts &, return_value_policy, handle) { - return py::none().release(); - } -}; -}} - -// test_custom_caster_destruction -class DestructionTester { -public: - DestructionTester() { print_default_created(this); } - ~DestructionTester() { print_destroyed(this); } - DestructionTester(const DestructionTester &) { print_copy_created(this); } - DestructionTester(DestructionTester &&) { print_move_created(this); } - DestructionTester &operator=(const DestructionTester &) { print_copy_assigned(this); return *this; } - DestructionTester &operator=(DestructionTester &&) { print_move_assigned(this); return *this; } -}; -namespace pybind11 { namespace detail { -template <> struct type_caster { - PYBIND11_TYPE_CASTER(DestructionTester, _("DestructionTester")); - bool load(handle, bool) { return true; } - - static handle cast(const DestructionTester &, return_value_policy, handle) { - return py::bool_(true).release(); - } -}; -}} - // Test None-allowed py::arg argument policy class NoneTester { public: int answer = 42; }; int none1(const NoneTester &obj) { return obj.answer; } @@ -220,6 +153,7 @@ TEST_SUBMODULE(methods_and_attributes, m) { py::class_ emna(m, "ExampleMandA"); emna.def(py::init<>()) .def(py::init()) + .def(py::init()) .def(py::init()) .def("add1", &ExampleMandA::add1) .def("add2", &ExampleMandA::add2) @@ -231,6 +165,7 @@ TEST_SUBMODULE(methods_and_attributes, m) { .def("add8", &ExampleMandA::add8) .def("add9", &ExampleMandA::add9) .def("add10", &ExampleMandA::add10) + .def("consume_str", &ExampleMandA::consume_str) .def("self1", &ExampleMandA::self1) .def("self2", &ExampleMandA::self2) .def("self3", &ExampleMandA::self3) @@ -354,6 +289,7 @@ TEST_SUBMODULE(methods_and_attributes, m) { class DynamicClass { public: DynamicClass() { print_default_created(this); } + DynamicClass(const DynamicClass&) = delete; ~DynamicClass() { print_destroyed(this); } }; py::class_(m, "DynamicClass", py::dynamic_attr()) @@ -364,33 +300,6 @@ TEST_SUBMODULE(methods_and_attributes, m) { .def(py::init()); #endif - // test_noconvert_args - // - // Test converting. The ArgAlwaysConverts is just there to make the first no-conversion pass - // fail so that our call always ends up happening via the second dispatch (the one that allows - // some conversion). - class ArgInspector { - public: - ArgInspector1 f(ArgInspector1 a, ArgAlwaysConverts) { return a; } - std::string g(ArgInspector1 a, const ArgInspector1 &b, int c, ArgInspector2 *d, ArgAlwaysConverts) { - return a.arg + "\n" + b.arg + "\n" + std::to_string(c) + "\n" + d->arg; - } - static ArgInspector2 h(ArgInspector2 a, ArgAlwaysConverts) { return a; } - }; - 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()) - ; - 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()); - - 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()); - // test_bad_arg_default // Issue/PR #648: bad arg default debugging output #if !defined(NDEBUG) @@ -454,18 +363,6 @@ TEST_SUBMODULE(methods_and_attributes, m) { using Adapted = decltype(py::method_adaptor(&RegisteredDerived::do_nothing)); static_assert(std::is_same::value, ""); - // test_custom_caster_destruction - // Test that `take_ownership` works on types with a custom type caster when given a pointer - - // default policy: don't take ownership: - m.def("custom_caster_no_destroy", []() { static auto *dt = new DestructionTester(); return dt; }); - - m.def("custom_caster_destroy", []() { return new DestructionTester(); }, - py::return_value_policy::take_ownership); // Takes ownership: destroy when finished - m.def("custom_caster_destroy_const", []() -> const DestructionTester * { return new DestructionTester(); }, - py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction) - m.def("destruction_tester_cstats", &ConstructorStats::get, py::return_value_policy::reference); - // test_methods_and_attributes py::class_(m, "RefQualified") .def(py::init<>()) diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index 2604b6ea5a..6515cbf594 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import methods_and_attributes as m from pybind11_tests import ConstructorStats @@ -59,7 +60,7 @@ def test_methods_and_attributes(): assert cstats.values() == ["32"] assert cstats.default_constructions == 1 assert cstats.copy_constructions == 2 - assert cstats.move_constructions == 2 + assert cstats.move_constructions >= 2 assert cstats.copy_assignments == 0 assert cstats.move_assignments == 0 @@ -321,69 +322,6 @@ def test_cyclic_gc(): assert cstats.alive() == 0 -def test_noconvert_args(msg): - a = m.ArgInspector() - assert msg(a.f("hi")) == """ - loading ArgInspector1 argument WITH conversion allowed. Argument value = hi - """ - assert msg(a.g("this is a", "this is b")) == """ - loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a - loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b - 13 - loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2) - """ # noqa: E501 line too long - assert msg(a.g("this is a", "this is b", 42)) == """ - loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a - loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b - 42 - loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2) - """ # noqa: E501 line too long - assert msg(a.g("this is a", "this is b", 42, "this is d")) == """ - loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a - loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b - 42 - loading ArgInspector2 argument WITH conversion allowed. Argument value = this is d - """ - assert (a.h("arg 1") == - "loading ArgInspector2 argument WITHOUT conversion allowed. Argument value = arg 1") - assert msg(m.arg_inspect_func("A1", "A2")) == """ - loading ArgInspector2 argument WITH conversion allowed. Argument value = A1 - loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = A2 - """ - - assert m.floats_preferred(4) == 2.0 - assert m.floats_only(4.0) == 2.0 - with pytest.raises(TypeError) as excinfo: - m.floats_only(4) - assert msg(excinfo.value) == """ - floats_only(): incompatible function arguments. The following argument types are supported: - 1. (f: float) -> float - - Invoked with: 4 - """ - - assert m.ints_preferred(4) == 2 - assert m.ints_preferred(True) == 0 - with pytest.raises(TypeError) as excinfo: - m.ints_preferred(4.0) - assert msg(excinfo.value) == """ - ints_preferred(): incompatible function arguments. The following argument types are supported: - 1. (i: int) -> int - - Invoked with: 4.0 - """ # noqa: E501 line too long - - assert m.ints_only(4) == 2 - with pytest.raises(TypeError) as excinfo: - m.ints_only(4.0) - assert msg(excinfo.value) == """ - ints_only(): incompatible function arguments. The following argument types are supported: - 1. (i: int) -> int - - Invoked with: 4.0 - """ - - def test_bad_arg_default(msg): from pybind11_tests import debug_enabled @@ -488,30 +426,6 @@ def test_unregistered_base_implementations(): assert a.ro_value_prop == 1.75 -def test_custom_caster_destruction(): - """Tests that returning a pointer to a type that gets converted with a custom type caster gets - destroyed when the function has py::return_value_policy::take_ownership policy applied.""" - - cstats = m.destruction_tester_cstats() - # This one *doesn't* have take_ownership: the pointer should be used but not destroyed: - z = m.custom_caster_no_destroy() - assert cstats.alive() == 1 and cstats.default_constructions == 1 - assert z - - # take_ownership applied: this constructs a new object, casts it, then destroys it: - z = m.custom_caster_destroy() - assert z - assert cstats.default_constructions == 2 - - # Same, but with a const pointer return (which should *not* inhibit destruction): - z = m.custom_caster_destroy_const() - assert z - assert cstats.default_constructions == 3 - - # Make sure we still only have the original object (from ..._no_destroy()) alive: - assert cstats.alive() == 1 - - def test_ref_qualified(): """Tests that explicit lvalue ref-qualified methods can be called just like their non ref-qualified counterparts.""" diff --git a/tests/test_modules.py b/tests/test_modules.py index 2552838c2b..7e21005245 100644 --- a/tests/test_modules.py +++ b/tests/test_modules.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from pybind11_tests import modules as m from pybind11_tests.modules import subsubmodule as ms from pybind11_tests import ConstructorStats diff --git a/tests/test_multiple_inheritance.cpp b/tests/test_multiple_inheritance.cpp index 2842543f17..bd5907c635 100644 --- a/tests/test_multiple_inheritance.cpp +++ b/tests/test_multiple_inheritance.cpp @@ -214,7 +214,7 @@ TEST_SUBMODULE(multiple_inheritance, m) { .def_readwrite_static("static_value", &VanillaStaticMix2::static_value); -#if !defined(PYPY_VERSION) +#if !(defined(PYPY_VERSION) && (PYPY_VERSION_NUM < 0x06000000)) struct WithDict { }; struct VanillaDictMix1 : Vanilla, WithDict { }; struct VanillaDictMix2 : WithDict, Vanilla { }; diff --git a/tests/test_multiple_inheritance.py b/tests/test_multiple_inheritance.py index 15b55cbb9f..146aaebcef 100644 --- a/tests/test_multiple_inheritance.py +++ b/tests/test_multiple_inheritance.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import ConstructorStats from pybind11_tests import multiple_inheritance as m @@ -10,6 +11,7 @@ def test_multiple_inheritance_cpp(): assert mt.bar() == 4 +@pytest.bug_in_pypy def test_multiple_inheritance_mix1(): class Base1: def __init__(self, i): @@ -49,6 +51,7 @@ def __init__(self, i, j): assert mt.bar() == 4 +@pytest.bug_in_pypy def test_multiple_inheritance_python(): class MI1(m.Base1, m.Base2): @@ -253,7 +256,7 @@ def test_mi_static_properties(): assert d.static_value == 0 -@pytest.unsupported_on_pypy +@pytest.unsupported_on_pypy_lt_6 def test_mi_dynamic_attributes(): """Mixing bases with and without dynamic attribute support""" diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index 55571964d0..2c977cd62e 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import numpy_array as m diff --git a/tests/test_numpy_dtypes.py b/tests/test_numpy_dtypes.py index 2e63885174..d173435fe6 100644 --- a/tests/test_numpy_dtypes.py +++ b/tests/test_numpy_dtypes.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import re import pytest from pybind11_tests import numpy_dtypes as m diff --git a/tests/test_numpy_vectorize.py b/tests/test_numpy_vectorize.py index c078ee7cf0..bd3c01347c 100644 --- a/tests/test_numpy_vectorize.py +++ b/tests/test_numpy_vectorize.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import numpy_vectorize as m diff --git a/tests/test_opaque_types.py b/tests/test_opaque_types.py index 6b3802fdba..3f2392775d 100644 --- a/tests/test_opaque_types.py +++ b/tests/test_opaque_types.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import opaque_types as m from pybind11_tests import ConstructorStats, UserType diff --git a/tests/test_operator_overloading.cpp b/tests/test_operator_overloading.cpp index 52fcd33836..f3c2eaafa9 100644 --- a/tests/test_operator_overloading.cpp +++ b/tests/test_operator_overloading.cpp @@ -187,6 +187,38 @@ TEST_SUBMODULE(operators, m) { .def(py::self *= int()) .def_readwrite("b", &NestC::b); m.def("get_NestC", [](const NestC &c) { return c.value; }); + + + // test_overriding_eq_reset_hash + // #2191 Overriding __eq__ should set __hash__ to None + struct Comparable { + int value; + bool operator==(const Comparable& rhs) const {return value == rhs.value;} + }; + + struct Hashable : Comparable { + explicit Hashable(int value): Comparable{value}{}; + size_t hash() const { return static_cast(value); } + }; + + struct Hashable2 : Hashable { + using Hashable::Hashable; + }; + + py::class_(m, "Comparable") + .def(py::init()) + .def(py::self == py::self); + + py::class_(m, "Hashable") + .def(py::init()) + .def(py::self == py::self) + .def("__hash__", &Hashable::hash); + + // define __hash__ before __eq__ + py::class_(m, "Hashable2") + .def("__hash__", &Hashable::hash) + .def(py::init()) + .def(py::self == py::self); } #ifndef _MSC_VER diff --git a/tests/test_operator_overloading.py b/tests/test_operator_overloading.py index 1cee29889c..39e3aee271 100644 --- a/tests/test_operator_overloading.py +++ b/tests/test_operator_overloading.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import operators as m from pybind11_tests import ConstructorStats @@ -126,3 +127,19 @@ def test_nested(): assert abase.value == 42 del abase, b pytest.gc_collect() + + +def test_overriding_eq_reset_hash(): + + assert m.Comparable(15) is not m.Comparable(15) + assert m.Comparable(15) == m.Comparable(15) + + with pytest.raises(TypeError): + hash(m.Comparable(15)) # TypeError: unhashable type: 'm.Comparable' + + for hashable in (m.Hashable, m.Hashable2): + assert hashable(15) is not hashable(15) + assert hashable(15) == hashable(15) + + assert hash(hashable(15)) == 15 + assert hash(hashable(15)) == hash(hashable(15)) diff --git a/tests/test_ownership_transfer.py b/tests/test_ownership_transfer.py index 49c2814df1..c8a6edfe91 100644 --- a/tests/test_ownership_transfer.py +++ b/tests/test_ownership_transfer.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from pybind11_tests import ownership_transfer as m from pybind11_tests import ConstructorStats diff --git a/tests/test_pickling.py b/tests/test_pickling.py index 5ae05aaa0c..58d67a6339 100644 --- a/tests/test_pickling.py +++ b/tests/test_pickling.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import pickling as m diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index e70ffae9e6..9f7bc37dc6 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -32,6 +32,11 @@ TEST_SUBMODULE(pytypes, m) { for (auto item : list) py::print("list item {}: {}"_s.format(index++, item)); }); + // test_none + m.def("get_none", []{return py::none();}); + m.def("print_none", [](py::none none) { + py::print("none: {}"_s.format(none)); + }); // test_set m.def("get_set", []() { @@ -313,4 +318,53 @@ TEST_SUBMODULE(pytypes, m) { m.def("test_list_slicing", [](py::list a) { return a[py::slice(0, -1, 2)]; }); + + m.def("test_memoryview_object", [](py::buffer b) { + return py::memoryview(b); + }); + + m.def("test_memoryview_buffer_info", [](py::buffer b) { + return py::memoryview(b.request()); + }); + + m.def("test_memoryview_from_buffer", [](bool is_unsigned) { + static const int16_t si16[] = { 3, 1, 4, 1, 5 }; + static const uint16_t ui16[] = { 2, 7, 1, 8 }; + if (is_unsigned) + return py::memoryview::from_buffer( + ui16, { 4 }, { sizeof(uint16_t) }); + else + return py::memoryview::from_buffer( + si16, { 5 }, { sizeof(int16_t) }); + }); + + m.def("test_memoryview_from_buffer_nativeformat", []() { + static const char* format = "@i"; + static const int32_t arr[] = { 4, 7, 5 }; + return py::memoryview::from_buffer( + arr, sizeof(int32_t), format, { 3 }, { sizeof(int32_t) }); + }); + + m.def("test_memoryview_from_buffer_empty_shape", []() { + static const char* buf = ""; + return py::memoryview::from_buffer(buf, 1, "B", { }, { }); + }); + + m.def("test_memoryview_from_buffer_invalid_strides", []() { + static const char* buf = "\x02\x03\x04"; + return py::memoryview::from_buffer(buf, 1, "B", { 3 }, { }); + }); + + m.def("test_memoryview_from_buffer_nullptr", []() { + return py::memoryview::from_buffer( + static_cast(nullptr), 1, "B", { }, { }); + }); + +#if PY_MAJOR_VERSION >= 3 + m.def("test_memoryview_from_memory", []() { + const char* buf = "\xff\xe1\xab\x37"; + return py::memoryview::from_memory( + buf, static_cast(strlen(buf))); + }); +#endif } diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index d6223b9ba8..4cfc707a32 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from __future__ import division import pytest import sys @@ -37,6 +38,11 @@ def test_list(capture, doc): assert doc(m.print_list) == "print_list(arg0: list) -> None" +def test_none(capture, doc): + assert doc(m.get_none) == "get_none() -> None" + assert doc(m.print_none) == "print_none(arg0: None) -> None" + + def test_set(capture, doc): s = m.get_set() assert s == {"key1", "key2", "key3"} @@ -273,3 +279,71 @@ def test_number_protocol(): def test_list_slicing(): li = list(range(100)) assert li[::2] == m.test_list_slicing(li) + + +@pytest.mark.parametrize('method, args, fmt, expected_view', [ + (m.test_memoryview_object, (b'red',), 'B', b'red'), + (m.test_memoryview_buffer_info, (b'green',), 'B', b'green'), + (m.test_memoryview_from_buffer, (False,), 'h', [3, 1, 4, 1, 5]), + (m.test_memoryview_from_buffer, (True,), 'H', [2, 7, 1, 8]), + (m.test_memoryview_from_buffer_nativeformat, (), '@i', [4, 7, 5]), +]) +def test_memoryview(method, args, fmt, expected_view): + view = method(*args) + assert isinstance(view, memoryview) + assert view.format == fmt + if isinstance(expected_view, bytes) or sys.version_info[0] >= 3: + view_as_list = list(view) + else: + # Using max to pick non-zero byte (big-endian vs little-endian). + view_as_list = [max([ord(c) for c in s]) for s in view] + assert view_as_list == list(expected_view) + + +@pytest.mark.skipif( + not hasattr(sys, 'getrefcount'), + reason='getrefcount is not available') +@pytest.mark.parametrize('method', [ + m.test_memoryview_object, + m.test_memoryview_buffer_info, +]) +def test_memoryview_refcount(method): + buf = b'\x0a\x0b\x0c\x0d' + ref_before = sys.getrefcount(buf) + view = method(buf) + ref_after = sys.getrefcount(buf) + assert ref_before < ref_after + assert list(view) == list(buf) + + +def test_memoryview_from_buffer_empty_shape(): + view = m.test_memoryview_from_buffer_empty_shape() + assert isinstance(view, memoryview) + assert view.format == 'B' + if sys.version_info.major < 3: + # Python 2 behavior is weird, but Python 3 (the future) is fine. + # PyPy3 has >(m, "TypeWithMoveOnlyHolder") - .def_static("make", []() { return custom_unique_ptr(new C); }); + .def_static("make", []() { return custom_unique_ptr(new C); }) + .def_static("make_as_object", []() { return py::cast(custom_unique_ptr(new C)); }); // test_holder_with_addressof_operator struct TypeForHolderWithAddressOf { @@ -339,6 +340,8 @@ TEST_SUBMODULE(smart_ptr, m) { // #187: issue involving std::shared_ptr<> return value policy & garbage collection struct ElementBase { virtual ~ElementBase() { } /* Force creation of virtual table */ + ElementBase() = default; + ElementBase(const ElementBase&) = delete; }; py::class_>(m, "ElementBase"); diff --git a/tests/test_smart_ptr.py b/tests/test_smart_ptr.py index 63d9171b59..8c97e5ab90 100644 --- a/tests/test_smart_ptr.py +++ b/tests/test_smart_ptr.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import pytest from pybind11_tests import smart_ptr as m from pybind11_tests import ConstructorStats @@ -218,7 +219,10 @@ def test_shared_ptr_from_this_and_references(): def test_move_only_holder(): a = m.TypeWithMoveOnlyHolder.make() + b = m.TypeWithMoveOnlyHolder.make_as_object() stats = ConstructorStats.get(m.TypeWithMoveOnlyHolder) + assert stats.alive() == 2 + del b assert stats.alive() == 1 del a assert stats.alive() == 0 @@ -291,14 +295,15 @@ def test_unique_ptr_arg(): pass_through_list = [ m.unique_ptr_pass_through, - m.unique_ptr_pass_through_cast_from_py, - m.unique_ptr_pass_through_move_from_py, m.unique_ptr_pass_through_move_to_py, m.unique_ptr_pass_through_cast_to_py, + # TODO(eric.cousineau): Fix these cases. + # m.unique_ptr_pass_through_cast_from_py, + # m.unique_ptr_pass_through_move_from_py, ] for pass_through in pass_through_list: obj = m.UniquePtrHeld(1) - obj_ref = m.unique_ptr_pass_through(obj) + obj_ref = pass_through(obj) assert stats.alive() == 1 assert obj.value() == 1 assert obj == obj_ref diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp index 207c9fb2bf..928635788e 100644 --- a/tests/test_stl.cpp +++ b/tests/test_stl.cpp @@ -50,6 +50,17 @@ namespace std { } +template