ext: Updated Pybind11 to version 2.4.1.
This updates Pybind11 from version 2.2.1 to version 2.4.1. This fixes
warning/error received when "<experiment/optional>" is used when
compiling using c++14 with clang. It should be noted that
"ext/pybind11/include/pybind11/std.h" has been changed to include a fix
added by commit ba42457254. This is
necessary to avoid build errors.
Built: Linux (gcc, c++11) and MacOS (clang, c++14).
Tested: Ran quick tests for X86, ARM, and RISC-V.
Deprecates: https://gem5-review.googlesource.com/c/public/gem5/+/21019
Change-Id: Ie9783511cb6be50136076a55330e645f4f36d075
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/21119
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Maintainer: Jason Lowe-Power <jason@lowepower.com>
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
@@ -3,6 +3,7 @@ image:
|
||||
- Visual Studio 2017
|
||||
- Visual Studio 2015
|
||||
test: off
|
||||
skip_branch_with_pr: true
|
||||
build:
|
||||
parallel: true
|
||||
platform:
|
||||
@@ -34,19 +35,21 @@ install:
|
||||
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"
|
||||
pip install --disable-pip-version-check --user --upgrade pip wheel
|
||||
pip install pytest numpy
|
||||
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
|
||||
}
|
||||
- ps: |
|
||||
@@ -59,6 +62,7 @@ build_script:
|
||||
-DPYBIND11_WERROR=ON
|
||||
-DDOWNLOAD_CATCH=ON
|
||||
-DCMAKE_SUPPRESS_REGENERATION=1
|
||||
.
|
||||
- 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%
|
||||
|
||||
1
ext/pybind11/.gitignore
vendored
1
ext/pybind11/.gitignore
vendored
@@ -27,6 +27,7 @@ MANIFEST
|
||||
*.py[co]
|
||||
*.egg-info
|
||||
*~
|
||||
.*.swp
|
||||
.DS_Store
|
||||
/dist
|
||||
/build
|
||||
|
||||
2
ext/pybind11/.gitmodules
vendored
2
ext/pybind11/.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "tools/clang"]
|
||||
path = tools/clang
|
||||
url = https://github.com/wjakob/clang-cindex-python3
|
||||
url = ../../wjakob/clang-cindex-python3
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
language: cpp
|
||||
dist: trusty
|
||||
sudo: false
|
||||
matrix:
|
||||
include:
|
||||
# This config does a few things:
|
||||
@@ -10,16 +8,17 @@ matrix:
|
||||
# - 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
|
||||
env: STYLE DOCS PIP
|
||||
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
|
||||
- $PY_CMD -m pip install --user --upgrade pip wheel setuptools
|
||||
install:
|
||||
- $PY_CMD -m pip install --user --upgrade sphinx sphinx_rtd_theme breathe flake8 pep8-naming pytest
|
||||
- curl -fsSL ftp://ftp.stack.nl/pub/users/dimitri/doxygen-1.8.12.linux.bin.tar.gz | tar xz
|
||||
- export PATH="$PWD/doxygen-1.8.12/bin:$PATH"
|
||||
- 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
|
||||
@@ -32,62 +31,119 @@ matrix:
|
||||
diff -rq $installed ./include/pybind11
|
||||
- |
|
||||
# Barebones build
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON
|
||||
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.\*]
|
||||
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.\*]
|
||||
- sudo: true
|
||||
services: docker
|
||||
env: PYTHON=2.7 CPP=14 GCC=6 CMAKE=1
|
||||
- sudo: true
|
||||
services: docker
|
||||
env: PYTHON=3.5 CPP=14 GCC=6 DEBUG=1
|
||||
- sudo: true
|
||||
services: docker
|
||||
env: PYTHON=3.6 CPP=17 GCC=7
|
||||
sources:
|
||||
- deadsnakes
|
||||
packages:
|
||||
- python3.6-dev
|
||||
- python3.6-venv
|
||||
- cmake=2.\*
|
||||
- cmake-data=2.\*
|
||||
- os: linux
|
||||
env: PYTHON=3.6 CPP=17 CLANG=5.0
|
||||
dist: trusty
|
||||
env: PYTHON=2.7 CPP=14 GCC=6 CMAKE=1
|
||||
name: Python 2.7, c++14, gcc 4.8, CMake test
|
||||
addons:
|
||||
apt:
|
||||
sources: [deadsnakes, llvm-toolchain-trusty-5.0, ubuntu-toolchain-r-test]
|
||||
packages: [python3.6-dev python3.6-venv clang-5.0 llvm-5.0-dev, lld-5.0]
|
||||
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: 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
|
||||
osx_image: xcode8.3
|
||||
env: PYTHON=3.6 CPP=14 CLANG DEBUG=1
|
||||
name: Python 3.7, c++14, AppleClang 9, Debug build
|
||||
osx_image: xcode9
|
||||
env: PYTHON=3.7 CPP=14 CLANG DEBUG=1
|
||||
# Test a PyPy 2.7 build
|
||||
- os: linux
|
||||
dist: trusty
|
||||
env: PYPY=5.8 PYTHON=2.7 CPP=11 GCC=4.8
|
||||
name: PyPy 5.8, Python 2.7, c++11, gcc 4.8
|
||||
addons:
|
||||
apt:
|
||||
packages: [libblas-dev, liblapack-dev, gfortran]
|
||||
packages:
|
||||
- libblas-dev
|
||||
- liblapack-dev
|
||||
- gfortran
|
||||
# Build in 32-bit mode and tests against the CMake-installed version
|
||||
- sudo: true
|
||||
- os: linux
|
||||
dist: trusty
|
||||
services: docker
|
||||
env: ARCH=i386 PYTHON=3.5 CPP=14 GCC=6 INSTALL=1
|
||||
env: DOCKER=i386/debian:stretch PYTHON=3.5 CPP=14 GCC=6 INSTALL=1
|
||||
name: Python 3.4, c++14, gcc 6, 32-bit
|
||||
script:
|
||||
- |
|
||||
$SCRIPT_RUN_PREFIX sh -c "set -e
|
||||
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"
|
||||
# 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
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.local/bin
|
||||
@@ -97,28 +153,27 @@ cache:
|
||||
before_install:
|
||||
- |
|
||||
# Configure build variables
|
||||
set -ex
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
if [ -n "$CLANG" ]; then
|
||||
export CXX=clang++-$CLANG CC=clang-$CLANG
|
||||
COMPILER_PACKAGES="clang-$CLANG llvm-$CLANG-dev"
|
||||
EXTRA_PACKAGES+=" clang-$CLANG llvm-$CLANG-dev"
|
||||
else
|
||||
if [ -z "$GCC" ]; then GCC=4.8
|
||||
else COMPILER_PACKAGES=g++-$GCC
|
||||
else EXTRA_PACKAGES+=" g++-$GCC"
|
||||
fi
|
||||
export CXX=g++-$GCC CC=gcc-$GCC
|
||||
fi
|
||||
if [ "$GCC" = "6" ]; then DOCKER=${ARCH:+$ARCH/}debian:stretch
|
||||
elif [ "$GCC" = "7" ]; then DOCKER=debian:buster
|
||||
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="${CMAKE_EXTRA_ARGS} -DCMAKE_BUILD_TYPE=Debug"; fi
|
||||
if [ -n "$DEBUG" ]; then CMAKE_EXTRA_ARGS+=" -DCMAKE_BUILD_TYPE=Debug"; fi
|
||||
set +ex
|
||||
- |
|
||||
# Initialize environment
|
||||
set -e
|
||||
set -ex
|
||||
if [ -n "$DOCKER" ]; then
|
||||
docker pull $DOCKER
|
||||
|
||||
@@ -133,12 +188,12 @@ before_install:
|
||||
if [ "$PYPY" = "5.8" ]; then
|
||||
curl -fSL https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-linux64.tar.bz2 | tar xj
|
||||
PY_CMD=$(echo `pwd`/pypy2-v5.8.0-linux64/bin/pypy)
|
||||
CMAKE_EXTRA_ARGS="${CMAKE_EXTRA_ARGS} -DPYTHON_EXECUTABLE:FILEPATH=$PY_CMD"
|
||||
CMAKE_EXTRA_ARGS+=" -DPYTHON_EXECUTABLE:FILEPATH=$PY_CMD"
|
||||
else
|
||||
PY_CMD=python$PYTHON
|
||||
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
if [ "$PY" = "3" ]; then
|
||||
brew install python$PY;
|
||||
brew update && brew upgrade python
|
||||
else
|
||||
curl -fsSL https://bootstrap.pypa.io/get-pip.py | $PY_CMD - --user
|
||||
fi
|
||||
@@ -147,66 +202,79 @@ before_install:
|
||||
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 +e
|
||||
set +ex
|
||||
install:
|
||||
- |
|
||||
# Install dependencies
|
||||
set -e
|
||||
set -ex
|
||||
cmake --version
|
||||
if [ -n "$DOCKER" ]; then
|
||||
if [ -n "$DEBUG" ]; then
|
||||
PY_DEBUG="python$PYTHON-dbg python$PY-scipy-dbg"
|
||||
CMAKE_EXTRA_ARGS="${CMAKE_EXTRA_ARGS} -DPYTHON_EXECUTABLE=/usr/bin/python${PYTHON}dm"
|
||||
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 ${COMPILER_PACKAGES} && break; done"
|
||||
libeigen3-dev libboost-dev cmake make ${EXTRA_PACKAGES} && break; done"
|
||||
else
|
||||
|
||||
if [ "$CLANG" = "5.0" ]; then
|
||||
if ! [ -d ~/.local/include/c++/v1 ]; then
|
||||
# Neither debian nor llvm provide a libc++ 5.0 deb; luckily it's fairly quick
|
||||
# to build, install (and cache), so do it ourselves:
|
||||
git clone --depth=1 https://github.com/llvm-mirror/llvm.git llvm-source
|
||||
git clone https://github.com/llvm-mirror/libcxx.git llvm-source/projects/libcxx -b release_50
|
||||
git clone https://github.com/llvm-mirror/libcxxabi.git llvm-source/projects/libcxxabi -b release_50
|
||||
mkdir llvm-build && cd llvm-build
|
||||
# Building llvm requires a newer cmake than is provided by the trusty container:
|
||||
CMAKE_VER=cmake-3.8.0-Linux-x86_64
|
||||
curl https://cmake.org/files/v3.8/$CMAKE_VER.tar.gz | tar xz
|
||||
./$CMAKE_VER/bin/cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=~/.local ../llvm-source
|
||||
make -j2 install-cxxabi install-cxx
|
||||
cp -a include/c++/v1/*cxxabi*.h ~/.local/include/c++/v1
|
||||
cd ..
|
||||
fi
|
||||
export CXXFLAGS="-isystem $HOME/.local/include/c++/v1 -stdlib=libc++"
|
||||
export LDFLAGS="-L$HOME/.local/lib -fuse-ld=lld-$CLANG"
|
||||
export LD_LIBRARY_PATH="$HOME/.local/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
if [ "$CPP" = "-std=c++17" ]; then CPP="-std=c++1z"; fi
|
||||
if [ "$CLANG" = "7" ]; then
|
||||
export CXXFLAGS="-stdlib=libc++"
|
||||
fi
|
||||
|
||||
export NPY_NUM_BUILD_JOBS=2
|
||||
echo "Installing pytest, numpy, scipy..."
|
||||
${PYPY:+travis_wait 30} $PY_CMD -m pip install --user --upgrade pytest numpy scipy \
|
||||
${PYPY:+--extra-index-url https://imaginary.ca/trusty-pypi}
|
||||
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://imaginary.ca/trusty-pypi \
|
||||
pytest numpy==1.15.4 scipy==1.2.0
|
||||
else
|
||||
$PY_CMD -m pip install --user --upgrade pytest numpy scipy
|
||||
fi
|
||||
echo "done."
|
||||
|
||||
wget -q -O eigen.tar.gz https://bitbucket.org/eigen/eigen/get/3.3.3.tar.gz
|
||||
tar xzf eigen.tar.gz
|
||||
export CMAKE_INCLUDE_PATH="${CMAKE_INCLUDE_PATH:+:}$PWD/eigen-eigen-67e894c6cd8f"
|
||||
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 +e
|
||||
set +ex
|
||||
script:
|
||||
- $SCRIPT_RUN_PREFIX cmake ${CMAKE_EXTRA_ARGS}
|
||||
-DPYBIND11_PYTHON_VERSION=$PYTHON
|
||||
-DPYBIND11_CPP_STANDARD=$CPP
|
||||
-DPYBIND11_WERROR=${WERROR:-ON}
|
||||
-DDOWNLOAD_CATCH=ON
|
||||
- $SCRIPT_RUN_PREFIX make pytest -j 2
|
||||
- $SCRIPT_RUN_PREFIX make cpptest -j 2
|
||||
- if [ -n "$CMAKE" ]; then $SCRIPT_RUN_PREFIX make test_cmake_build; fi
|
||||
- |
|
||||
# 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:
|
||||
- if [ -n "$DOCKER" ]; then docker stop "$containerid"; docker rm "$containerid"; fi
|
||||
- |
|
||||
# Cleanup (Docker)
|
||||
set -ex
|
||||
if [ -n "$DOCKER" ]; then docker stop "$containerid"; docker rm "$containerid"; fi
|
||||
set +ex
|
||||
|
||||
@@ -38,6 +38,8 @@ 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 "")
|
||||
|
||||
# NB: when adding a header don't forget to also add it to setup.py
|
||||
set(PYBIND11_HEADERS
|
||||
|
||||
@@ -27,11 +27,23 @@ 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``.)
|
||||
* This project has a strong focus on providing general solutions using a
|
||||
minimal amount of code, thus small pull requests are greatly preferred.
|
||||
|
||||
### License
|
||||
### Licensing of contributions
|
||||
|
||||
pybind11 is provided under a BSD-style license that can be found in the
|
||||
``LICENSE`` file. By using, distributing, or contributing to this project, you
|
||||
agree to the terms and conditions of this license.
|
||||
|
||||
You are under no obligation whatsoever to provide any bug fixes, patches, or
|
||||
upgrades to the features, functionality or performance of the source code
|
||||
("Enhancements") to anyone; however, if you choose to make your Enhancements
|
||||
available either publicly, or directly to the author of this software, without
|
||||
imposing a separate written license agreement for such Enhancements, then you
|
||||
hereby grant the following license: a non-exclusive, royalty-free perpetual
|
||||
license to install, use, modify, prepare derivative works, incorporate into
|
||||
other computer software, distribute, and sublicense such enhancements or
|
||||
derivative works thereof, in binary and source code form.
|
||||
|
||||
@@ -25,12 +25,5 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
You are under no obligation whatsoever to provide any bug fixes, patches, or
|
||||
upgrades to the features, functionality or performance of the source code
|
||||
("Enhancements") to anyone; however, if you choose to make your Enhancements
|
||||
available either publicly, or directly to the author of this software, without
|
||||
imposing a separate written license agreement for such Enhancements, then you
|
||||
hereby grant the following license: a non-exclusive, royalty-free perpetual
|
||||
license to install, use, modify, prepare derivative works, incorporate into
|
||||
other computer software, distribute, and sublicense such enhancements or
|
||||
derivative works thereof, in binary and source code form.
|
||||
Please also refer to the file CONTRIBUTING.md, which clarifies licensing of
|
||||
external contributions to this project including patches, pull requests, etc.
|
||||
|
||||
@@ -51,7 +51,6 @@ pybind11 can map the following core C++ features to Python
|
||||
- Custom operators
|
||||
- Single and multiple inheritance
|
||||
- STL data structures
|
||||
- Iterators and ranges
|
||||
- Smart pointers with reference counting like ``std::shared_ptr``
|
||||
- Internal references with correct reference counting
|
||||
- C++ classes with virtual (and pure virtual) methods can be extended in Python
|
||||
@@ -87,9 +86,8 @@ In addition to the core functionality, pybind11 provides some extra goodies:
|
||||
[reported](http://graylab.jhu.edu/RosettaCon2016/PyRosetta-4.pdf) a binary
|
||||
size reduction of **5.4x** and compile time reduction by **5.8x**.
|
||||
|
||||
- When supported by the compiler, two new C++14 features (relaxed constexpr and
|
||||
return value deduction) are used to precompute function signatures at compile
|
||||
time, leading to smaller binaries.
|
||||
- Function signatures are precomputed at compile time (using ``constexpr``),
|
||||
leading to smaller binaries.
|
||||
|
||||
- With little extra effort, C++ types can be pickled and unpickled similar to
|
||||
regular Python objects.
|
||||
@@ -99,7 +97,7 @@ In addition to the core functionality, pybind11 provides some extra goodies:
|
||||
1. Clang/LLVM 3.3 or newer (for Apple Xcode's clang, this is 5.0.0 or newer)
|
||||
2. GCC 4.8 or newer
|
||||
3. Microsoft Visual Studio 2015 Update 3 or newer
|
||||
4. Intel C++ compiler 16 or newer (15 with a [workaround](https://github.com/pybind/pybind11/issues/276))
|
||||
4. Intel C++ compiler 17 or newer (16 with pybind11 v2.0 and 15 with pybind11 v2.0 and a [workaround](https://github.com/pybind/pybind11/issues/276))
|
||||
5. Cygwin/GCC (tested on 2.5.1)
|
||||
|
||||
## About
|
||||
@@ -107,6 +105,7 @@ In addition to the core functionality, pybind11 provides some extra goodies:
|
||||
This project was created by [Wenzel Jakob](http://rgl.epfl.ch/people/wjakob).
|
||||
Significant features and/or improvements to the code were contributed by
|
||||
Jonas Adler,
|
||||
Lori A. Burns,
|
||||
Sylvain Corlay,
|
||||
Trent Houliston,
|
||||
Axel Huebl,
|
||||
@@ -119,6 +118,7 @@ Ben Pritchard,
|
||||
Jason Rhinelander,
|
||||
Boris Schäling,
|
||||
Pim Schellart,
|
||||
Henry Schreiner,
|
||||
Ivan Smirnov, and
|
||||
Patrick Stewart.
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ Provided conversions
|
||||
|
||||
.. rubric:: Python to C++
|
||||
|
||||
- ``datetime.datetime`` → ``std::chrono::system_clock::time_point``
|
||||
- ``datetime.datetime`` or ``datetime.date`` or ``datetime.time`` → ``std::chrono::system_clock::time_point``
|
||||
Date/time objects are converted into system clock timepoints. Any
|
||||
timezone information is ignored and the type is treated as a naive
|
||||
object.
|
||||
|
||||
@@ -37,11 +37,11 @@ that maps into the source ``numpy.ndarray`` data: this requires both that the
|
||||
data types are the same (e.g. ``dtype='float64'`` and ``MatrixType::Scalar`` is
|
||||
``double``); and that the storage is layout compatible. The latter limitation
|
||||
is discussed in detail in the section below, and requires careful
|
||||
consideration: by default, numpy matrices and eigen matrices are *not* storage
|
||||
consideration: by default, numpy matrices and Eigen matrices are *not* storage
|
||||
compatible.
|
||||
|
||||
If the numpy matrix cannot be used as is (either because its types differ, e.g.
|
||||
passing an array of integers to an Eigen paramater requiring doubles, or
|
||||
passing an array of integers to an Eigen parameter requiring doubles, or
|
||||
because the storage is incompatible), pybind11 makes a temporary copy and
|
||||
passes the copy instead.
|
||||
|
||||
@@ -89,7 +89,7 @@ as dictated by the binding function's return value policy (see the
|
||||
documentation on :ref:`return_value_policies` for full details). That means,
|
||||
without an explicit return value policy, lvalue references will be copied and
|
||||
pointers will be managed by pybind11. In order to avoid copying, you should
|
||||
explictly specify an appropriate return value policy, as in the following
|
||||
explicitly specify an appropriate return value policy, as in the following
|
||||
example:
|
||||
|
||||
.. code-block:: cpp
|
||||
@@ -226,7 +226,7 @@ order.
|
||||
Failing rather than copying
|
||||
===========================
|
||||
|
||||
The default behaviour when binding ``Eigen::Ref<const MatrixType>`` eigen
|
||||
The default behaviour when binding ``Eigen::Ref<const MatrixType>`` Eigen
|
||||
references is to copy matrix values when passed a numpy array that does not
|
||||
conform to the element type of ``MatrixType`` or does not have a compatible
|
||||
stride layout. If you want to explicitly avoid copying in such a case, you
|
||||
@@ -275,7 +275,7 @@ Vectors versus column/row matrices
|
||||
Eigen and numpy have fundamentally different notions of a vector. In Eigen, a
|
||||
vector is simply a matrix with the number of columns or rows set to 1 at
|
||||
compile time (for a column vector or row vector, respectively). Numpy, in
|
||||
contast, has comparable 2-dimensional 1xN and Nx1 arrays, but *also* has
|
||||
contrast, has comparable 2-dimensional 1xN and Nx1 arrays, but *also* has
|
||||
1-dimensional arrays of size N.
|
||||
|
||||
When passing a 2-dimensional 1xN or Nx1 array to Eigen, the Eigen type must
|
||||
@@ -287,15 +287,15 @@ On the other hand, pybind11 allows you to pass 1-dimensional arrays of length N
|
||||
as Eigen parameters. If the Eigen type can hold a column vector of length N it
|
||||
will be passed as such a column vector. If not, but the Eigen type constraints
|
||||
will accept a row vector, it will be passed as a row vector. (The column
|
||||
vector takes precendence when both are supported, for example, when passing a
|
||||
vector takes precedence when both are supported, for example, when passing a
|
||||
1D numpy array to a MatrixXd argument). Note that the type need not be
|
||||
expicitly a vector: it is permitted to pass a 1D numpy array of size 5 to an
|
||||
explicitly a vector: it is permitted to pass a 1D numpy array of size 5 to an
|
||||
Eigen ``Matrix<double, Dynamic, 5>``: you would end up with a 1x5 Eigen matrix.
|
||||
Passing the same to an ``Eigen::MatrixXd`` would result in a 5x1 Eigen matrix.
|
||||
|
||||
When returning an eigen vector to numpy, the conversion is ambiguous: a row
|
||||
When returning an Eigen vector to numpy, the conversion is ambiguous: a row
|
||||
vector of length 4 could be returned as either a 1D array of length 4, or as a
|
||||
2D array of size 1x4. When encoutering such a situation, pybind11 compromises
|
||||
2D array of size 1x4. When encountering such a situation, pybind11 compromises
|
||||
by considering the returned Eigen type: if it is a compile-time vector--that
|
||||
is, the type has either the number of rows or columns set to 1 at compile
|
||||
time--pybind11 converts to a 1D numpy array when returning the value. For
|
||||
|
||||
@@ -131,6 +131,8 @@ as arguments and return values, refer to the section on binding :ref:`classes`.
|
||||
+------------------------------------+---------------------------+-------------------------------+
|
||||
| ``std::vector<T>`` | STL dynamic array | :file:`pybind11/stl.h` |
|
||||
+------------------------------------+---------------------------+-------------------------------+
|
||||
| ``std::deque<T>`` | STL double-ended queue | :file:`pybind11/stl.h` |
|
||||
+------------------------------------+---------------------------+-------------------------------+
|
||||
| ``std::valarray<T>`` | STL value array | :file:`pybind11/stl.h` |
|
||||
+------------------------------------+---------------------------+-------------------------------+
|
||||
| ``std::list<T>`` | STL linked list | :file:`pybind11/stl.h` |
|
||||
|
||||
@@ -5,7 +5,7 @@ Automatic conversion
|
||||
====================
|
||||
|
||||
When including the additional header file :file:`pybind11/stl.h`, conversions
|
||||
between ``std::vector<>``/``std::list<>``/``std::array<>``,
|
||||
between ``std::vector<>``/``std::deque<>``/``std::list<>``/``std::array<>``,
|
||||
``std::set<>``/``std::unordered_set<>``, and
|
||||
``std::map<>``/``std::unordered_map<>`` and the Python ``list``, ``set`` and
|
||||
``dict`` data structures are automatically enabled. The types ``std::pair<>``
|
||||
@@ -175,9 +175,6 @@ in Python, and to define a set of available operations, e.g.:
|
||||
}, py::keep_alive<0, 1>()) /* Keep vector alive while iterator is used */
|
||||
// ....
|
||||
|
||||
Please take a look at the :ref:`macro_notes` before using the
|
||||
``PYBIND11_MAKE_OPAQUE`` macro.
|
||||
|
||||
.. seealso::
|
||||
|
||||
The file :file:`tests/test_opaque_types.cpp` contains a complete
|
||||
|
||||
@@ -58,7 +58,9 @@ Passing bytes to C++
|
||||
--------------------
|
||||
|
||||
A Python ``bytes`` object will be passed to C++ functions that accept
|
||||
``std::string`` or ``char*`` *without* conversion.
|
||||
``std::string`` or ``char*`` *without* conversion. On Python 3, in order to
|
||||
make a function *only* accept ``bytes`` (and not ``str``), declare it as taking
|
||||
a ``py::bytes`` argument.
|
||||
|
||||
|
||||
Returning C++ strings to Python
|
||||
|
||||
@@ -46,11 +46,10 @@ Normally, the binding code for these classes would look as follows:
|
||||
.. code-block:: cpp
|
||||
|
||||
PYBIND11_MODULE(example, m) {
|
||||
py::class_<Animal> animal(m, "Animal");
|
||||
animal
|
||||
py::class_<Animal>(m, "Animal")
|
||||
.def("go", &Animal::go);
|
||||
|
||||
py::class_<Dog>(m, "Dog", animal)
|
||||
py::class_<Dog, Animal>(m, "Dog")
|
||||
.def(py::init<>());
|
||||
|
||||
m.def("call_go", &call_go);
|
||||
@@ -81,10 +80,10 @@ helper class that is defined as follows:
|
||||
}
|
||||
};
|
||||
|
||||
The macro :func:`PYBIND11_OVERLOAD_PURE` should be used for pure virtual
|
||||
functions, and :func:`PYBIND11_OVERLOAD` should be used for functions which have
|
||||
a default implementation. There are also two alternate macros
|
||||
:func:`PYBIND11_OVERLOAD_PURE_NAME` and :func:`PYBIND11_OVERLOAD_NAME` which
|
||||
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
|
||||
: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
|
||||
when the C++ and Python versions of the
|
||||
@@ -93,15 +92,14 @@ function have different names, e.g. ``operator()`` vs ``__call__``.
|
||||
The binding code also needs a few minor adaptations (highlighted):
|
||||
|
||||
.. code-block:: cpp
|
||||
:emphasize-lines: 2,4,5
|
||||
:emphasize-lines: 2,3
|
||||
|
||||
PYBIND11_MODULE(example, m) {
|
||||
py::class_<Animal, PyAnimal /* <--- trampoline*/> animal(m, "Animal");
|
||||
animal
|
||||
py::class_<Animal, PyAnimal /* <--- trampoline*/>(m, "Animal")
|
||||
.def(py::init<>())
|
||||
.def("go", &Animal::go);
|
||||
|
||||
py::class_<Dog>(m, "Dog", animal)
|
||||
py::class_<Dog, Animal>(m, "Dog")
|
||||
.def(py::init<>());
|
||||
|
||||
m.def("call_go", &call_go);
|
||||
@@ -116,11 +114,11 @@ define a constructor as usual.
|
||||
Bindings should be made against the actual class, not the trampoline helper class.
|
||||
|
||||
.. code-block:: cpp
|
||||
:emphasize-lines: 3
|
||||
|
||||
py::class_<Animal, PyAnimal /* <--- trampoline*/> animal(m, "Animal");
|
||||
animal
|
||||
.def(py::init<>())
|
||||
.def("go", &PyAnimal::go); /* <--- THIS IS WRONG, use &Animal::go */
|
||||
py::class_<Animal, PyAnimal /* <--- trampoline*/>(m, "Animal");
|
||||
.def(py::init<>())
|
||||
.def("go", &PyAnimal::go); /* <--- THIS IS WRONG, use &Animal::go */
|
||||
|
||||
Note, however, that the above is sufficient for allowing python classes to
|
||||
extend ``Animal``, but not ``Dog``: see :ref:`virtual_and_inheritance` for the
|
||||
@@ -155,9 +153,9 @@ Here is an example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class Dachschund(Dog):
|
||||
class Dachshund(Dog):
|
||||
def __init__(self, name):
|
||||
Dog.__init__(self) # Without this, undefind behavior may occur if the C++ portions are referenced.
|
||||
Dog.__init__(self) # Without this, undefined behavior may occur if the C++ portions are referenced.
|
||||
self.name = name
|
||||
def bark(self):
|
||||
return "yap!"
|
||||
@@ -241,7 +239,7 @@ override the ``name()`` method):
|
||||
class PyDog : public Dog {
|
||||
public:
|
||||
using Dog::Dog; // Inherit constructors
|
||||
std::string go(int n_times) override { PYBIND11_OVERLOAD_PURE(std::string, Dog, go, n_times); }
|
||||
std::string go(int n_times) override { PYBIND11_OVERLOAD(std::string, Dog, go, n_times); }
|
||||
std::string name() override { PYBIND11_OVERLOAD(std::string, Dog, name, ); }
|
||||
std::string bark() override { PYBIND11_OVERLOAD(std::string, Dog, bark, ); }
|
||||
};
|
||||
@@ -327,6 +325,10 @@ can now create a python class that inherits from ``Dog``:
|
||||
Extended trampoline class functionality
|
||||
=======================================
|
||||
|
||||
.. _extended_class_functionality_forced_trampoline:
|
||||
|
||||
Forced trampoline class initialisation
|
||||
--------------------------------------
|
||||
The trampoline classes described in the previous sections are, by default, only
|
||||
initialized when needed. More specifically, they are initialized when a python
|
||||
class actually inherits from a registered type (instead of merely creating an
|
||||
@@ -354,6 +356,45 @@ ensuring member initialization and (eventual) destruction.
|
||||
See the file :file:`tests/test_virtual_functions.cpp` for complete examples
|
||||
showing both normal and forced trampoline instantiation.
|
||||
|
||||
Different method signatures
|
||||
---------------------------
|
||||
The macro's introduced in :ref:`overriding_virtuals` cover most of the standard
|
||||
use cases when exposing C++ classes to Python. Sometimes it is hard or unwieldy
|
||||
to create a direct one-on-one mapping between the arguments and method return
|
||||
type.
|
||||
|
||||
An example would be when the C++ signature contains output arguments using
|
||||
references (See also :ref:`faq_reference_arguments`). Another way of solving
|
||||
this is to use the method body of the trampoline class to do conversions to the
|
||||
input and return of the Python method.
|
||||
|
||||
The main building block to do so is the :func:`get_overload`, this function
|
||||
allows retrieving a method implemented in Python from within the trampoline's
|
||||
methods. Consider for example a C++ method which has the signature
|
||||
``bool myMethod(int32_t& value)``, where the return indicates whether
|
||||
something should be done with the ``value``. This can be made convenient on the
|
||||
Python side by allowing the Python function to return ``None`` or an ``int``:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
bool MyClass::myMethod(int32_t& value)
|
||||
{
|
||||
pybind11::gil_scoped_acquire gil; // Acquire the GIL while in this scope.
|
||||
// Try to look up the overloaded method on the Python side.
|
||||
pybind11::function overload = pybind11::get_overload(this, "myMethod");
|
||||
if (overload) { // method is found
|
||||
auto obj = overload(value); // Call the Python function.
|
||||
if (py::isinstance<py::int_>(obj)) { // check if it returned a Python integer type
|
||||
value = obj.cast<int32_t>(); // Cast it and assign it to the value.
|
||||
return true; // Return true; value should be used.
|
||||
} else {
|
||||
return false; // Python returned none, return false.
|
||||
}
|
||||
}
|
||||
return false; // Alternatively return MyClass::myMethod(value);
|
||||
}
|
||||
|
||||
|
||||
.. _custom_constructors:
|
||||
|
||||
Custom constructors
|
||||
@@ -621,6 +662,7 @@ to Python.
|
||||
.def(py::self *= float())
|
||||
.def(float() * py::self)
|
||||
.def(py::self * float())
|
||||
.def(-py::self)
|
||||
.def("__repr__", &Vector2::toString);
|
||||
}
|
||||
|
||||
@@ -760,7 +802,7 @@ document)---pybind11 will automatically find out which is which. The only
|
||||
requirement is that the first template argument is the type to be declared.
|
||||
|
||||
It is also permitted to inherit multiply from exported C++ classes in Python,
|
||||
as well as inheriting from multiple Python and/or pybind-exported classes.
|
||||
as well as inheriting from multiple Python and/or pybind11-exported classes.
|
||||
|
||||
There is one caveat regarding the implementation of this feature:
|
||||
|
||||
@@ -781,7 +823,7 @@ are listed.
|
||||
Module-local class bindings
|
||||
===========================
|
||||
|
||||
When creating a binding for a class, pybind by default makes that binding
|
||||
When creating a binding for a class, pybind11 by default makes that binding
|
||||
"global" across modules. What this means is that a type defined in one module
|
||||
can be returned from any module resulting in the same Python type. For
|
||||
example, this allows the following:
|
||||
@@ -999,3 +1041,86 @@ described trampoline:
|
||||
requires a more explicit function binding in the form of
|
||||
``.def("foo", static_cast<int (A::*)() const>(&Publicist::foo));``
|
||||
where ``int (A::*)() const`` is the type of ``A::foo``.
|
||||
|
||||
Custom automatic downcasters
|
||||
============================
|
||||
|
||||
As explained in :ref:`inheritance`, pybind11 comes with built-in
|
||||
understanding of the dynamic type of polymorphic objects in C++; that
|
||||
is, returning a Pet to Python produces a Python object that knows it's
|
||||
wrapping a Dog, if Pet has virtual methods and pybind11 knows about
|
||||
Dog and this Pet is in fact a Dog. Sometimes, you might want to
|
||||
provide this automatic downcasting behavior when creating bindings for
|
||||
a class hierarchy that does not use standard C++ polymorphism, such as
|
||||
LLVM [#f4]_. As long as there's some way to determine at runtime
|
||||
whether a downcast is safe, you can proceed by specializing the
|
||||
``pybind11::polymorphic_type_hook`` template:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
enum class PetKind { Cat, Dog, Zebra };
|
||||
struct Pet { // Not polymorphic: has no virtual methods
|
||||
const PetKind kind;
|
||||
int age = 0;
|
||||
protected:
|
||||
Pet(PetKind _kind) : kind(_kind) {}
|
||||
};
|
||||
struct Dog : Pet {
|
||||
Dog() : Pet(PetKind::Dog) {}
|
||||
std::string sound = "woof!";
|
||||
std::string bark() const { return sound; }
|
||||
};
|
||||
|
||||
namespace pybind11 {
|
||||
template<> struct polymorphic_type_hook<Pet> {
|
||||
static const void *get(const Pet *src, const std::type_info*& type) {
|
||||
// note that src may be nullptr
|
||||
if (src && src->kind == PetKind::Dog) {
|
||||
type = &typeid(Dog);
|
||||
return static_cast<const Dog*>(src);
|
||||
}
|
||||
return src;
|
||||
}
|
||||
};
|
||||
} // namespace pybind11
|
||||
|
||||
When pybind11 wants to convert a C++ pointer of type ``Base*`` to a
|
||||
Python object, it calls ``polymorphic_type_hook<Base>::get()`` to
|
||||
determine if a downcast is possible. The ``get()`` function should use
|
||||
whatever runtime information is available to determine if its ``src``
|
||||
parameter is in fact an instance of some class ``Derived`` that
|
||||
inherits from ``Base``. If it finds such a ``Derived``, it sets ``type
|
||||
= &typeid(Derived)`` and returns a pointer to the ``Derived`` object
|
||||
that contains ``src``. Otherwise, it just returns ``src``, leaving
|
||||
``type`` at its default value of nullptr. If you set ``type`` to a
|
||||
type that pybind11 doesn't know about, no downcasting will occur, and
|
||||
the original ``src`` pointer will be used with its static type
|
||||
``Base*``.
|
||||
|
||||
It is critical that the returned pointer and ``type`` argument of
|
||||
``get()`` agree with each other: if ``type`` is set to something
|
||||
non-null, the returned pointer must point to the start of an object
|
||||
whose type is ``type``. If the hierarchy being exposed uses only
|
||||
single inheritance, a simple ``return src;`` will achieve this just
|
||||
fine, but in the general case, you must cast ``src`` to the
|
||||
appropriate derived-class pointer (e.g. using
|
||||
``static_cast<Derived>(src)``) before allowing it to be returned as a
|
||||
``void*``.
|
||||
|
||||
.. [#f4] https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html
|
||||
|
||||
.. note::
|
||||
|
||||
pybind11's standard support for downcasting objects whose types
|
||||
have virtual methods is implemented using
|
||||
``polymorphic_type_hook`` too, using the standard C++ ability to
|
||||
determine the most-derived type of a polymorphic object using
|
||||
``typeid()`` and to cast a base pointer to that most-derived type
|
||||
(even if you don't know what it is) using ``dynamic_cast<void*>``.
|
||||
|
||||
.. seealso::
|
||||
|
||||
The file :file:`tests/test_tagbased_polymorphic.cpp` contains a
|
||||
more complete example, including a demonstration of how to provide
|
||||
automatic downcasting for an entire class hierarchy without
|
||||
writing one get() function for each class.
|
||||
|
||||
@@ -11,45 +11,45 @@ exceptions:
|
||||
|
||||
.. tabularcolumns:: |p{0.5\textwidth}|p{0.45\textwidth}|
|
||||
|
||||
+--------------------------------------+------------------------------+
|
||||
| C++ exception type | Python exception type |
|
||||
+======================================+==============================+
|
||||
| :class:`std::exception` | ``RuntimeError`` |
|
||||
+--------------------------------------+------------------------------+
|
||||
| :class:`std::bad_alloc` | ``MemoryError`` |
|
||||
+--------------------------------------+------------------------------+
|
||||
| :class:`std::domain_error` | ``ValueError`` |
|
||||
+--------------------------------------+------------------------------+
|
||||
| :class:`std::invalid_argument` | ``ValueError`` |
|
||||
+--------------------------------------+------------------------------+
|
||||
| :class:`std::length_error` | ``ValueError`` |
|
||||
+--------------------------------------+------------------------------+
|
||||
| :class:`std::out_of_range` | ``ValueError`` |
|
||||
+--------------------------------------+------------------------------+
|
||||
| :class:`std::range_error` | ``ValueError`` |
|
||||
+--------------------------------------+------------------------------+
|
||||
| :class:`pybind11::stop_iteration` | ``StopIteration`` (used to |
|
||||
| | implement custom iterators) |
|
||||
+--------------------------------------+------------------------------+
|
||||
| :class:`pybind11::index_error` | ``IndexError`` (used to |
|
||||
| | indicate out of bounds |
|
||||
| | accesses in ``__getitem__``, |
|
||||
| | ``__setitem__``, etc.) |
|
||||
+--------------------------------------+------------------------------+
|
||||
| :class:`pybind11::value_error` | ``ValueError`` (used to |
|
||||
| | indicate wrong value passed |
|
||||
| | in ``container.remove(...)`` |
|
||||
+--------------------------------------+------------------------------+
|
||||
| :class:`pybind11::key_error` | ``KeyError`` (used to |
|
||||
| | indicate out of bounds |
|
||||
| | accesses in ``__getitem__``, |
|
||||
| | ``__setitem__`` in dict-like |
|
||||
| | objects, etc.) |
|
||||
+--------------------------------------+------------------------------+
|
||||
| :class:`pybind11::error_already_set` | Indicates that the Python |
|
||||
| | exception flag has already |
|
||||
| | been initialized |
|
||||
+--------------------------------------+------------------------------+
|
||||
+--------------------------------------+--------------------------------------+
|
||||
| C++ exception type | Python exception type |
|
||||
+======================================+======================================+
|
||||
| :class:`std::exception` | ``RuntimeError`` |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
| :class:`std::bad_alloc` | ``MemoryError`` |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
| :class:`std::domain_error` | ``ValueError`` |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
| :class:`std::invalid_argument` | ``ValueError`` |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
| :class:`std::length_error` | ``ValueError`` |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
| :class:`std::out_of_range` | ``IndexError`` |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
| :class:`std::range_error` | ``ValueError`` |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
| :class:`pybind11::stop_iteration` | ``StopIteration`` (used to implement |
|
||||
| | custom iterators) |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
| :class:`pybind11::index_error` | ``IndexError`` (used to indicate out |
|
||||
| | of bounds access in ``__getitem__``, |
|
||||
| | ``__setitem__``, etc.) |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
| :class:`pybind11::value_error` | ``ValueError`` (used to indicate |
|
||||
| | wrong value passed in |
|
||||
| | ``container.remove(...)``) |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
| :class:`pybind11::key_error` | ``KeyError`` (used to indicate out |
|
||||
| | of bounds access in ``__getitem__``, |
|
||||
| | ``__setitem__`` in dict-like |
|
||||
| | objects, etc.) |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
| :class:`pybind11::error_already_set` | Indicates that the Python exception |
|
||||
| | flag has already been set via Python |
|
||||
| | API calls from C++ code; this C++ |
|
||||
| | exception is used to propagate such |
|
||||
| | a Python exception back to Python. |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
|
||||
When a Python function invoked from C++ throws an exception, it is converted
|
||||
into a C++ exception of type :class:`error_already_set` whose string payload
|
||||
@@ -138,5 +138,5 @@ section.
|
||||
error return without exception set``.
|
||||
|
||||
Exceptions that you do not plan to handle should simply not be caught, or
|
||||
may be explicity (re-)thrown to delegate it to the other,
|
||||
may be explicitly (re-)thrown to delegate it to the other,
|
||||
previously-declared existing exception translators.
|
||||
|
||||
@@ -126,7 +126,7 @@ targeted arguments can be passed through the :class:`cpp_function` constructor:
|
||||
|
||||
.. warning::
|
||||
|
||||
Code with invalid return value policies might access unitialized memory or
|
||||
Code with invalid return value policies might access uninitialized memory or
|
||||
free data structures multiple times, which can lead to hard-to-debug
|
||||
non-determinism and segmentation faults, hence it is worth spending the
|
||||
time to understand all the different options in the table above.
|
||||
@@ -438,7 +438,7 @@ To explicitly enable or disable this behaviour, using the
|
||||
py::class_<Cat>(m, "Cat").def(py::init<>());
|
||||
m.def("bark", [](Dog *dog) -> std::string {
|
||||
if (dog) return "woof!"; /* Called with a Dog instance */
|
||||
else return "(no dog)"; /* Called with None, d == nullptr */
|
||||
else return "(no dog)"; /* Called with None, dog == nullptr */
|
||||
}, py::arg("dog").none(true));
|
||||
m.def("meow", [](Cat *cat) -> std::string {
|
||||
// Can't be called with None argument
|
||||
@@ -467,13 +467,22 @@ dog)"``, while attempting to call ``meow(None)`` will raise a ``TypeError``:
|
||||
|
||||
The default behaviour when the tag is unspecified is to allow ``None``.
|
||||
|
||||
.. note::
|
||||
|
||||
Even when ``.none(true)`` is specified for an argument, ``None`` will be converted to a
|
||||
``nullptr`` *only* for custom and :ref:`opaque <opaque>` types. Pointers to built-in types
|
||||
(``double *``, ``int *``, ...) and STL types (``std::vector<T> *``, ...; if ``pybind11/stl.h``
|
||||
is included) are copied when converted to C++ (see :doc:`/advanced/cast/overview`) and will
|
||||
not allow ``None`` as argument. To pass optional argument of these copied types consider
|
||||
using ``std::optional<T>``
|
||||
|
||||
Overload resolution order
|
||||
=========================
|
||||
|
||||
When a function or method with multiple overloads is called from Python,
|
||||
pybind11 determines which overload to call in two passes. The first pass
|
||||
attempts to call each overload without allowing argument conversion (as if
|
||||
every argument had been specified as ``py::arg().noconvert()`` as decribed
|
||||
every argument had been specified as ``py::arg().noconvert()`` as described
|
||||
above).
|
||||
|
||||
If no overload succeeds in the no-conversion first pass, a second pass is
|
||||
|
||||
@@ -7,13 +7,32 @@ General notes regarding convenience macros
|
||||
==========================================
|
||||
|
||||
pybind11 provides a few convenience macros such as
|
||||
:func:`PYBIND11_MAKE_OPAQUE` and :func:`PYBIND11_DECLARE_HOLDER_TYPE`, and
|
||||
``PYBIND11_OVERLOAD_*``. Since these are "just" macros that are evaluated
|
||||
in the preprocessor (which has no concept of types), they *will* get confused
|
||||
by commas in a template argument such as ``PYBIND11_OVERLOAD(MyReturnValue<T1,
|
||||
T2>, myFunc)``. In this case, the preprocessor assumes that the comma indicates
|
||||
the beginning of the next parameter. Use a ``typedef`` to bind the template to
|
||||
another name and use it in the macro to avoid this problem.
|
||||
:func:`PYBIND11_DECLARE_HOLDER_TYPE` and ``PYBIND11_OVERLOAD_*``. Since these
|
||||
are "just" macros that are evaluated in the preprocessor (which has no concept
|
||||
of types), they *will* get confused by commas in a template argument; for
|
||||
example, consider:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
PYBIND11_OVERLOAD(MyReturnType<T1, T2>, Class<T3, T4>, func)
|
||||
|
||||
The limitation of the C preprocessor interprets this as five arguments (with new
|
||||
arguments beginning after each comma) rather than three. To get around this,
|
||||
there are two alternatives: you can use a type alias, or you can wrap the type
|
||||
using the ``PYBIND11_TYPE`` macro:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
// Version 1: using a type alias
|
||||
using ReturnType = MyReturnType<T1, T2>;
|
||||
using ClassType = Class<T3, T4>;
|
||||
PYBIND11_OVERLOAD(ReturnType, ClassType, func);
|
||||
|
||||
// Version 2: using the PYBIND11_TYPE macro:
|
||||
PYBIND11_OVERLOAD(PYBIND11_TYPE(MyReturnType<T1, T2>),
|
||||
PYBIND11_TYPE(Class<T3, T4>), func)
|
||||
|
||||
The ``PYBIND11_MAKE_OPAQUE`` macro does *not* require the above workarounds.
|
||||
|
||||
.. _gil:
|
||||
|
||||
@@ -137,7 +156,7 @@ Naturally, both methods will fail when there are cyclic dependencies.
|
||||
|
||||
Note that pybind11 code compiled with hidden-by-default symbol visibility (e.g.
|
||||
via the command line flag ``-fvisibility=hidden`` on GCC/Clang), which is
|
||||
required proper pybind11 functionality, can interfere with the ability to
|
||||
required for proper pybind11 functionality, can interfere with the ability to
|
||||
access types defined in another extension module. Working around this requires
|
||||
manually exporting types that are accessed by multiple extension modules;
|
||||
pybind11 provides a macro to do just this:
|
||||
@@ -216,6 +235,21 @@ avoids this issue involves weak reference with a cleanup callback:
|
||||
// Create a weak reference with a cleanup callback and initially leak it
|
||||
(void) py::weakref(m.attr("BaseClass"), cleanup_callback).release();
|
||||
|
||||
.. note::
|
||||
|
||||
PyPy (at least version 5.9) does not garbage collect objects when the
|
||||
interpreter exits. An alternative approach (which also works on CPython) is to use
|
||||
the :py:mod:`atexit` module [#f7]_, for example:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
auto atexit = py::module::import("atexit");
|
||||
atexit.attr("register")(py::cpp_function([]() {
|
||||
// perform cleanup here -- this function is called with the GIL held
|
||||
}));
|
||||
|
||||
.. [#f7] https://docs.python.org/3/library/atexit.html
|
||||
|
||||
|
||||
Generating documentation using Sphinx
|
||||
=====================================
|
||||
|
||||
@@ -41,7 +41,7 @@ completely avoid copy operations with Python expressions like
|
||||
py::format_descriptor<float>::format(), /* Python struct-style format descriptor */
|
||||
2, /* Number of dimensions */
|
||||
{ m.rows(), m.cols() }, /* Buffer dimensions */
|
||||
{ sizeof(float) * m.rows(), /* Strides (in bytes) for each index */
|
||||
{ sizeof(float) * m.cols(), /* Strides (in bytes) for each index */
|
||||
sizeof(float) }
|
||||
);
|
||||
});
|
||||
@@ -261,7 +261,7 @@ simply using ``vectorize``).
|
||||
namespace py = pybind11;
|
||||
|
||||
py::array_t<double> add_arrays(py::array_t<double> input1, py::array_t<double> input2) {
|
||||
auto buf1 = input1.request(), buf2 = input2.request();
|
||||
py::buffer_info buf1 = input1.request(), buf2 = input2.request();
|
||||
|
||||
if (buf1.ndim != 1 || buf2.ndim != 1)
|
||||
throw std::runtime_error("Number of dimensions must be one");
|
||||
@@ -272,7 +272,7 @@ simply using ``vectorize``).
|
||||
/* No pointer is passed, so NumPy will allocate the buffer */
|
||||
auto result = py::array_t<double>(buf1.size);
|
||||
|
||||
auto buf3 = result.request();
|
||||
py::buffer_info buf3 = result.request();
|
||||
|
||||
double *ptr1 = (double *) buf1.ptr,
|
||||
*ptr2 = (double *) buf2.ptr,
|
||||
@@ -364,3 +364,23 @@ uses of ``py::array``:
|
||||
|
||||
The file :file:`tests/test_numpy_array.cpp` contains additional examples
|
||||
demonstrating the use of this feature.
|
||||
|
||||
Ellipsis
|
||||
========
|
||||
|
||||
Python 3 provides a convenient ``...`` ellipsis notation that is often used to
|
||||
slice multidimensional arrays. For instance, the following snippet extracts the
|
||||
middle dimensions of a tensor with the first and last index set to zero.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
a = # a NumPy array
|
||||
b = a[0, ..., 0]
|
||||
|
||||
The function ``py::ellipsis()`` function can be used to perform the same
|
||||
operation on the C++ side:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
py::array a = /* A NumPy array */;
|
||||
py::array b = a[py::make_tuple(0, py::ellipsis(), 0)];
|
||||
|
||||
@@ -6,10 +6,297 @@ Changelog
|
||||
Starting with version 1.8.0, pybind11 releases use a `semantic versioning
|
||||
<http://semver.org>`_ policy.
|
||||
|
||||
v2.3.0 (Not yet released)
|
||||
v2.4.1 (Sep 20, 2019)
|
||||
-----------------------------------------------------
|
||||
|
||||
* TBD
|
||||
* Fixed a problem involving implicit conversion from enumerations to integers
|
||||
on Python 3.8. `1780 <https://github.com/pybind/pybind11/pull/1780>`_.
|
||||
|
||||
v2.4.0 (Sep 19, 2019)
|
||||
-----------------------------------------------------
|
||||
|
||||
* Try harder to keep pybind11-internal data structures separate when there
|
||||
are potential ABI incompatibilities. Fixes crashes that occurred when loading
|
||||
multiple pybind11 extensions that were e.g. compiled by GCC (libstdc++)
|
||||
and Clang (libc++).
|
||||
`#1588 <https://github.com/pybind/pybind11/pull/1588>`_ and
|
||||
`c9f5a <https://github.com/pybind/pybind11/commit/c9f5a>`_.
|
||||
|
||||
* Added support for ``__await__``, ``__aiter__``, and ``__anext__`` protocols.
|
||||
`#1842 <https://github.com/pybind/pybind11/pull/1842>`_.
|
||||
|
||||
* ``pybind11_add_module()``: don't strip symbols when compiling in
|
||||
``RelWithDebInfo`` mode. `#1980
|
||||
<https://github.com/pybind/pybind11/pull/1980>`_.
|
||||
|
||||
* ``enum_``: Reproduce Python behavior when comparing against invalid values
|
||||
(e.g. ``None``, strings, etc.). Add back support for ``__invert__()``.
|
||||
`#1912 <https://github.com/pybind/pybind11/pull/1912>`_,
|
||||
`#1907 <https://github.com/pybind/pybind11/pull/1907>`_.
|
||||
|
||||
* List insertion operation for ``py::list``.
|
||||
Added ``.empty()`` to all collection types.
|
||||
Added ``py::set::contains()`` and ``py::dict::contains()``.
|
||||
`#1887 <https://github.com/pybind/pybind11/pull/1887>`_,
|
||||
`#1884 <https://github.com/pybind/pybind11/pull/1884>`_,
|
||||
`#1888 <https://github.com/pybind/pybind11/pull/1888>`_.
|
||||
|
||||
* ``py::details::overload_cast_impl`` is available in C++11 mode, can be used
|
||||
like ``overload_cast`` with an additional set of parantheses.
|
||||
`#1581 <https://github.com/pybind/pybind11/pull/1581>`_.
|
||||
|
||||
* Fixed ``get_include()`` on Conda.
|
||||
`#1877 <https://github.com/pybind/pybind11/pull/1877>`_.
|
||||
|
||||
* ``stl_bind.h``: negative indexing support.
|
||||
`#1882 <https://github.com/pybind/pybind11/pull/1882>`_.
|
||||
|
||||
* Minor CMake fix to add MinGW compatibility.
|
||||
`#1851 <https://github.com/pybind/pybind11/pull/1851>`_.
|
||||
|
||||
* GIL-related fixes.
|
||||
`#1836 <https://github.com/pybind/pybind11/pull/1836>`_,
|
||||
`8b90b <https://github.com/pybind/pybind11/commit/8b90b>`_.
|
||||
|
||||
* Other very minor/subtle fixes and improvements.
|
||||
`#1329 <https://github.com/pybind/pybind11/pull/1329>`_,
|
||||
`#1910 <https://github.com/pybind/pybind11/pull/1910>`_,
|
||||
`#1863 <https://github.com/pybind/pybind11/pull/1863>`_,
|
||||
`#1847 <https://github.com/pybind/pybind11/pull/1847>`_,
|
||||
`#1890 <https://github.com/pybind/pybind11/pull/1890>`_,
|
||||
`#1860 <https://github.com/pybind/pybind11/pull/1860>`_,
|
||||
`#1848 <https://github.com/pybind/pybind11/pull/1848>`_,
|
||||
`#1821 <https://github.com/pybind/pybind11/pull/1821>`_,
|
||||
`#1837 <https://github.com/pybind/pybind11/pull/1837>`_,
|
||||
`#1833 <https://github.com/pybind/pybind11/pull/1833>`_,
|
||||
`#1748 <https://github.com/pybind/pybind11/pull/1748>`_,
|
||||
`#1852 <https://github.com/pybind/pybind11/pull/1852>`_.
|
||||
|
||||
v2.3.0 (June 11, 2019)
|
||||
-----------------------------------------------------
|
||||
|
||||
* Significantly reduced module binary size (10-20%) when compiled in C++11 mode
|
||||
with GCC/Clang, or in any mode with MSVC. Function signatures are now always
|
||||
precomputed at compile time (this was previously only available in C++14 mode
|
||||
for non-MSVC compilers).
|
||||
`#934 <https://github.com/pybind/pybind11/pull/934>`_.
|
||||
|
||||
* Add basic support for tag-based static polymorphism, where classes
|
||||
provide a method to returns the desired type of an instance.
|
||||
`#1326 <https://github.com/pybind/pybind11/pull/1326>`_.
|
||||
|
||||
* Python type wrappers (``py::handle``, ``py::object``, etc.)
|
||||
now support map Python's number protocol onto C++ arithmetic
|
||||
operators such as ``operator+``, ``operator/=``, etc.
|
||||
`#1511 <https://github.com/pybind/pybind11/pull/1511>`_.
|
||||
|
||||
* A number of improvements related to enumerations:
|
||||
|
||||
1. The ``enum_`` implementation was rewritten from scratch to reduce
|
||||
code bloat. Rather than instantiating a full implementation for each
|
||||
enumeration, most code is now contained in a generic base class.
|
||||
`#1511 <https://github.com/pybind/pybind11/pull/1511>`_.
|
||||
|
||||
2. The ``value()`` method of ``py::enum_`` now accepts an optional
|
||||
docstring that will be shown in the documentation of the associated
|
||||
enumeration. `#1160 <https://github.com/pybind/pybind11/pull/1160>`_.
|
||||
|
||||
3. check for already existing enum value and throw an error if present.
|
||||
`#1453 <https://github.com/pybind/pybind11/pull/1453>`_.
|
||||
|
||||
* Support for over-aligned type allocation via C++17's aligned ``new``
|
||||
statement. `#1582 <https://github.com/pybind/pybind11/pull/1582>`_.
|
||||
|
||||
* Added ``py::ellipsis()`` method for slicing of multidimensional NumPy arrays
|
||||
`#1502 <https://github.com/pybind/pybind11/pull/1502>`_.
|
||||
|
||||
* Numerous Improvements to the ``mkdoc.py`` script for extracting documentation
|
||||
from C++ header files.
|
||||
`#1788 <https://github.com/pybind/pybind11/pull/1788>`_.
|
||||
|
||||
* ``pybind11_add_module()``: allow including Python as a ``SYSTEM`` include path.
|
||||
`#1416 <https://github.com/pybind/pybind11/pull/1416>`_.
|
||||
|
||||
* ``pybind11/stl.h`` does not convert strings to ``vector<string>`` anymore.
|
||||
`#1258 <https://github.com/pybind/pybind11/issues/1258>`_.
|
||||
|
||||
* Mark static methods as such to fix auto-generated Sphinx documentation.
|
||||
`#1732 <https://github.com/pybind/pybind11/pull/1732>`_.
|
||||
|
||||
* Re-throw forced unwind exceptions (e.g. during pthread termination).
|
||||
`#1208 <https://github.com/pybind/pybind11/pull/1208>`_.
|
||||
|
||||
* Added ``__contains__`` method to the bindings of maps (``std::map``,
|
||||
``std::unordered_map``).
|
||||
`#1767 <https://github.com/pybind/pybind11/pull/1767>`_.
|
||||
|
||||
* Improvements to ``gil_scoped_acquire``.
|
||||
`#1211 <https://github.com/pybind/pybind11/pull/1211>`_.
|
||||
|
||||
* Type caster support for ``std::deque<T>``.
|
||||
`#1609 <https://github.com/pybind/pybind11/pull/1609>`_.
|
||||
|
||||
* Support for ``std::unique_ptr`` holders, whose deleters differ between a base and derived
|
||||
class. `#1353 <https://github.com/pybind/pybind11/pull/1353>`_.
|
||||
|
||||
* Construction of STL array/vector-like data structures from
|
||||
iterators. Added an ``extend()`` operation.
|
||||
`#1709 <https://github.com/pybind/pybind11/pull/1709>`_,
|
||||
|
||||
* CMake build system improvements for projects that include non-C++
|
||||
files (e.g. plain C, CUDA) in ``pybind11_add_module`` et al.
|
||||
`#1678 <https://github.com/pybind/pybind11/pull/1678>`_.
|
||||
|
||||
* Fixed asynchronous invocation and deallocation of Python functions
|
||||
wrapped in ``std::function``.
|
||||
`#1595 <https://github.com/pybind/pybind11/pull/1595>`_.
|
||||
|
||||
* Fixes regarding return value policy propagation in STL type casters.
|
||||
`#1603 <https://github.com/pybind/pybind11/pull/1603>`_.
|
||||
|
||||
* Fixed scoped enum comparisons.
|
||||
`#1571 <https://github.com/pybind/pybind11/pull/1571>`_.
|
||||
|
||||
* Fixed iostream redirection for code that releases the GIL.
|
||||
`#1368 <https://github.com/pybind/pybind11/pull/1368>`_,
|
||||
|
||||
* A number of CI-related fixes.
|
||||
`#1757 <https://github.com/pybind/pybind11/pull/1757>`_,
|
||||
`#1744 <https://github.com/pybind/pybind11/pull/1744>`_,
|
||||
`#1670 <https://github.com/pybind/pybind11/pull/1670>`_.
|
||||
|
||||
v2.2.4 (September 11, 2018)
|
||||
-----------------------------------------------------
|
||||
|
||||
* Use new Python 3.7 Thread Specific Storage (TSS) implementation if available.
|
||||
`#1454 <https://github.com/pybind/pybind11/pull/1454>`_,
|
||||
`#1517 <https://github.com/pybind/pybind11/pull/1517>`_.
|
||||
|
||||
* Fixes for newer MSVC versions and C++17 mode.
|
||||
`#1347 <https://github.com/pybind/pybind11/pull/1347>`_,
|
||||
`#1462 <https://github.com/pybind/pybind11/pull/1462>`_.
|
||||
|
||||
* Propagate return value policies to type-specific casters
|
||||
when casting STL containers.
|
||||
`#1455 <https://github.com/pybind/pybind11/pull/1455>`_.
|
||||
|
||||
* Allow ostream-redirection of more than 1024 characters.
|
||||
`#1479 <https://github.com/pybind/pybind11/pull/1479>`_.
|
||||
|
||||
* Set ``Py_DEBUG`` define when compiling against a debug Python build.
|
||||
`#1438 <https://github.com/pybind/pybind11/pull/1438>`_.
|
||||
|
||||
* Untangle integer logic in number type caster to work for custom
|
||||
types that may only be castable to a restricted set of builtin types.
|
||||
`#1442 <https://github.com/pybind/pybind11/pull/1442>`_.
|
||||
|
||||
* CMake build system: Remember Python version in cache file.
|
||||
`#1434 <https://github.com/pybind/pybind11/pull/1434>`_.
|
||||
|
||||
* Fix for custom smart pointers: use ``std::addressof`` to obtain holder
|
||||
address instead of ``operator&``.
|
||||
`#1435 <https://github.com/pybind/pybind11/pull/1435>`_.
|
||||
|
||||
* Properly report exceptions thrown during module initialization.
|
||||
`#1362 <https://github.com/pybind/pybind11/pull/1362>`_.
|
||||
|
||||
* Fixed a segmentation fault when creating empty-shaped NumPy array.
|
||||
`#1371 <https://github.com/pybind/pybind11/pull/1371>`_.
|
||||
|
||||
* The version of Intel C++ compiler must be >= 2017, and this is now checked by
|
||||
the header files. `#1363 <https://github.com/pybind/pybind11/pull/1363>`_.
|
||||
|
||||
* A few minor typo fixes and improvements to the test suite, and
|
||||
patches that silence compiler warnings.
|
||||
|
||||
* Vectors now support construction from generators, as well as ``extend()`` from a
|
||||
list or generator.
|
||||
`#1496 <https://github.com/pybind/pybind11/pull/1496>`_.
|
||||
|
||||
|
||||
v2.2.3 (April 29, 2018)
|
||||
-----------------------------------------------------
|
||||
|
||||
* The pybind11 header location detection was replaced by a new implementation
|
||||
that no longer depends on ``pip`` internals (the recently released ``pip``
|
||||
10 has restricted access to this API).
|
||||
`#1190 <https://github.com/pybind/pybind11/pull/1190>`_.
|
||||
|
||||
* Small adjustment to an implementation detail to work around a compiler segmentation fault in Clang 3.3/3.4.
|
||||
`#1350 <https://github.com/pybind/pybind11/pull/1350>`_.
|
||||
|
||||
* The minimal supported version of the Intel compiler was >= 17.0 since
|
||||
pybind11 v2.1. This check is now explicit, and a compile-time error is raised
|
||||
if the compiler meet the requirement.
|
||||
`#1363 <https://github.com/pybind/pybind11/pull/1363>`_.
|
||||
|
||||
* Fixed an endianness-related fault in the test suite.
|
||||
`#1287 <https://github.com/pybind/pybind11/pull/1287>`_.
|
||||
|
||||
v2.2.2 (February 7, 2018)
|
||||
-----------------------------------------------------
|
||||
|
||||
* Fixed a segfault when combining embedded interpreter
|
||||
shutdown/reinitialization with external loaded pybind11 modules.
|
||||
`#1092 <https://github.com/pybind/pybind11/pull/1092>`_.
|
||||
|
||||
* Eigen support: fixed a bug where Nx1/1xN numpy inputs couldn't be passed as
|
||||
arguments to Eigen vectors (which for Eigen are simply compile-time fixed
|
||||
Nx1/1xN matrices).
|
||||
`#1106 <https://github.com/pybind/pybind11/pull/1106>`_.
|
||||
|
||||
* Clarified to license by moving the licensing of contributions from
|
||||
``LICENSE`` into ``CONTRIBUTING.md``: the licensing of contributions is not
|
||||
actually part of the software license as distributed. This isn't meant to be
|
||||
a substantial change in the licensing of the project, but addresses concerns
|
||||
that the clause made the license non-standard.
|
||||
`#1109 <https://github.com/pybind/pybind11/issues/1109>`_.
|
||||
|
||||
* Fixed a regression introduced in 2.1 that broke binding functions with lvalue
|
||||
character literal arguments.
|
||||
`#1128 <https://github.com/pybind/pybind11/pull/1128>`_.
|
||||
|
||||
* MSVC: fix for compilation failures under /permissive-, and added the flag to
|
||||
the appveyor test suite.
|
||||
`#1155 <https://github.com/pybind/pybind11/pull/1155>`_.
|
||||
|
||||
* Fixed ``__qualname__`` generation, and in turn, fixes how class names
|
||||
(especially nested class names) are shown in generated docstrings.
|
||||
`#1171 <https://github.com/pybind/pybind11/pull/1171>`_.
|
||||
|
||||
* Updated the FAQ with a suggested project citation reference.
|
||||
`#1189 <https://github.com/pybind/pybind11/pull/1189>`_.
|
||||
|
||||
* Added fixes for deprecation warnings when compiled under C++17 with
|
||||
``-Wdeprecated`` turned on, and add ``-Wdeprecated`` to the test suite
|
||||
compilation flags.
|
||||
`#1191 <https://github.com/pybind/pybind11/pull/1191>`_.
|
||||
|
||||
* Fixed outdated PyPI URLs in ``setup.py``.
|
||||
`#1213 <https://github.com/pybind/pybind11/pull/1213>`_.
|
||||
|
||||
* Fixed a refcount leak for arguments that end up in a ``py::args`` argument
|
||||
for functions with both fixed positional and ``py::args`` arguments.
|
||||
`#1216 <https://github.com/pybind/pybind11/pull/1216>`_.
|
||||
|
||||
* Fixed a potential segfault resulting from possible premature destruction of
|
||||
``py::args``/``py::kwargs`` arguments with overloaded functions.
|
||||
`#1223 <https://github.com/pybind/pybind11/pull/1223>`_.
|
||||
|
||||
* Fixed ``del map[item]`` for a ``stl_bind.h`` bound stl map.
|
||||
`#1229 <https://github.com/pybind/pybind11/pull/1229>`_.
|
||||
|
||||
* Fixed a regression from v2.1.x where the aggregate initialization could
|
||||
unintentionally end up at a constructor taking a templated
|
||||
``std::initializer_list<T>`` argument.
|
||||
`#1249 <https://github.com/pybind/pybind11/pull/1249>`_.
|
||||
|
||||
* Fixed an issue where calling a function with a keep_alive policy on the same
|
||||
nurse/patient pair would cause the internal patient storage to needlessly
|
||||
grow (unboundedly, if the nurse is long-lived).
|
||||
`#1251 <https://github.com/pybind/pybind11/issues/1251>`_.
|
||||
|
||||
* Various other minor fixes.
|
||||
|
||||
v2.2.1 (September 14, 2017)
|
||||
-----------------------------------------------------
|
||||
@@ -236,6 +523,9 @@ v2.2.0 (August 31, 2017)
|
||||
* Fixed overriding static properties in derived classes.
|
||||
`#784 <https://github.com/pybind/pybind11/pull/784>`_.
|
||||
|
||||
* Added support for write only properties.
|
||||
`#1144 <https://github.com/pybind/pybind11/pull/1144>`_.
|
||||
|
||||
* Improved deduction of member functions of a derived class when its bases
|
||||
aren't registered with pybind11.
|
||||
`#855 <https://github.com/pybind/pybind11/pull/855>`_.
|
||||
@@ -503,7 +793,7 @@ Happy Christmas!
|
||||
being (notably dynamic attributes in custom types).
|
||||
`#527 <https://github.com/pybind/pybind11/pull/527>`_.
|
||||
|
||||
* Significant work on the documentation -- in particular, the monolitic
|
||||
* Significant work on the documentation -- in particular, the monolithic
|
||||
``advanced.rst`` file was restructured into a easier to read hierarchical
|
||||
organization. `#448 <https://github.com/pybind/pybind11/pull/448>`_.
|
||||
|
||||
@@ -571,8 +861,8 @@ Happy Christmas!
|
||||
<https://github.com/pybind/pybind11/pull/527>`_.
|
||||
|
||||
|
||||
3. This version of pybind11 uses a redesigned mechnism for instantiating
|
||||
trempoline classes that are used to override virtual methods from within
|
||||
3. This version of pybind11 uses a redesigned mechanism for instantiating
|
||||
trampoline classes that are used to override virtual methods from within
|
||||
Python. This led to the following user-visible syntax change: instead of
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
@@ -155,6 +155,9 @@ the setter and getter functions:
|
||||
.def_property("name", &Pet::getName, &Pet::setName)
|
||||
// ... remainder ...
|
||||
|
||||
Write only properties can be defined by passing ``nullptr`` as the
|
||||
input for the read function.
|
||||
|
||||
.. seealso::
|
||||
|
||||
Similar functions :func:`class_::def_readwrite_static`,
|
||||
@@ -225,8 +228,8 @@ just brings them on par.
|
||||
|
||||
.. _inheritance:
|
||||
|
||||
Inheritance and automatic upcasting
|
||||
===================================
|
||||
Inheritance and automatic downcasting
|
||||
=====================================
|
||||
|
||||
Suppose now that the example consists of two data structures with an
|
||||
inheritance relationship:
|
||||
@@ -295,7 +298,7 @@ inheritance relationship. This is reflected in Python:
|
||||
|
||||
>>> p = example.pet_store()
|
||||
>>> type(p) # `Dog` instance behind `Pet` pointer
|
||||
Pet # no pointer upcasting for regular non-polymorphic types
|
||||
Pet # no pointer downcasting for regular non-polymorphic types
|
||||
>>> p.bark()
|
||||
AttributeError: 'Pet' object has no attribute 'bark'
|
||||
|
||||
@@ -327,11 +330,11 @@ will automatically recognize this:
|
||||
|
||||
>>> p = example.pet_store2()
|
||||
>>> type(p)
|
||||
PolymorphicDog # automatically upcast
|
||||
PolymorphicDog # automatically downcast
|
||||
>>> p.bark()
|
||||
u'woof!'
|
||||
|
||||
Given a pointer to a polymorphic base, pybind11 performs automatic upcasting
|
||||
Given a pointer to a polymorphic base, pybind11 performs automatic downcasting
|
||||
to the actual derived type. Note that this goes beyond the usual situation in
|
||||
C++: we don't just get access to the virtual functions of the base, we get the
|
||||
concrete derived type including functions and attributes that the base type may
|
||||
@@ -419,6 +422,17 @@ on constness, the ``py::const_`` tag should be used:
|
||||
.def("foo_mutable", py::overload_cast<int, float>(&Widget::foo))
|
||||
.def("foo_const", py::overload_cast<int, float>(&Widget::foo, py::const_));
|
||||
|
||||
If you prefer the ``py::overload_cast`` syntax but have a C++11 compatible compiler only,
|
||||
you can use ``py::detail::overload_cast_impl`` with an additional set of parentheses:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
template <typename... Args>
|
||||
using overload_cast_ = pybind11::detail::overload_cast_impl<Args...>;
|
||||
|
||||
py::class_<Pet>(m, "Pet")
|
||||
.def("set", overload_cast_<int>()(&Pet::set), "Set the pet's age")
|
||||
.def("set", overload_cast_<const std::string &>()(&Pet::set), "Set the pet's name");
|
||||
|
||||
.. [#cpp14] A compiler which supports the ``-std=c++14`` flag
|
||||
or Visual Studio 2015 Update 2 and newer.
|
||||
@@ -485,6 +499,24 @@ The entries defined by the enumeration type are exposed in the ``__members__`` p
|
||||
>>> Pet.Kind.__members__
|
||||
{'Dog': Kind.Dog, 'Cat': Kind.Cat}
|
||||
|
||||
The ``name`` property returns the name of the enum value as a unicode string.
|
||||
|
||||
.. note::
|
||||
|
||||
It is also possible to use ``str(enum)``, however these accomplish different
|
||||
goals. The following shows how these two approaches differ.
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> p = Pet( "Lucy", Pet.Cat )
|
||||
>>> pet_type = p.type
|
||||
>>> pet_type
|
||||
Pet.Cat
|
||||
>>> str(pet_type)
|
||||
'Pet.Cat'
|
||||
>>> pet_type.name
|
||||
'Cat'
|
||||
|
||||
.. note::
|
||||
|
||||
When the special tag ``py::arithmetic()`` is specified to the ``enum_``
|
||||
|
||||
@@ -59,7 +59,7 @@ function with the following signature:
|
||||
.. code-block:: cmake
|
||||
|
||||
pybind11_add_module(<name> [MODULE | SHARED] [EXCLUDE_FROM_ALL]
|
||||
[NO_EXTRAS] [THIN_LTO] source1 [source2 ...])
|
||||
[NO_EXTRAS] [SYSTEM] [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,6 +86,10 @@ 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
|
||||
@@ -145,6 +149,18 @@ See the `Config file`_ docstring for details of relevant CMake variables.
|
||||
find_package(pybind11 REQUIRED)
|
||||
pybind11_add_module(example example.cpp)
|
||||
|
||||
Note that ``find_package(pybind11)`` will only work correctly if pybind11
|
||||
has been correctly installed on the system, e. g. after downloading or cloning
|
||||
the pybind11 repository :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cd pybind11
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make install
|
||||
|
||||
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
|
||||
|
||||
@@ -61,9 +61,9 @@ author = 'Wenzel Jakob'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '2.2'
|
||||
version = '2.4'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '2.2.1'
|
||||
release = '2.4.1'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -4,9 +4,13 @@ Frequently asked questions
|
||||
"ImportError: dynamic module does not define init function"
|
||||
===========================================================
|
||||
|
||||
You are likely using an incompatible version of Python (for instance, the
|
||||
extension library was compiled against Python 2, while the interpreter is
|
||||
running on top of some version of Python 3, or vice versa).
|
||||
1. Make sure that the name specified in PYBIND11_MODULE is identical to the
|
||||
filename of the extension library (without prefixes 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
|
||||
Python 2, while the interpreter is running on top of some version of Python
|
||||
3, or vice versa).
|
||||
|
||||
"Symbol not found: ``__Py_ZeroStruct`` / ``_PyInstanceMethod_Type``"
|
||||
========================================================================
|
||||
@@ -35,6 +39,8 @@ multiple versions of Python and it finds the wrong one, delete
|
||||
|
||||
cmake -DPYTHON_EXECUTABLE:FILEPATH=<path-to-python-executable> .
|
||||
|
||||
.. _faq_reference_arguments:
|
||||
|
||||
Limitations involving reference arguments
|
||||
=========================================
|
||||
|
||||
@@ -116,7 +122,7 @@ following example:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
void init_ex1(py::module &m) {
|
||||
void init_ex2(py::module &m) {
|
||||
m.def("sub", [](int a, int b) { return a - b; });
|
||||
}
|
||||
|
||||
@@ -228,46 +234,64 @@ In addition to decreasing binary size, ``-fvisibility=hidden`` also avoids
|
||||
potential serious issues when loading multiple modules and is required for
|
||||
proper pybind operation. See the previous FAQ entry for more details.
|
||||
|
||||
Another aspect that can require a fair bit of code are function signature
|
||||
descriptions. pybind11 automatically generates human-readable function
|
||||
signatures for docstrings, e.g.:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
| __init__(...)
|
||||
| __init__(*args, **kwargs)
|
||||
| Overloaded function.
|
||||
|
|
||||
| 1. __init__(example.Example1) -> NoneType
|
||||
|
|
||||
| Docstring for overload #1 goes here
|
||||
|
|
||||
| 2. __init__(example.Example1, int) -> NoneType
|
||||
|
|
||||
| Docstring for overload #2 goes here
|
||||
|
|
||||
| 3. __init__(example.Example1, example.Example1) -> NoneType
|
||||
|
|
||||
| Docstring for overload #3 goes here
|
||||
|
||||
|
||||
In C++11 mode, these are generated at run time using string concatenation,
|
||||
which can amount to 10-20% of the size of the resulting binary. If you can,
|
||||
enable C++14 language features (using ``-std=c++14`` for GCC/Clang), in which
|
||||
case signatures are efficiently pre-generated at compile time. Unfortunately,
|
||||
Visual Studio's C++14 support (``constexpr``) is not good enough as of April
|
||||
2016, so it always uses the more expensive run-time approach.
|
||||
|
||||
Working with ancient Visual Studio 2009 builds on Windows
|
||||
Working with ancient Visual Studio 2008 builds on Windows
|
||||
=========================================================
|
||||
|
||||
The official Windows distributions of Python are compiled using truly
|
||||
ancient versions of Visual Studio that lack good C++11 support. Some users
|
||||
implicitly assume that it would be impossible to load a plugin built with
|
||||
Visual Studio 2015 into a Python distribution that was compiled using Visual
|
||||
Studio 2009. However, no such issue exists: it's perfectly legitimate to
|
||||
Studio 2008. However, no such issue exists: it's perfectly legitimate to
|
||||
interface DLLs that are built with different compilers and/or C libraries.
|
||||
Common gotchas to watch out for involve not ``free()``-ing memory region
|
||||
that that were ``malloc()``-ed in another shared library, using data
|
||||
structures with incompatible ABIs, and so on. pybind11 is very careful not
|
||||
to make these types of mistakes.
|
||||
|
||||
Inconsistent detection of Python version in CMake and pybind11
|
||||
==============================================================
|
||||
|
||||
The functions ``find_package(PythonInterp)`` and ``find_package(PythonLibs)`` provided by CMake
|
||||
for Python version detection are not used by pybind11 due to unreliability and limitations that make
|
||||
them unsuitable for pybind11's needs. Instead pybind provides its own, more reliable Python detection
|
||||
CMake code. Conflicts can arise, however, when using pybind11 in a project that *also* uses the CMake
|
||||
Python detection in a system with several Python versions installed.
|
||||
|
||||
This difference may cause inconsistencies and errors if *both* mechanisms are used in the same project. Consider the following
|
||||
Cmake code executed in a system with Python 2.7 and 3.x installed:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package(PythonInterp)
|
||||
find_package(PythonLibs)
|
||||
find_package(pybind11)
|
||||
|
||||
It will detect Python 2.7 and pybind11 will pick it as well.
|
||||
|
||||
In contrast this code:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package(pybind11)
|
||||
find_package(PythonInterp)
|
||||
find_package(PythonLibs)
|
||||
|
||||
will detect Python 3.x for pybind11 and may crash on ``find_package(PythonLibs)`` afterwards.
|
||||
|
||||
It is advised to avoid using ``find_package(PythonInterp)`` and ``find_package(PythonLibs)`` from CMake and rely
|
||||
on pybind11 in detecting Python version. If this is not possible CMake machinery should be called *before* including pybind11.
|
||||
|
||||
How to cite this project?
|
||||
=========================
|
||||
|
||||
We suggest the following BibTeX template to cite pybind11 in scientific
|
||||
discourse:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@misc{pybind11,
|
||||
author = {Wenzel Jakob and Jason Rhinelander and Dean Moldovan},
|
||||
year = {2017},
|
||||
note = {https://github.com/pybind/pybind11},
|
||||
title = {pybind11 -- Seamless operability between C++11 and Python}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ The following core C++ features can be mapped to Python
|
||||
- Custom operators
|
||||
- Single and multiple inheritance
|
||||
- STL data structures
|
||||
- Iterators and ranges
|
||||
- Smart pointers with reference counting like ``std::shared_ptr``
|
||||
- Internal references with correct reference counting
|
||||
- C++ classes with virtual (and pure virtual) methods can be extended in Python
|
||||
@@ -77,9 +76,8 @@ In addition to the core functionality, pybind11 provides some extra goodies:
|
||||
of `PyRosetta`_, an enormous Boost.Python binding project, reported a binary
|
||||
size reduction of **5.4x** and compile time reduction by **5.8x**.
|
||||
|
||||
- When supported by the compiler, two new C++14 features (relaxed constexpr and
|
||||
return value deduction) are used to precompute function signatures at compile
|
||||
time, leading to smaller binaries.
|
||||
- Function signatures are precomputed at compile time (using ``constexpr``),
|
||||
leading to smaller binaries.
|
||||
|
||||
- With little extra effort, C++ types can be pickled and unpickled similar to
|
||||
regular Python objects.
|
||||
@@ -92,4 +90,4 @@ Supported compilers
|
||||
1. Clang/LLVM (any non-ancient version with C++11 support)
|
||||
2. GCC 4.8 or newer
|
||||
3. Microsoft Visual Studio 2015 or newer
|
||||
4. Intel C++ compiler v15 or newer
|
||||
4. Intel C++ compiler v17 or newer (v16 with pybind11 v2.0 and v15 with pybind11 v2.0 and a `workaround <https://github.com/pybind/pybind11/issues/276>`_ )
|
||||
|
||||
@@ -80,12 +80,27 @@ Redirecting C++ streams
|
||||
|
||||
.. doxygenfunction:: add_ostream_redirect
|
||||
|
||||
Python build-in functions
|
||||
Python built-in functions
|
||||
=========================
|
||||
|
||||
.. doxygengroup:: python_builtins
|
||||
:members:
|
||||
|
||||
Inheritance
|
||||
===========
|
||||
|
||||
See :doc:`/classes` and :doc:`/advanced/classes` for more detail.
|
||||
|
||||
.. doxygendefine:: PYBIND11_OVERLOAD
|
||||
|
||||
.. doxygendefine:: PYBIND11_OVERLOAD_PURE
|
||||
|
||||
.. doxygendefine:: PYBIND11_OVERLOAD_NAME
|
||||
|
||||
.. doxygendefine:: PYBIND11_OVERLOAD_PURE_NAME
|
||||
|
||||
.. doxygenfunction:: get_overload
|
||||
|
||||
Exceptions
|
||||
==========
|
||||
|
||||
|
||||
@@ -200,7 +200,8 @@ struct function_record {
|
||||
/// Special data structure which (temporarily) holds metadata about a bound class
|
||||
struct type_record {
|
||||
PYBIND11_NOINLINE type_record()
|
||||
: multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), module_local(false) { }
|
||||
: multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false),
|
||||
default_holder(true), module_local(false) { }
|
||||
|
||||
/// Handle to the parent scope
|
||||
handle scope;
|
||||
@@ -214,11 +215,14 @@ struct type_record {
|
||||
/// How large is the underlying C++ type?
|
||||
size_t type_size = 0;
|
||||
|
||||
/// What is the alignment of the underlying C++ type?
|
||||
size_t type_align = 0;
|
||||
|
||||
/// How large is the type's holder?
|
||||
size_t holder_size = 0;
|
||||
|
||||
/// The global operator new can be overridden with a class-specific variant
|
||||
void *(*operator_new)(size_t) = ::operator new;
|
||||
void *(*operator_new)(size_t) = nullptr;
|
||||
|
||||
/// Function pointer to class_<..>::init_instance
|
||||
void (*init_instance)(instance *, const void *) = nullptr;
|
||||
@@ -278,7 +282,7 @@ struct type_record {
|
||||
}
|
||||
};
|
||||
|
||||
inline function_call::function_call(function_record &f, handle p) :
|
||||
inline function_call::function_call(const function_record &f, handle p) :
|
||||
func(f), parent(p) {
|
||||
args.reserve(f.nargs);
|
||||
args_convert.reserve(f.nargs);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#if defined(PYBIND11_CPP17)
|
||||
# if defined(__has_include)
|
||||
@@ -203,10 +204,10 @@ PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool t
|
||||
}
|
||||
|
||||
struct value_and_holder {
|
||||
instance *inst;
|
||||
size_t index;
|
||||
const detail::type_info *type;
|
||||
void **vh;
|
||||
instance *inst = nullptr;
|
||||
size_t index = 0u;
|
||||
const detail::type_info *type = nullptr;
|
||||
void **vh = nullptr;
|
||||
|
||||
// Main constructor for a found value/holder:
|
||||
value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) :
|
||||
@@ -215,7 +216,7 @@ struct value_and_holder {
|
||||
{}
|
||||
|
||||
// Default constructor (used to signal a value-and-holder not found by get_value_and_holder())
|
||||
value_and_holder() : inst{nullptr} {}
|
||||
value_and_holder() {}
|
||||
|
||||
// Used for past-the-end iterator
|
||||
value_and_holder(size_t index) : index{index} {}
|
||||
@@ -269,8 +270,8 @@ public:
|
||||
|
||||
struct iterator {
|
||||
private:
|
||||
instance *inst;
|
||||
const type_vec *types;
|
||||
instance *inst = nullptr;
|
||||
const type_vec *types = nullptr;
|
||||
value_and_holder curr;
|
||||
friend struct values_and_holders;
|
||||
iterator(instance *inst, const type_vec *tinfo)
|
||||
@@ -570,7 +571,17 @@ public:
|
||||
// Lazy allocation for unallocated values:
|
||||
if (vptr == nullptr) {
|
||||
auto *type = v_h.type ? v_h.type : typeinfo;
|
||||
vptr = type->operator_new(type->type_size);
|
||||
if (type->operator_new) {
|
||||
vptr = type->operator_new(type->type_size);
|
||||
} else {
|
||||
#if defined(PYBIND11_CPP17)
|
||||
if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
|
||||
vptr = ::operator new(type->type_size,
|
||||
(std::align_val_t) type->type_align);
|
||||
else
|
||||
#endif
|
||||
vptr = ::operator new(type->type_size);
|
||||
}
|
||||
}
|
||||
value = vptr;
|
||||
}
|
||||
@@ -764,7 +775,9 @@ template <typename T, typename SFINAE = void> struct is_copy_constructible : std
|
||||
// so, copy constructability depends on whether the value_type is copy constructible.
|
||||
template <typename Container> struct is_copy_constructible<Container, enable_if_t<all_of<
|
||||
std::is_copy_constructible<Container>,
|
||||
std::is_same<typename Container::value_type &, typename Container::reference>
|
||||
std::is_same<typename Container::value_type &, typename Container::reference>,
|
||||
// Avoid infinite recursion
|
||||
negation<std::is_same<Container, typename Container::value_type>>
|
||||
>::value>> : is_copy_constructible<typename Container::value_type> {};
|
||||
|
||||
#if !defined(PYBIND11_CPP17)
|
||||
@@ -774,11 +787,47 @@ template <typename T1, typename T2> struct is_copy_constructible<std::pair<T1, T
|
||||
: all_of<is_copy_constructible<T1>, is_copy_constructible<T2>> {};
|
||||
#endif
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
// polymorphic_type_hook<itype>::get(src, tinfo) determines whether the object pointed
|
||||
// to by `src` actually is an instance of some class derived from `itype`.
|
||||
// If so, it sets `tinfo` to point to the std::type_info representing that derived
|
||||
// type, and returns a pointer to the start of the most-derived object of that type
|
||||
// (in which `src` is a subobject; this will be the same address as `src` in most
|
||||
// single inheritance cases). If not, or if `src` is nullptr, it simply returns `src`
|
||||
// and leaves `tinfo` at its default value of nullptr.
|
||||
//
|
||||
// The default polymorphic_type_hook just returns src. A specialization for polymorphic
|
||||
// types determines the runtime type of the passed object and adjusts the this-pointer
|
||||
// appropriately via dynamic_cast<void*>. This is what enables a C++ Animal* to appear
|
||||
// to Python as a Dog (if Dog inherits from Animal, Animal is polymorphic, Dog is
|
||||
// registered with pybind11, and this Animal is in fact a Dog).
|
||||
//
|
||||
// You may specialize polymorphic_type_hook yourself for types that want to appear
|
||||
// polymorphic to Python but do not use C++ RTTI. (This is a not uncommon pattern
|
||||
// in performance-sensitive applications, used most notably in LLVM.)
|
||||
template <typename itype, typename SFINAE = void>
|
||||
struct polymorphic_type_hook
|
||||
{
|
||||
static const void *get(const itype *src, const std::type_info*&) { return src; }
|
||||
};
|
||||
template <typename itype>
|
||||
struct polymorphic_type_hook<itype, detail::enable_if_t<std::is_polymorphic<itype>::value>>
|
||||
{
|
||||
static const void *get(const itype *src, const std::type_info*& type) {
|
||||
type = src ? &typeid(*src) : nullptr;
|
||||
return dynamic_cast<const void*>(src);
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
/// Generic type caster for objects stored on the heap
|
||||
template <typename type> class type_caster_base : public type_caster_generic {
|
||||
using itype = intrinsic_t<type>;
|
||||
|
||||
public:
|
||||
static PYBIND11_DESCR name() { return type_descr(_<type>()); }
|
||||
static constexpr auto name = _<type>();
|
||||
|
||||
type_caster_base() : type_caster_base(typeid(type)) { }
|
||||
explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { }
|
||||
@@ -793,32 +842,28 @@ public:
|
||||
return cast(&src, return_value_policy::move, parent);
|
||||
}
|
||||
|
||||
// Returns a (pointer, type_info) pair taking care of necessary RTTI type lookup for a
|
||||
// polymorphic type. If the instance isn't derived, returns the non-RTTI base version.
|
||||
template <typename T = itype, enable_if_t<std::is_polymorphic<T>::value, int> = 0>
|
||||
// Returns a (pointer, type_info) pair taking care of necessary type lookup for a
|
||||
// polymorphic type (using RTTI by default, but can be overridden by specializing
|
||||
// polymorphic_type_hook). If the instance isn't derived, returns the base version.
|
||||
static std::pair<const void *, const type_info *> src_and_type(const itype *src) {
|
||||
const void *vsrc = src;
|
||||
auto &cast_type = typeid(itype);
|
||||
const std::type_info *instance_type = nullptr;
|
||||
if (vsrc) {
|
||||
instance_type = &typeid(*src);
|
||||
if (!same_type(cast_type, *instance_type)) {
|
||||
// This is a base pointer to a derived type; if it is a pybind11-registered type, we
|
||||
// can get the correct derived pointer (which may be != base pointer) by a
|
||||
// dynamic_cast to most derived type:
|
||||
if (auto *tpi = get_type_info(*instance_type))
|
||||
return {dynamic_cast<const void *>(src), const_cast<const type_info *>(tpi)};
|
||||
}
|
||||
const void *vsrc = polymorphic_type_hook<itype>::get(src, instance_type);
|
||||
if (instance_type && !same_type(cast_type, *instance_type)) {
|
||||
// This is a base pointer to a derived type. If the derived type is registered
|
||||
// with pybind11, we want to make the full derived object available.
|
||||
// In the typical case where itype is polymorphic, we get the correct
|
||||
// derived pointer (which may be != base pointer) by a dynamic_cast to
|
||||
// most derived type. If itype is not polymorphic, we won't get here
|
||||
// except via a user-provided specialization of polymorphic_type_hook,
|
||||
// and the user has promised that no this-pointer adjustment is
|
||||
// required in that case, so it's OK to use static_cast.
|
||||
if (const auto *tpi = get_type_info(*instance_type))
|
||||
return {vsrc, tpi};
|
||||
}
|
||||
// Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so
|
||||
// don't do a cast
|
||||
return type_caster_generic::src_and_type(vsrc, cast_type, instance_type);
|
||||
}
|
||||
|
||||
// Non-polymorphic type, so no dynamic casting; just call the generic version directly
|
||||
template <typename T = itype, enable_if_t<!std::is_polymorphic<T>::value, int> = 0>
|
||||
static std::pair<const void *, const type_info *> src_and_type(const itype *src) {
|
||||
return type_caster_generic::src_and_type(src, typeid(itype));
|
||||
return type_caster_generic::src_and_type(src, cast_type, instance_type);
|
||||
}
|
||||
|
||||
static handle cast(const itype *src, return_value_policy policy, handle parent) {
|
||||
@@ -835,7 +880,7 @@ public:
|
||||
nullptr, nullptr, holder);
|
||||
}
|
||||
|
||||
template <typename T> using cast_op_type = cast_op_type<T>;
|
||||
template <typename T> using cast_op_type = detail::cast_op_type<T>;
|
||||
|
||||
operator itype*() { return (type *) value; }
|
||||
operator itype&() { if (!value) throw reference_cast_error(); return *((itype *) value); }
|
||||
@@ -885,7 +930,7 @@ private:
|
||||
"std::reference_wrapper<T> caster requires T to have a caster with an `T &` operator");
|
||||
public:
|
||||
bool load(handle src, bool convert) { return subcaster.load(src, convert); }
|
||||
static PYBIND11_DESCR name() { return caster_t::name(); }
|
||||
static constexpr auto name = caster_t::name;
|
||||
static handle cast(const std::reference_wrapper<type> &src, return_value_policy policy, handle parent) {
|
||||
// It is definitely wrong to take ownership of this pointer, so mask that rvp
|
||||
if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic)
|
||||
@@ -900,7 +945,7 @@ public:
|
||||
protected: \
|
||||
type value; \
|
||||
public: \
|
||||
static PYBIND11_DESCR name() { return type_descr(py_name); } \
|
||||
static constexpr auto name = py_name; \
|
||||
template <typename T_, enable_if_t<std::is_same<type, remove_cv_t<T_>>::value, int> = 0> \
|
||||
static handle cast(T_ *src, return_value_policy policy, handle parent) { \
|
||||
if (!src) return none().release(); \
|
||||
@@ -952,9 +997,11 @@ public:
|
||||
}
|
||||
|
||||
bool py_err = py_value == (py_type) -1 && PyErr_Occurred();
|
||||
|
||||
// Protect std::numeric_limits::min/max with parentheses
|
||||
if (py_err || (std::is_integral<T>::value && sizeof(py_type) != sizeof(T) &&
|
||||
(py_value < (py_type) std::numeric_limits<T>::min() ||
|
||||
py_value > (py_type) std::numeric_limits<T>::max()))) {
|
||||
(py_value < (py_type) (std::numeric_limits<T>::min)() ||
|
||||
py_value > (py_type) (std::numeric_limits<T>::max)()))) {
|
||||
bool type_error = py_err && PyErr_ExceptionMatches(
|
||||
#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION)
|
||||
PyExc_SystemError
|
||||
@@ -977,20 +1024,34 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
static handle cast(T src, return_value_policy /* policy */, handle /* parent */) {
|
||||
if (std::is_floating_point<T>::value) {
|
||||
return PyFloat_FromDouble((double) src);
|
||||
} else if (sizeof(T) <= sizeof(long)) {
|
||||
if (std::is_signed<T>::value)
|
||||
return PyLong_FromLong((long) src);
|
||||
else
|
||||
return PyLong_FromUnsignedLong((unsigned long) src);
|
||||
} else {
|
||||
if (std::is_signed<T>::value)
|
||||
return PyLong_FromLongLong((long long) src);
|
||||
else
|
||||
return PyLong_FromUnsignedLongLong((unsigned long long) src);
|
||||
}
|
||||
template<typename U = T>
|
||||
static typename std::enable_if<std::is_floating_point<U>::value, handle>::type
|
||||
cast(U src, return_value_policy /* policy */, handle /* parent */) {
|
||||
return PyFloat_FromDouble((double) src);
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
static typename std::enable_if<!std::is_floating_point<U>::value && std::is_signed<U>::value && (sizeof(U) <= sizeof(long)), handle>::type
|
||||
cast(U src, return_value_policy /* policy */, handle /* parent */) {
|
||||
return PYBIND11_LONG_FROM_SIGNED((long) src);
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
static typename std::enable_if<!std::is_floating_point<U>::value && std::is_unsigned<U>::value && (sizeof(U) <= sizeof(unsigned long)), handle>::type
|
||||
cast(U src, return_value_policy /* policy */, handle /* parent */) {
|
||||
return PYBIND11_LONG_FROM_UNSIGNED((unsigned long) src);
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
static typename std::enable_if<!std::is_floating_point<U>::value && std::is_signed<U>::value && (sizeof(U) > sizeof(long)), handle>::type
|
||||
cast(U src, return_value_policy /* policy */, handle /* parent */) {
|
||||
return PyLong_FromLongLong((long long) src);
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
static typename std::enable_if<!std::is_floating_point<U>::value && std::is_unsigned<U>::value && (sizeof(U) > sizeof(unsigned long)), handle>::type
|
||||
cast(U src, return_value_policy /* policy */, handle /* parent */) {
|
||||
return PyLong_FromUnsignedLongLong((unsigned long long) src);
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(T, _<std::is_integral<T>::value>("int", "float"));
|
||||
@@ -1049,7 +1110,7 @@ public:
|
||||
|
||||
template <typename T> using cast_op_type = void*&;
|
||||
operator void *&() { return value; }
|
||||
static PYBIND11_DESCR name() { return type_descr(_("capsule")); }
|
||||
static constexpr auto name = _("capsule");
|
||||
private:
|
||||
void *value = nullptr;
|
||||
};
|
||||
@@ -1171,7 +1232,7 @@ private:
|
||||
#else
|
||||
// PyPy seems to have multiple problems related to PyUnicode_UTF*: the UTF8 version
|
||||
// sometimes segfaults for unknown reasons, while the UTF16 and 32 versions require a
|
||||
// non-const char * arguments, which is also a nuissance, so bypass the whole thing by just
|
||||
// non-const char * arguments, which is also a nuisance, so bypass the whole thing by just
|
||||
// passing the encoding as a string value, which works properly:
|
||||
return PyUnicode_Decode(buffer, nbytes, UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr);
|
||||
#endif
|
||||
@@ -1216,6 +1277,7 @@ template <typename CharT> struct type_caster<CharT, enable_if_t<is_std_char_type
|
||||
using StringCaster = type_caster<StringType>;
|
||||
StringCaster str_caster;
|
||||
bool none = false;
|
||||
CharT one_char = 0;
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
if (!src) return false;
|
||||
@@ -1243,7 +1305,7 @@ public:
|
||||
}
|
||||
|
||||
operator CharT*() { return none ? nullptr : const_cast<CharT *>(static_cast<StringType &>(str_caster).c_str()); }
|
||||
operator CharT() {
|
||||
operator CharT&() {
|
||||
if (none)
|
||||
throw value_error("Cannot convert None to a character");
|
||||
|
||||
@@ -1267,7 +1329,8 @@ public:
|
||||
if (char0_bytes == str_len) {
|
||||
// If we have a 128-255 value, we can decode it into a single char:
|
||||
if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) { // 0x110000xx 0x10xxxxxx
|
||||
return static_cast<CharT>(((v0 & 3) << 6) + (static_cast<unsigned char>(value[1]) & 0x3F));
|
||||
one_char = static_cast<CharT>(((v0 & 3) << 6) + (static_cast<unsigned char>(value[1]) & 0x3F));
|
||||
return one_char;
|
||||
}
|
||||
// Otherwise we have a single character, but it's > U+00FF
|
||||
throw value_error("Character code point not in range(0x100)");
|
||||
@@ -1278,19 +1341,20 @@ public:
|
||||
// surrogate pair with total length 2 instantly indicates a range error (but not a "your
|
||||
// string was too long" error).
|
||||
else if (StringCaster::UTF_N == 16 && str_len == 2) {
|
||||
char16_t v0 = static_cast<char16_t>(value[0]);
|
||||
if (v0 >= 0xD800 && v0 < 0xE000)
|
||||
one_char = static_cast<CharT>(value[0]);
|
||||
if (one_char >= 0xD800 && one_char < 0xE000)
|
||||
throw value_error("Character code point not in range(0x10000)");
|
||||
}
|
||||
|
||||
if (str_len != 1)
|
||||
throw value_error("Expected a character, but multi-character string found");
|
||||
|
||||
return value[0];
|
||||
one_char = value[0];
|
||||
return one_char;
|
||||
}
|
||||
|
||||
static PYBIND11_DESCR name() { return type_descr(_(PYBIND11_STRING_NAME)); }
|
||||
template <typename _T> using cast_op_type = remove_reference_t<pybind11::detail::cast_op_type<_T>>;
|
||||
static constexpr auto name = _(PYBIND11_STRING_NAME);
|
||||
template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>;
|
||||
};
|
||||
|
||||
// Base implementation for std::tuple and std::pair
|
||||
@@ -1314,9 +1378,7 @@ public:
|
||||
return cast_impl(std::forward<T>(src), policy, parent, indices{});
|
||||
}
|
||||
|
||||
static PYBIND11_DESCR name() {
|
||||
return type_descr(_("Tuple[") + detail::concat(make_caster<Ts>::name()...) + _("]"));
|
||||
}
|
||||
static constexpr auto name = _("Tuple[") + concat(make_caster<Ts>::name...) + _("]");
|
||||
|
||||
template <typename T> using cast_op_type = type;
|
||||
|
||||
@@ -1389,7 +1451,7 @@ public:
|
||||
|
||||
explicit operator type*() { return this->value; }
|
||||
explicit operator type&() { return *(this->value); }
|
||||
explicit operator holder_type*() { return &holder; }
|
||||
explicit operator holder_type*() { return std::addressof(holder); }
|
||||
|
||||
// Workaround for Intel compiler bug
|
||||
// see pybind11 issue 94
|
||||
@@ -1414,7 +1476,7 @@ protected:
|
||||
bool load_value(value_and_holder &&v_h) {
|
||||
if (v_h.holder_constructed()) {
|
||||
value = v_h.value_ptr();
|
||||
holder = v_h.holder<holder_type>();
|
||||
holder = v_h.template holder<holder_type>();
|
||||
return true;
|
||||
} else {
|
||||
throw cast_error("Unable to cast from non-held to held instance (T& to Holder<T>) "
|
||||
@@ -1459,9 +1521,9 @@ struct move_only_holder_caster {
|
||||
|
||||
static handle cast(holder_type &&src, return_value_policy, handle) {
|
||||
auto *ptr = holder_helper<holder_type>::get(src);
|
||||
return type_caster_base<type>::cast_holder(ptr, &src);
|
||||
return type_caster_base<type>::cast_holder(ptr, std::addressof(src));
|
||||
}
|
||||
static PYBIND11_DESCR name() { return type_caster_base<type>::name(); }
|
||||
static constexpr auto name = type_caster_base<type>::name;
|
||||
};
|
||||
|
||||
template <typename type, typename deleter>
|
||||
@@ -1492,10 +1554,10 @@ template <typename base, typename holder> struct is_holder_type :
|
||||
template <typename base, typename deleter> struct is_holder_type<base, std::unique_ptr<base, deleter>> :
|
||||
std::true_type {};
|
||||
|
||||
template <typename T> struct handle_type_name { static PYBIND11_DESCR name() { return _<T>(); } };
|
||||
template <> struct handle_type_name<bytes> { static PYBIND11_DESCR name() { return _(PYBIND11_BYTES_NAME); } };
|
||||
template <> struct handle_type_name<args> { static PYBIND11_DESCR name() { return _("*args"); } };
|
||||
template <> struct handle_type_name<kwargs> { static PYBIND11_DESCR name() { return _("**kwargs"); } };
|
||||
template <typename T> struct handle_type_name { static constexpr auto name = _<T>(); };
|
||||
template <> struct handle_type_name<bytes> { static constexpr auto name = _(PYBIND11_BYTES_NAME); };
|
||||
template <> struct handle_type_name<args> { static constexpr auto name = _("*args"); };
|
||||
template <> struct handle_type_name<kwargs> { static constexpr auto name = _("**kwargs"); };
|
||||
|
||||
template <typename type>
|
||||
struct pyobject_caster {
|
||||
@@ -1513,7 +1575,7 @@ struct pyobject_caster {
|
||||
static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
return src.inc_ref();
|
||||
}
|
||||
PYBIND11_TYPE_CASTER(type, handle_type_name<type>::name());
|
||||
PYBIND11_TYPE_CASTER(type, handle_type_name<type>::name);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -1553,17 +1615,23 @@ template <typename T> using move_never = none_of<move_always<T>, move_if_unrefer
|
||||
// everything else returns a reference/pointer to a local variable.
|
||||
template <typename type> using cast_is_temporary_value_reference = bool_constant<
|
||||
(std::is_reference<type>::value || std::is_pointer<type>::value) &&
|
||||
!std::is_base_of<type_caster_generic, make_caster<type>>::value
|
||||
!std::is_base_of<type_caster_generic, make_caster<type>>::value &&
|
||||
!std::is_same<intrinsic_t<type>, void>::value
|
||||
>;
|
||||
|
||||
// When a value returned from a C++ function is being cast back to Python, we almost always want to
|
||||
// force `policy = move`, regardless of the return value policy the function/method was declared
|
||||
// with. Some classes (most notably Eigen::Ref and related) need to avoid this, and so can do so by
|
||||
// specializing this struct.
|
||||
// with.
|
||||
template <typename Return, typename SFINAE = void> struct return_value_policy_override {
|
||||
static return_value_policy policy(return_value_policy p) { return p; }
|
||||
};
|
||||
|
||||
template <typename Return> struct return_value_policy_override<Return,
|
||||
detail::enable_if_t<std::is_base_of<type_caster_generic, make_caster<Return>>::value, void>> {
|
||||
static return_value_policy policy(return_value_policy p) {
|
||||
return !std::is_lvalue_reference<Return>::value && !std::is_pointer<Return>::value
|
||||
? return_value_policy::move : p;
|
||||
return !std::is_lvalue_reference<Return>::value &&
|
||||
!std::is_pointer<Return>::value
|
||||
? return_value_policy::move : p;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1574,7 +1642,7 @@ template <typename T, typename SFINAE> type_caster<T, SFINAE> &load_type(type_ca
|
||||
throw cast_error("Unable to cast Python instance to C++ type (compile in debug mode for details)");
|
||||
#else
|
||||
throw cast_error("Unable to cast Python instance of type " +
|
||||
(std::string) str(handle.get_type()) + " to C++ type '" + type_id<T>() + "''");
|
||||
(std::string) str(handle.get_type()) + " to C++ type '" + type_id<T>() + "'");
|
||||
#endif
|
||||
}
|
||||
return conv;
|
||||
@@ -1683,6 +1751,9 @@ template <> inline void cast_safe<void>(object &&) {}
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
template <return_value_policy policy = return_value_policy::automatic_reference>
|
||||
tuple make_tuple() { return tuple(0); }
|
||||
|
||||
template <return_value_policy policy = return_value_policy::automatic_reference,
|
||||
typename... Args> tuple make_tuple(Args&&... args_) {
|
||||
constexpr size_t size = sizeof...(Args);
|
||||
@@ -1788,7 +1859,7 @@ struct function_record;
|
||||
|
||||
/// Internal data associated with a single function call
|
||||
struct function_call {
|
||||
function_call(function_record &f, handle p); // Implementation in attr.h
|
||||
function_call(const function_record &f, handle p); // Implementation in attr.h
|
||||
|
||||
/// The function data:
|
||||
const function_record &func;
|
||||
@@ -1799,6 +1870,10 @@ struct function_call {
|
||||
/// The `convert` value the arguments should be loaded with
|
||||
std::vector<bool> args_convert;
|
||||
|
||||
/// Extra references for the optional `py::args` and/or `py::kwargs` arguments (which, if
|
||||
/// present, are also in `args` but without a reference).
|
||||
object args_ref, kwargs_ref;
|
||||
|
||||
/// The parent, if any
|
||||
handle parent;
|
||||
|
||||
@@ -1826,7 +1901,7 @@ public:
|
||||
static constexpr bool has_kwargs = kwargs_pos < 0;
|
||||
static constexpr bool has_args = args_pos < 0;
|
||||
|
||||
static PYBIND11_DESCR arg_names() { return detail::concat(make_caster<Args>::name()...); }
|
||||
static constexpr auto arg_names = concat(type_descr(make_caster<Args>::name)...);
|
||||
|
||||
bool load_args(function_call &call) {
|
||||
return load_impl_sequence(call, indices{});
|
||||
@@ -2045,9 +2120,13 @@ object object_api<Derived>::call(Args &&...args) const {
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
#define PYBIND11_MAKE_OPAQUE(Type) \
|
||||
#define PYBIND11_MAKE_OPAQUE(...) \
|
||||
namespace pybind11 { namespace detail { \
|
||||
template<> class type_caster<Type> : public type_caster_base<Type> { }; \
|
||||
template<> class type_caster<__VA_ARGS__> : public type_caster_base<__VA_ARGS__> { }; \
|
||||
}}
|
||||
|
||||
/// Lets you pass a type containing a `,` through a macro parameter without needing a separate
|
||||
/// typedef, e.g.: `PYBIND11_OVERLOAD(PYBIND11_TYPE(ReturnType<A, B>), PYBIND11_TYPE(Parent<C, D>), f, arg)`
|
||||
#define PYBIND11_TYPE(...) __VA_ARGS__
|
||||
|
||||
NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
@@ -106,8 +106,11 @@ public:
|
||||
if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
|
||||
|
||||
if (!src) return false;
|
||||
|
||||
std::tm cal;
|
||||
microseconds msecs;
|
||||
|
||||
if (PyDateTime_Check(src.ptr())) {
|
||||
std::tm cal;
|
||||
cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr());
|
||||
cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr());
|
||||
cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr());
|
||||
@@ -115,11 +118,30 @@ public:
|
||||
cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
|
||||
cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
|
||||
cal.tm_isdst = -1;
|
||||
|
||||
value = system_clock::from_time_t(std::mktime(&cal)) + microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
|
||||
return true;
|
||||
msecs = microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
|
||||
} else if (PyDate_Check(src.ptr())) {
|
||||
cal.tm_sec = 0;
|
||||
cal.tm_min = 0;
|
||||
cal.tm_hour = 0;
|
||||
cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
|
||||
cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
|
||||
cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
|
||||
cal.tm_isdst = -1;
|
||||
msecs = microseconds(0);
|
||||
} else if (PyTime_Check(src.ptr())) {
|
||||
cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr());
|
||||
cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr());
|
||||
cal.tm_hour = PyDateTime_TIME_GET_HOUR(src.ptr());
|
||||
cal.tm_mday = 1; // This date (day, month, year) = (1, 0, 70)
|
||||
cal.tm_mon = 0; // represents 1-Jan-1970, which is the first
|
||||
cal.tm_year = 70; // earliest available date for Python's datetime
|
||||
cal.tm_isdst = -1;
|
||||
msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr()));
|
||||
}
|
||||
else return false;
|
||||
|
||||
value = system_clock::from_time_t(std::mktime(&cal)) + msecs;
|
||||
return true;
|
||||
}
|
||||
|
||||
static handle cast(const std::chrono::time_point<std::chrono::system_clock, Duration> &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
@@ -128,7 +150,7 @@ public:
|
||||
// Lazy initialise the PyDateTime import
|
||||
if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
|
||||
|
||||
std::time_t tt = system_clock::to_time_t(src);
|
||||
std::time_t tt = system_clock::to_time_t(time_point_cast<system_clock::duration>(src));
|
||||
// this function uses static memory so it's best to copy it out asap just in case
|
||||
// otherwise other code that is using localtime may break this (not just python code)
|
||||
std::tm localtime = *std::localtime(&tt);
|
||||
|
||||
@@ -25,9 +25,13 @@ template <typename T> struct format_descriptor<std::complex<T>, detail::enable_i
|
||||
static std::string format() { return std::string(value); }
|
||||
};
|
||||
|
||||
#ifndef PYBIND11_CPP17
|
||||
|
||||
template <typename T> constexpr const char format_descriptor<
|
||||
std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>>::value[3];
|
||||
|
||||
#endif
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <typename T> struct is_fmt_numeric<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
|
||||
@@ -10,10 +10,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "../attr.h"
|
||||
#include "../options.h"
|
||||
|
||||
NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
# define PYBIND11_BUILTIN_QUALNAME
|
||||
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj)
|
||||
#else
|
||||
// In pre-3.3 Python, we still set __qualname__ so that we can produce reliable function type
|
||||
// signatures; in 3.3+ this macro expands to nothing:
|
||||
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) setattr((PyObject *) obj, "__qualname__", nameobj)
|
||||
#endif
|
||||
|
||||
inline PyTypeObject *type_incref(PyTypeObject *type) {
|
||||
Py_INCREF(type);
|
||||
return type;
|
||||
@@ -48,7 +58,7 @@ inline PyTypeObject *make_static_property_type() {
|
||||
pybind11_fail("make_static_property_type(): error allocating type!");
|
||||
|
||||
heap_type->ht_name = name_obj.inc_ref().ptr();
|
||||
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
|
||||
#ifdef PYBIND11_BUILTIN_QUALNAME
|
||||
heap_type->ht_qualname = name_obj.inc_ref().ptr();
|
||||
#endif
|
||||
|
||||
@@ -63,6 +73,7 @@ inline PyTypeObject *make_static_property_type() {
|
||||
pybind11_fail("make_static_property_type(): failure in PyType_Ready()!");
|
||||
|
||||
setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
|
||||
PYBIND11_SET_OLDPY_QUALNAME(type, name_obj);
|
||||
|
||||
return type;
|
||||
}
|
||||
@@ -161,7 +172,7 @@ inline PyTypeObject* make_default_metaclass() {
|
||||
pybind11_fail("make_default_metaclass(): error allocating metaclass!");
|
||||
|
||||
heap_type->ht_name = name_obj.inc_ref().ptr();
|
||||
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
|
||||
#ifdef PYBIND11_BUILTIN_QUALNAME
|
||||
heap_type->ht_qualname = name_obj.inc_ref().ptr();
|
||||
#endif
|
||||
|
||||
@@ -179,6 +190,7 @@ inline PyTypeObject* make_default_metaclass() {
|
||||
pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!");
|
||||
|
||||
setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
|
||||
PYBIND11_SET_OLDPY_QUALNAME(type, name_obj);
|
||||
|
||||
return type;
|
||||
}
|
||||
@@ -363,7 +375,7 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
|
||||
pybind11_fail("make_object_base_type(): error allocating type!");
|
||||
|
||||
heap_type->ht_name = name_obj.inc_ref().ptr();
|
||||
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
|
||||
#ifdef PYBIND11_BUILTIN_QUALNAME
|
||||
heap_type->ht_qualname = name_obj.inc_ref().ptr();
|
||||
#endif
|
||||
|
||||
@@ -384,6 +396,7 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
|
||||
pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string());
|
||||
|
||||
setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
|
||||
PYBIND11_SET_OLDPY_QUALNAME(type, name_obj);
|
||||
|
||||
assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
|
||||
return (PyObject *) heap_type;
|
||||
@@ -456,7 +469,7 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
|
||||
if (tinfo && tinfo->get_buffer)
|
||||
break;
|
||||
}
|
||||
if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) {
|
||||
if (view == nullptr || !tinfo || !tinfo->get_buffer) {
|
||||
if (view)
|
||||
view->obj = nullptr;
|
||||
PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error");
|
||||
@@ -504,13 +517,15 @@ inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) {
|
||||
inline PyObject* make_new_python_type(const type_record &rec) {
|
||||
auto name = reinterpret_steal<object>(PYBIND11_FROM_STRING(rec.name));
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
|
||||
auto ht_qualname = name;
|
||||
if (rec.scope && hasattr(rec.scope, "__qualname__")) {
|
||||
ht_qualname = reinterpret_steal<object>(
|
||||
auto qualname = name;
|
||||
if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
qualname = reinterpret_steal<object>(
|
||||
PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr()));
|
||||
}
|
||||
#else
|
||||
qualname = str(rec.scope.attr("__qualname__").cast<std::string>() + "." + rec.name);
|
||||
#endif
|
||||
}
|
||||
|
||||
object module;
|
||||
if (rec.scope) {
|
||||
@@ -552,8 +567,8 @@ inline PyObject* make_new_python_type(const type_record &rec) {
|
||||
pybind11_fail(std::string(rec.name) + ": Unable to create type object!");
|
||||
|
||||
heap_type->ht_name = name.release().ptr();
|
||||
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
|
||||
heap_type->ht_qualname = ht_qualname.release().ptr();
|
||||
#ifdef PYBIND11_BUILTIN_QUALNAME
|
||||
heap_type->ht_qualname = qualname.inc_ref().ptr();
|
||||
#endif
|
||||
|
||||
auto type = &heap_type->ht_type;
|
||||
@@ -571,6 +586,9 @@ inline PyObject* make_new_python_type(const type_record &rec) {
|
||||
type->tp_as_number = &heap_type->as_number;
|
||||
type->tp_as_sequence = &heap_type->as_sequence;
|
||||
type->tp_as_mapping = &heap_type->as_mapping;
|
||||
#if PY_VERSION_HEX >= 0x03050000
|
||||
type->tp_as_async = &heap_type->as_async;
|
||||
#endif
|
||||
|
||||
/* Flags */
|
||||
type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
|
||||
@@ -599,6 +617,8 @@ inline PyObject* make_new_python_type(const type_record &rec) {
|
||||
if (module) // Needed by pydoc
|
||||
setattr((PyObject *) type, "__module__", module);
|
||||
|
||||
PYBIND11_SET_OLDPY_QUALNAME(type, qualname);
|
||||
|
||||
return (PyObject *) type;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,15 +27,16 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER)
|
||||
#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER)
|
||||
# if __cplusplus >= 201402L
|
||||
# define PYBIND11_CPP14
|
||||
# if __cplusplus > 201402L /* Temporary: should be updated to >= the final C++17 value once known */
|
||||
# if __cplusplus >= 201703L
|
||||
# define PYBIND11_CPP17
|
||||
# endif
|
||||
# endif
|
||||
#elif defined(_MSC_VER)
|
||||
#elif defined(_MSC_VER) && __cplusplus == 199711L
|
||||
// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented)
|
||||
// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer
|
||||
# if _MSVC_LANG >= 201402L
|
||||
# define PYBIND11_CPP14
|
||||
# if _MSVC_LANG > 201402L && _MSC_VER >= 1910
|
||||
@@ -46,8 +47,8 @@
|
||||
|
||||
// Compiler version assertions
|
||||
#if defined(__INTEL_COMPILER)
|
||||
# if __INTEL_COMPILER < 1500
|
||||
# error pybind11 requires Intel C++ compiler v15 or newer
|
||||
# if __INTEL_COMPILER < 1700
|
||||
# error pybind11 requires Intel C++ compiler v17 or newer
|
||||
# endif
|
||||
#elif defined(__clang__) && !defined(__apple_build_version__)
|
||||
# if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3)
|
||||
@@ -92,7 +93,7 @@
|
||||
#endif
|
||||
|
||||
#define PYBIND11_VERSION_MAJOR 2
|
||||
#define PYBIND11_VERSION_MINOR 2
|
||||
#define PYBIND11_VERSION_MINOR 4
|
||||
#define PYBIND11_VERSION_PATCH 1
|
||||
|
||||
/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode
|
||||
@@ -112,10 +113,6 @@
|
||||
#include <frameobject.h>
|
||||
#include <pythread.h>
|
||||
|
||||
#if defined(_WIN32) && (defined(min) || defined(max))
|
||||
# error Macro clash with min and max -- define NOMINMAX when compiling your program on Windows
|
||||
#endif
|
||||
|
||||
#if defined(isalnum)
|
||||
# undef isalnum
|
||||
# undef isalpha
|
||||
@@ -158,6 +155,8 @@
|
||||
#define PYBIND11_BYTES_SIZE PyBytes_Size
|
||||
#define PYBIND11_LONG_CHECK(o) PyLong_Check(o)
|
||||
#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o)
|
||||
#define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) o)
|
||||
#define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) o)
|
||||
#define PYBIND11_BYTES_NAME "bytes"
|
||||
#define PYBIND11_STRING_NAME "str"
|
||||
#define PYBIND11_SLICE_OBJECT PyObject
|
||||
@@ -165,7 +164,9 @@
|
||||
#define PYBIND11_STR_TYPE ::pybind11::str
|
||||
#define PYBIND11_BOOL_ATTR "__bool__"
|
||||
#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool)
|
||||
// Providing a separate declaration to make Clang's -Wmissing-prototypes happy
|
||||
#define PYBIND11_PLUGIN_IMPL(name) \
|
||||
extern "C" PYBIND11_EXPORT PyObject *PyInit_##name(); \
|
||||
extern "C" PYBIND11_EXPORT PyObject *PyInit_##name()
|
||||
|
||||
#else
|
||||
@@ -180,6 +181,8 @@
|
||||
#define PYBIND11_BYTES_SIZE PyString_Size
|
||||
#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o))
|
||||
#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o))
|
||||
#define PYBIND11_LONG_FROM_SIGNED(o) PyInt_FromSsize_t((ssize_t) o) // Returns long if needed.
|
||||
#define PYBIND11_LONG_FROM_UNSIGNED(o) PyInt_FromSize_t((size_t) o) // Returns long if needed.
|
||||
#define PYBIND11_BYTES_NAME "str"
|
||||
#define PYBIND11_STRING_NAME "unicode"
|
||||
#define PYBIND11_SLICE_OBJECT PySliceObject
|
||||
@@ -187,8 +190,10 @@
|
||||
#define PYBIND11_STR_TYPE ::pybind11::bytes
|
||||
#define PYBIND11_BOOL_ATTR "__nonzero__"
|
||||
#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero)
|
||||
// Providing a separate PyInit decl to make Clang's -Wmissing-prototypes happy
|
||||
#define PYBIND11_PLUGIN_IMPL(name) \
|
||||
static PyObject *pybind11_init_wrapper(); \
|
||||
extern "C" PYBIND11_EXPORT void init##name(); \
|
||||
extern "C" PYBIND11_EXPORT void init##name() { \
|
||||
(void)pybind11_init_wrapper(); \
|
||||
} \
|
||||
@@ -207,6 +212,31 @@ extern "C" {
|
||||
#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x)
|
||||
#define PYBIND11_CONCAT(first, second) first##second
|
||||
|
||||
#define PYBIND11_CHECK_PYTHON_VERSION \
|
||||
{ \
|
||||
const char *compiled_ver = PYBIND11_TOSTRING(PY_MAJOR_VERSION) \
|
||||
"." PYBIND11_TOSTRING(PY_MINOR_VERSION); \
|
||||
const char *runtime_ver = Py_GetVersion(); \
|
||||
size_t len = std::strlen(compiled_ver); \
|
||||
if (std::strncmp(runtime_ver, compiled_ver, len) != 0 \
|
||||
|| (runtime_ver[len] >= '0' && runtime_ver[len] <= '9')) { \
|
||||
PyErr_Format(PyExc_ImportError, \
|
||||
"Python version mismatch: module was compiled for Python %s, " \
|
||||
"but the interpreter version is incompatible: %s.", \
|
||||
compiled_ver, runtime_ver); \
|
||||
return nullptr; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
catch (pybind11::error_already_set &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
} catch (const std::exception &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
} \
|
||||
|
||||
/** \rst
|
||||
***Deprecated in favor of PYBIND11_MODULE***
|
||||
|
||||
@@ -226,27 +256,10 @@ extern "C" {
|
||||
PYBIND11_DEPRECATED("PYBIND11_PLUGIN is deprecated, use PYBIND11_MODULE") \
|
||||
static PyObject *pybind11_init(); \
|
||||
PYBIND11_PLUGIN_IMPL(name) { \
|
||||
int major, minor; \
|
||||
if (sscanf(Py_GetVersion(), "%i.%i", &major, &minor) != 2) { \
|
||||
PyErr_SetString(PyExc_ImportError, "Can't parse Python version."); \
|
||||
return nullptr; \
|
||||
} else if (major != PY_MAJOR_VERSION || minor != PY_MINOR_VERSION) { \
|
||||
PyErr_Format(PyExc_ImportError, \
|
||||
"Python version mismatch: module was compiled for " \
|
||||
"version %i.%i, while the interpreter is running " \
|
||||
"version %i.%i.", PY_MAJOR_VERSION, PY_MINOR_VERSION, \
|
||||
major, minor); \
|
||||
return nullptr; \
|
||||
} \
|
||||
PYBIND11_CHECK_PYTHON_VERSION \
|
||||
try { \
|
||||
return pybind11_init(); \
|
||||
} catch (pybind11::error_already_set &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
} catch (const std::exception &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
} \
|
||||
} PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
} \
|
||||
PyObject *pybind11_init()
|
||||
|
||||
@@ -270,29 +283,12 @@ extern "C" {
|
||||
#define PYBIND11_MODULE(name, variable) \
|
||||
static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \
|
||||
PYBIND11_PLUGIN_IMPL(name) { \
|
||||
int major, minor; \
|
||||
if (sscanf(Py_GetVersion(), "%i.%i", &major, &minor) != 2) { \
|
||||
PyErr_SetString(PyExc_ImportError, "Can't parse Python version."); \
|
||||
return nullptr; \
|
||||
} else if (major != PY_MAJOR_VERSION || minor != PY_MINOR_VERSION) { \
|
||||
PyErr_Format(PyExc_ImportError, \
|
||||
"Python version mismatch: module was compiled for " \
|
||||
"version %i.%i, while the interpreter is running " \
|
||||
"version %i.%i.", PY_MAJOR_VERSION, PY_MINOR_VERSION, \
|
||||
major, minor); \
|
||||
return nullptr; \
|
||||
} \
|
||||
PYBIND11_CHECK_PYTHON_VERSION \
|
||||
auto m = pybind11::module(PYBIND11_TOSTRING(name)); \
|
||||
try { \
|
||||
PYBIND11_CONCAT(pybind11_init_, name)(m); \
|
||||
return m.ptr(); \
|
||||
} catch (pybind11::error_already_set &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
} catch (const std::exception &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
} \
|
||||
} PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
} \
|
||||
void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable)
|
||||
|
||||
@@ -377,18 +373,20 @@ constexpr size_t instance_simple_holder_in_ptrs() {
|
||||
struct type_info;
|
||||
struct value_and_holder;
|
||||
|
||||
struct nonsimple_values_and_holders {
|
||||
void **values_and_holders;
|
||||
uint8_t *status;
|
||||
};
|
||||
|
||||
/// The 'instance' type which needs to be standard layout (need to be able to use 'offsetof')
|
||||
struct instance {
|
||||
PyObject_HEAD
|
||||
/// Storage for pointers and holder; see simple_layout, below, for a description
|
||||
union {
|
||||
void *simple_value_holder[1 + instance_simple_holder_in_ptrs()];
|
||||
struct {
|
||||
void **values_and_holders;
|
||||
uint8_t *status;
|
||||
} nonsimple;
|
||||
nonsimple_values_and_holders nonsimple;
|
||||
};
|
||||
/// Weak references (needed for keep alive):
|
||||
/// Weak references
|
||||
PyObject *weakrefs;
|
||||
/// If true, the pointer is owned which means we're free to manage it with a holder.
|
||||
bool owned : 1;
|
||||
@@ -405,10 +403,10 @@ struct instance {
|
||||
* (which is typically the size of two pointers), or when multiple inheritance is used on the
|
||||
* python side. Non-simple layout allocates the required amount of memory to have multiple
|
||||
* bound C++ classes as parents. Under this layout, `nonsimple.values_and_holders` is set to a
|
||||
* pointer to allocated space of the required space to hold a a sequence of value pointers and
|
||||
* pointer to allocated space of the required space to hold a sequence of value pointers and
|
||||
* holders followed `status`, a set of bit flags (1 byte each), i.e.
|
||||
* [val1*][holder1][val2*][holder2]...[bb...] where each [block] is rounded up to a multiple of
|
||||
* `sizeof(void *)`. `nonsimple.holder_constructed` is, for convenience, a pointer to the
|
||||
* `sizeof(void *)`. `nonsimple.status` is, for convenience, a pointer to the
|
||||
* beginning of the [bb...] block (but not independently allocated).
|
||||
*
|
||||
* Status bits indicate whether the associated holder is constructed (&
|
||||
@@ -471,7 +469,7 @@ template <size_t... IPrev, size_t I, bool B, bool... Bs> struct select_indices_i
|
||||
: select_indices_impl<conditional_t<B, index_sequence<IPrev..., I>, index_sequence<IPrev...>>, I + 1, Bs...> {};
|
||||
template <bool... Bs> using select_indices = typename select_indices_impl<index_sequence<>, 0, Bs...>::type;
|
||||
|
||||
/// Backports of std::bool_constant and std::negation to accomodate older compilers
|
||||
/// Backports of std::bool_constant and std::negation to accommodate older compilers
|
||||
template <bool B> using bool_constant = std::integral_constant<bool, B>;
|
||||
template <typename T> struct negation : bool_constant<!T::value> { };
|
||||
|
||||
@@ -479,7 +477,7 @@ template <typename...> struct void_t_impl { using type = void; };
|
||||
template <typename... Ts> using void_t = typename void_t_impl<Ts...>::type;
|
||||
|
||||
/// Compile-time all/any/none of that check the boolean value of all template types
|
||||
#ifdef __cpp_fold_expressions
|
||||
#if defined(__cpp_fold_expressions) && !(defined(_MSC_VER) && (_MSC_VER < 1916))
|
||||
template <class... Ts> using all_of = bool_constant<(Ts::value && ...)>;
|
||||
template <class... Ts> using any_of = bool_constant<(Ts::value || ...)>;
|
||||
#elif !defined(_MSC_VER)
|
||||
@@ -581,6 +579,11 @@ template <typename T, typename... Us> using deferred_t = typename deferred_type<
|
||||
template <typename Base, typename Derived> using is_strict_base_of = bool_constant<
|
||||
std::is_base_of<Base, Derived>::value && !std::is_same<Base, Derived>::value>;
|
||||
|
||||
/// Like is_base_of, but also requires that the base type is accessible (i.e. that a Derived pointer
|
||||
/// can be converted to a Base pointer)
|
||||
template <typename Base, typename Derived> using is_accessible_base_of = bool_constant<
|
||||
std::is_base_of<Base, Derived>::value && std::is_convertible<Derived *, Base *>::value>;
|
||||
|
||||
template <template<typename...> class Base>
|
||||
struct is_template_base_of_impl {
|
||||
template <typename... Us> static std::true_type check(Base<Us...> *);
|
||||
@@ -670,6 +673,7 @@ PYBIND11_RUNTIME_EXCEPTION(index_error, PyExc_IndexError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(buffer_error, PyExc_BufferError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error
|
||||
PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally
|
||||
|
||||
@@ -699,9 +703,13 @@ template <typename T> struct format_descriptor<T, detail::enable_if_t<std::is_ar
|
||||
static std::string format() { return std::string(1, c); }
|
||||
};
|
||||
|
||||
#if !defined(PYBIND11_CPP17)
|
||||
|
||||
template <typename T> constexpr const char format_descriptor<
|
||||
T, detail::enable_if_t<std::is_arithmetic<T>::value>>::value[2];
|
||||
|
||||
#endif
|
||||
|
||||
/// RAII wrapper that temporarily clears any Python error state
|
||||
struct error_scope {
|
||||
PyObject *type, *value, *trace;
|
||||
@@ -712,10 +720,6 @@ struct error_scope {
|
||||
/// Dummy destructor wrapper that can be used to expose classes with a private destructor
|
||||
struct nodelete { template <typename T> void operator()(T*) { } };
|
||||
|
||||
// overload_cast requires variable templates: C++14
|
||||
#if defined(PYBIND11_CPP14)
|
||||
#define PYBIND11_OVERLOAD_CAST 1
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
template <typename... Args>
|
||||
struct overload_cast_impl {
|
||||
@@ -735,19 +739,23 @@ struct overload_cast_impl {
|
||||
};
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
// overload_cast requires variable templates: C++14
|
||||
#if defined(PYBIND11_CPP14)
|
||||
#define PYBIND11_OVERLOAD_CAST 1
|
||||
/// Syntax sugar for resolving overloaded function pointers:
|
||||
/// - regular: static_cast<Return (Class::*)(Arg0, Arg1, Arg2)>(&Class::func)
|
||||
/// - sweet: overload_cast<Arg0, Arg1, Arg2>(&Class::func)
|
||||
template <typename... Args>
|
||||
static constexpr detail::overload_cast_impl<Args...> overload_cast = {};
|
||||
// MSVC 2015 only accepts this particular initialization syntax for this variable template.
|
||||
#endif
|
||||
|
||||
/// Const member function selector for overload_cast
|
||||
/// - regular: static_cast<Return (Class::*)(Arg) const>(&Class::func)
|
||||
/// - sweet: overload_cast<Arg>(&Class::func, const_)
|
||||
static constexpr auto const_ = std::true_type{};
|
||||
|
||||
#else // no overload_cast: providing something that static_assert-fails:
|
||||
#if !defined(PYBIND11_CPP14) // no overload_cast: providing something that static_assert-fails:
|
||||
template <typename... Args> struct overload_cast {
|
||||
static_assert(detail::deferred_t<std::false_type, Args...>::value,
|
||||
"pybind11::overload_cast<...> requires compiling in C++14 mode");
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
pybind11/detail/descr.h: Helper type for concatenating type signatures
|
||||
either at runtime (C++11) or compile time (C++14)
|
||||
pybind11/detail/descr.h: Helper type for concatenating type signatures at compile time
|
||||
|
||||
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
||||
|
||||
@@ -15,171 +14,87 @@
|
||||
NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
/* Concatenate type signatures at compile time using C++14 */
|
||||
#if defined(PYBIND11_CPP14) && !defined(_MSC_VER)
|
||||
#define PYBIND11_CONSTEXPR_DESCR
|
||||
#if !defined(_MSC_VER)
|
||||
# define PYBIND11_DESCR_CONSTEXPR static constexpr
|
||||
#else
|
||||
# define PYBIND11_DESCR_CONSTEXPR const
|
||||
#endif
|
||||
|
||||
template <size_t Size1, size_t Size2> class descr {
|
||||
template <size_t Size1_, size_t Size2_> friend class descr;
|
||||
public:
|
||||
constexpr descr(char const (&text) [Size1+1], const std::type_info * const (&types)[Size2+1])
|
||||
: descr(text, types,
|
||||
make_index_sequence<Size1>(),
|
||||
make_index_sequence<Size2>()) { }
|
||||
/* Concatenate type signatures at compile time */
|
||||
template <size_t N, typename... Ts>
|
||||
struct descr {
|
||||
char text[N + 1];
|
||||
|
||||
constexpr const char *text() const { return m_text; }
|
||||
constexpr const std::type_info * const * types() const { return m_types; }
|
||||
constexpr descr() : text{'\0'} { }
|
||||
constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence<N>()) { }
|
||||
|
||||
template <size_t OtherSize1, size_t OtherSize2>
|
||||
constexpr descr<Size1 + OtherSize1, Size2 + OtherSize2> operator+(const descr<OtherSize1, OtherSize2> &other) const {
|
||||
return concat(other,
|
||||
make_index_sequence<Size1>(),
|
||||
make_index_sequence<Size2>(),
|
||||
make_index_sequence<OtherSize1>(),
|
||||
make_index_sequence<OtherSize2>());
|
||||
template <size_t... Is>
|
||||
constexpr descr(char const (&s)[N+1], index_sequence<Is...>) : text{s[Is]..., '\0'} { }
|
||||
|
||||
template <typename... Chars>
|
||||
constexpr descr(char c, Chars... cs) : text{c, static_cast<char>(cs)..., '\0'} { }
|
||||
|
||||
static constexpr std::array<const std::type_info *, sizeof...(Ts) + 1> types() {
|
||||
return {{&typeid(Ts)..., nullptr}};
|
||||
}
|
||||
|
||||
protected:
|
||||
template <size_t... Indices1, size_t... Indices2>
|
||||
constexpr descr(
|
||||
char const (&text) [Size1+1],
|
||||
const std::type_info * const (&types) [Size2+1],
|
||||
index_sequence<Indices1...>, index_sequence<Indices2...>)
|
||||
: m_text{text[Indices1]..., '\0'},
|
||||
m_types{types[Indices2]..., nullptr } {}
|
||||
|
||||
template <size_t OtherSize1, size_t OtherSize2, size_t... Indices1,
|
||||
size_t... Indices2, size_t... OtherIndices1, size_t... OtherIndices2>
|
||||
constexpr descr<Size1 + OtherSize1, Size2 + OtherSize2>
|
||||
concat(const descr<OtherSize1, OtherSize2> &other,
|
||||
index_sequence<Indices1...>, index_sequence<Indices2...>,
|
||||
index_sequence<OtherIndices1...>, index_sequence<OtherIndices2...>) const {
|
||||
return descr<Size1 + OtherSize1, Size2 + OtherSize2>(
|
||||
{ m_text[Indices1]..., other.m_text[OtherIndices1]..., '\0' },
|
||||
{ m_types[Indices2]..., other.m_types[OtherIndices2]..., nullptr }
|
||||
);
|
||||
}
|
||||
|
||||
protected:
|
||||
char m_text[Size1 + 1];
|
||||
const std::type_info * m_types[Size2 + 1];
|
||||
};
|
||||
|
||||
template <size_t Size> constexpr descr<Size - 1, 0> _(char const(&text)[Size]) {
|
||||
return descr<Size - 1, 0>(text, { nullptr });
|
||||
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2, size_t... Is1, size_t... Is2>
|
||||
constexpr descr<N1 + N2, Ts1..., Ts2...> plus_impl(const descr<N1, Ts1...> &a, const descr<N2, Ts2...> &b,
|
||||
index_sequence<Is1...>, index_sequence<Is2...>) {
|
||||
return {a.text[Is1]..., b.text[Is2]...};
|
||||
}
|
||||
|
||||
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2>
|
||||
constexpr descr<N1 + N2, Ts1..., Ts2...> operator+(const descr<N1, Ts1...> &a, const descr<N2, Ts2...> &b) {
|
||||
return plus_impl(a, b, make_index_sequence<N1>(), make_index_sequence<N2>());
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
constexpr descr<N - 1> _(char const(&text)[N]) { return descr<N - 1>(text); }
|
||||
constexpr descr<0> _(char const(&)[1]) { return {}; }
|
||||
|
||||
template <size_t Rem, size_t... Digits> struct int_to_str : int_to_str<Rem/10, Rem%10, Digits...> { };
|
||||
template <size_t...Digits> struct int_to_str<0, Digits...> {
|
||||
static constexpr auto digits = descr<sizeof...(Digits), 0>({ ('0' + Digits)..., '\0' }, { nullptr });
|
||||
static constexpr auto digits = descr<sizeof...(Digits)>(('0' + Digits)...);
|
||||
};
|
||||
|
||||
// Ternary description (like std::conditional)
|
||||
template <bool B, size_t Size1, size_t Size2>
|
||||
constexpr enable_if_t<B, descr<Size1 - 1, 0>> _(char const(&text1)[Size1], char const(&)[Size2]) {
|
||||
template <bool B, size_t N1, size_t N2>
|
||||
constexpr enable_if_t<B, descr<N1 - 1>> _(char const(&text1)[N1], char const(&)[N2]) {
|
||||
return _(text1);
|
||||
}
|
||||
template <bool B, size_t Size1, size_t Size2>
|
||||
constexpr enable_if_t<!B, descr<Size2 - 1, 0>> _(char const(&)[Size1], char const(&text2)[Size2]) {
|
||||
template <bool B, size_t N1, size_t N2>
|
||||
constexpr enable_if_t<!B, descr<N2 - 1>> _(char const(&)[N1], char const(&text2)[N2]) {
|
||||
return _(text2);
|
||||
}
|
||||
template <bool B, size_t SizeA1, size_t SizeA2, size_t SizeB1, size_t SizeB2>
|
||||
constexpr enable_if_t<B, descr<SizeA1, SizeA2>> _(descr<SizeA1, SizeA2> d, descr<SizeB1, SizeB2>) { return d; }
|
||||
template <bool B, size_t SizeA1, size_t SizeA2, size_t SizeB1, size_t SizeB2>
|
||||
constexpr enable_if_t<!B, descr<SizeB1, SizeB2>> _(descr<SizeA1, SizeA2>, descr<SizeB1, SizeB2> d) { return d; }
|
||||
|
||||
template <bool B, typename T1, typename T2>
|
||||
constexpr enable_if_t<B, T1> _(const T1 &d, const T2 &) { return d; }
|
||||
template <bool B, typename T1, typename T2>
|
||||
constexpr enable_if_t<!B, T2> _(const T1 &, const T2 &d) { return d; }
|
||||
|
||||
template <size_t Size> auto constexpr _() -> decltype(int_to_str<Size / 10, Size % 10>::digits) {
|
||||
return int_to_str<Size / 10, Size % 10>::digits;
|
||||
}
|
||||
|
||||
template <typename Type> constexpr descr<1, 1> _() {
|
||||
return descr<1, 1>({ '%', '\0' }, { &typeid(Type), nullptr });
|
||||
template <typename Type> constexpr descr<1, Type> _() { return {'%'}; }
|
||||
|
||||
constexpr descr<0> concat() { return {}; }
|
||||
|
||||
template <size_t N, typename... Ts>
|
||||
constexpr descr<N, Ts...> concat(const descr<N, Ts...> &descr) { return descr; }
|
||||
|
||||
template <size_t N, typename... Ts, typename... Args>
|
||||
constexpr auto concat(const descr<N, Ts...> &d, const Args &...args)
|
||||
-> decltype(std::declval<descr<N + 2, Ts...>>() + concat(args...)) {
|
||||
return d + _(", ") + concat(args...);
|
||||
}
|
||||
|
||||
inline constexpr descr<0, 0> concat() { return _(""); }
|
||||
template <size_t Size1, size_t Size2, typename... Args> auto constexpr concat(descr<Size1, Size2> descr) { return descr; }
|
||||
template <size_t Size1, size_t Size2, typename... Args> auto constexpr concat(descr<Size1, Size2> descr, Args&&... args) { return descr + _(", ") + concat(args...); }
|
||||
template <size_t Size1, size_t Size2> auto constexpr type_descr(descr<Size1, Size2> descr) { return _("{") + descr + _("}"); }
|
||||
|
||||
#define PYBIND11_DESCR constexpr auto
|
||||
|
||||
#else /* Simpler C++11 implementation based on run-time memory allocation and copying */
|
||||
|
||||
class descr {
|
||||
public:
|
||||
PYBIND11_NOINLINE descr(const char *text, const std::type_info * const * types) {
|
||||
size_t nChars = len(text), nTypes = len(types);
|
||||
m_text = new char[nChars];
|
||||
m_types = new const std::type_info *[nTypes];
|
||||
memcpy(m_text, text, nChars * sizeof(char));
|
||||
memcpy(m_types, types, nTypes * sizeof(const std::type_info *));
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE descr operator+(descr &&d2) && {
|
||||
descr r;
|
||||
|
||||
size_t nChars1 = len(m_text), nTypes1 = len(m_types);
|
||||
size_t nChars2 = len(d2.m_text), nTypes2 = len(d2.m_types);
|
||||
|
||||
r.m_text = new char[nChars1 + nChars2 - 1];
|
||||
r.m_types = new const std::type_info *[nTypes1 + nTypes2 - 1];
|
||||
memcpy(r.m_text, m_text, (nChars1-1) * sizeof(char));
|
||||
memcpy(r.m_text + nChars1 - 1, d2.m_text, nChars2 * sizeof(char));
|
||||
memcpy(r.m_types, m_types, (nTypes1-1) * sizeof(std::type_info *));
|
||||
memcpy(r.m_types + nTypes1 - 1, d2.m_types, nTypes2 * sizeof(std::type_info *));
|
||||
|
||||
delete[] m_text; delete[] m_types;
|
||||
delete[] d2.m_text; delete[] d2.m_types;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
char *text() { return m_text; }
|
||||
const std::type_info * * types() { return m_types; }
|
||||
|
||||
protected:
|
||||
PYBIND11_NOINLINE descr() { }
|
||||
|
||||
template <typename T> static size_t len(const T *ptr) { // return length including null termination
|
||||
const T *it = ptr;
|
||||
while (*it++ != (T) 0)
|
||||
;
|
||||
return static_cast<size_t>(it - ptr);
|
||||
}
|
||||
|
||||
const std::type_info **m_types = nullptr;
|
||||
char *m_text = nullptr;
|
||||
};
|
||||
|
||||
/* The 'PYBIND11_NOINLINE inline' combinations below are intentional to get the desired linkage while producing as little object code as possible */
|
||||
|
||||
PYBIND11_NOINLINE inline descr _(const char *text) {
|
||||
const std::type_info *types[1] = { nullptr };
|
||||
return descr(text, types);
|
||||
template <size_t N, typename... Ts>
|
||||
constexpr descr<N + 2, Ts...> type_descr(const descr<N, Ts...> &descr) {
|
||||
return _("{") + descr + _("}");
|
||||
}
|
||||
|
||||
template <bool B> PYBIND11_NOINLINE enable_if_t<B, descr> _(const char *text1, const char *) { return _(text1); }
|
||||
template <bool B> PYBIND11_NOINLINE enable_if_t<!B, descr> _(char const *, const char *text2) { return _(text2); }
|
||||
template <bool B> PYBIND11_NOINLINE enable_if_t<B, descr> _(descr d, descr) { return d; }
|
||||
template <bool B> PYBIND11_NOINLINE enable_if_t<!B, descr> _(descr, descr d) { return d; }
|
||||
|
||||
template <typename Type> PYBIND11_NOINLINE descr _() {
|
||||
const std::type_info *types[2] = { &typeid(Type), nullptr };
|
||||
return descr("%", types);
|
||||
}
|
||||
|
||||
template <size_t Size> PYBIND11_NOINLINE descr _() {
|
||||
const std::type_info *types[1] = { nullptr };
|
||||
return descr(std::to_string(Size).c_str(), types);
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE inline descr concat() { return _(""); }
|
||||
PYBIND11_NOINLINE inline descr concat(descr &&d) { return d; }
|
||||
template <typename... Args> PYBIND11_NOINLINE descr concat(descr &&d, Args&&... args) { return std::move(d) + _(", ") + concat(std::forward<Args>(args)...); }
|
||||
PYBIND11_NOINLINE inline descr type_descr(descr&& d) { return _("{") + std::move(d) + _("}"); }
|
||||
|
||||
#define PYBIND11_DESCR ::pybind11::detail::descr
|
||||
#endif
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
|
||||
template <typename> using cast_op_type = value_and_holder &;
|
||||
operator value_and_holder &() { return *value; }
|
||||
static PYBIND11_DESCR name() { return type_descr(_<value_and_holder>()); }
|
||||
static constexpr auto name = _<value_and_holder>();
|
||||
|
||||
private:
|
||||
value_and_holder *value = nullptr;
|
||||
@@ -52,6 +52,16 @@ bool is_alias(Cpp<Class> *ptr) {
|
||||
template <typename /*Class*/>
|
||||
constexpr bool is_alias(void *) { return false; }
|
||||
|
||||
// Constructs and returns a new object; if the given arguments don't map to a constructor, we fall
|
||||
// back to brace aggregate initiailization so that for aggregate initialization can be used with
|
||||
// py::init, e.g. `py::init<int, int>` to initialize a `struct T { int a; int b; }`. For
|
||||
// non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually
|
||||
// works, but will not do the expected thing when `T` has an `initializer_list<T>` constructor).
|
||||
template <typename Class, typename... Args, detail::enable_if_t<std::is_constructible<Class, Args...>::value, int> = 0>
|
||||
inline Class *construct_or_initialize(Args &&...args) { return new Class(std::forward<Args>(args)...); }
|
||||
template <typename Class, typename... Args, detail::enable_if_t<!std::is_constructible<Class, Args...>::value, int> = 0>
|
||||
inline Class *construct_or_initialize(Args &&...args) { return new Class{std::forward<Args>(args)...}; }
|
||||
|
||||
// Attempts to constructs an alias using a `Alias(Cpp &&)` constructor. This allows types with
|
||||
// an alias to provide only a single Cpp factory function as long as the Alias can be
|
||||
// constructed from an rvalue reference of the base Cpp type. This means that Alias classes
|
||||
@@ -161,7 +171,7 @@ struct constructor {
|
||||
template <typename Class, typename... Extra, enable_if_t<!Class::has_alias, int> = 0>
|
||||
static void execute(Class &cl, const Extra&... extra) {
|
||||
cl.def("__init__", [](value_and_holder &v_h, Args... args) {
|
||||
v_h.value_ptr() = new Cpp<Class>{std::forward<Args>(args)...};
|
||||
v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...);
|
||||
}, is_new_style_constructor(), extra...);
|
||||
}
|
||||
|
||||
@@ -171,9 +181,9 @@ struct constructor {
|
||||
static void execute(Class &cl, const Extra&... extra) {
|
||||
cl.def("__init__", [](value_and_holder &v_h, Args... args) {
|
||||
if (Py_TYPE(v_h.inst) == v_h.type->type)
|
||||
v_h.value_ptr() = new Cpp<Class>{std::forward<Args>(args)...};
|
||||
v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...);
|
||||
else
|
||||
v_h.value_ptr() = new Alias<Class>{std::forward<Args>(args)...};
|
||||
v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
|
||||
}, is_new_style_constructor(), extra...);
|
||||
}
|
||||
|
||||
@@ -182,7 +192,7 @@ struct constructor {
|
||||
!std::is_constructible<Cpp<Class>, Args...>::value, int> = 0>
|
||||
static void execute(Class &cl, const Extra&... extra) {
|
||||
cl.def("__init__", [](value_and_holder &v_h, Args... args) {
|
||||
v_h.value_ptr() = new Alias<Class>{std::forward<Args>(args)...};
|
||||
v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
|
||||
}, is_new_style_constructor(), extra...);
|
||||
}
|
||||
};
|
||||
@@ -193,7 +203,7 @@ template <typename... Args> struct alias_constructor {
|
||||
enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value, int> = 0>
|
||||
static void execute(Class &cl, const Extra&... extra) {
|
||||
cl.def("__init__", [](value_and_holder &v_h, Args... args) {
|
||||
v_h.value_ptr() = new Alias<Class>{std::forward<Args>(args)...};
|
||||
v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
|
||||
}, is_new_style_constructor(), extra...);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,6 +18,33 @@ inline PyTypeObject *make_static_property_type();
|
||||
inline PyTypeObject *make_default_metaclass();
|
||||
inline PyObject *make_object_base_type(PyTypeObject *metaclass);
|
||||
|
||||
// The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new
|
||||
// Thread Specific Storage (TSS) API.
|
||||
#if PY_VERSION_HEX >= 0x03070000
|
||||
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr
|
||||
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key))
|
||||
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value))
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr)
|
||||
#else
|
||||
// Usually an int but a long on Cygwin64 with Python 3.x
|
||||
# define PYBIND11_TLS_KEY_INIT(var) decltype(PyThread_create_key()) var = 0
|
||||
# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key))
|
||||
# if PY_MAJOR_VERSION < 3
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) \
|
||||
PyThread_delete_key_value(key)
|
||||
# define PYBIND11_TLS_REPLACE_VALUE(key, value) \
|
||||
do { \
|
||||
PyThread_delete_key_value((key)); \
|
||||
PyThread_set_key_value((key), (value)); \
|
||||
} while (false)
|
||||
# else
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) \
|
||||
PyThread_set_key_value((key), nullptr)
|
||||
# define PYBIND11_TLS_REPLACE_VALUE(key, value) \
|
||||
PyThread_set_key_value((key), (value))
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly
|
||||
// other STLs, this means `typeid(A)` from one module won't equal `typeid(A)` from another module
|
||||
// even when `A` is the same, non-hidden-visibility type (e.g. from a common include). Under
|
||||
@@ -79,7 +106,7 @@ struct internals {
|
||||
PyTypeObject *default_metaclass;
|
||||
PyObject *instance_base;
|
||||
#if defined(WITH_THREAD)
|
||||
decltype(PyThread_create_key()) tstate = 0; // Usually an int but a long on Cygwin64 with Python 3.x
|
||||
PYBIND11_TLS_KEY_INIT(tstate);
|
||||
PyInterpreterState *istate = nullptr;
|
||||
#endif
|
||||
};
|
||||
@@ -89,7 +116,7 @@ struct internals {
|
||||
struct type_info {
|
||||
PyTypeObject *type;
|
||||
const std::type_info *cpptype;
|
||||
size_t type_size, holder_size_in_ptrs;
|
||||
size_t type_size, type_align, holder_size_in_ptrs;
|
||||
void *(*operator_new)(size_t);
|
||||
void (*init_instance)(instance *, const void *);
|
||||
void (*dealloc)(value_and_holder &v_h);
|
||||
@@ -111,7 +138,48 @@ struct type_info {
|
||||
};
|
||||
|
||||
/// Tracks the `internals` and `type_info` ABI version independent of the main library version
|
||||
#define PYBIND11_INTERNALS_VERSION 1
|
||||
#define PYBIND11_INTERNALS_VERSION 3
|
||||
|
||||
/// On MSVC, debug and release builds are not ABI-compatible!
|
||||
#if defined(_MSC_VER) && defined(_DEBUG)
|
||||
# define PYBIND11_BUILD_TYPE "_debug"
|
||||
#else
|
||||
# define PYBIND11_BUILD_TYPE ""
|
||||
#endif
|
||||
|
||||
/// Let's assume that different compilers are ABI-incompatible.
|
||||
#if defined(_MSC_VER)
|
||||
# define PYBIND11_COMPILER_TYPE "_msvc"
|
||||
#elif defined(__INTEL_COMPILER)
|
||||
# define PYBIND11_COMPILER_TYPE "_icc"
|
||||
#elif defined(__clang__)
|
||||
# define PYBIND11_COMPILER_TYPE "_clang"
|
||||
#elif defined(__PGI)
|
||||
# define PYBIND11_COMPILER_TYPE "_pgi"
|
||||
#elif defined(__MINGW32__)
|
||||
# define PYBIND11_COMPILER_TYPE "_mingw"
|
||||
#elif defined(__CYGWIN__)
|
||||
# define PYBIND11_COMPILER_TYPE "_gcc_cygwin"
|
||||
#elif defined(__GNUC__)
|
||||
# define PYBIND11_COMPILER_TYPE "_gcc"
|
||||
#else
|
||||
# define PYBIND11_COMPILER_TYPE "_unknown"
|
||||
#endif
|
||||
|
||||
#if defined(_LIBCPP_VERSION)
|
||||
# define PYBIND11_STDLIB "_libcpp"
|
||||
#elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
|
||||
# define PYBIND11_STDLIB "_libstdcpp"
|
||||
#else
|
||||
# define PYBIND11_STDLIB ""
|
||||
#endif
|
||||
|
||||
/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility.
|
||||
#if defined(__GXX_ABI_VERSION)
|
||||
# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION)
|
||||
#else
|
||||
# define PYBIND11_BUILD_ABI ""
|
||||
#endif
|
||||
|
||||
#if defined(WITH_THREAD)
|
||||
# define PYBIND11_INTERNALS_KIND ""
|
||||
@@ -120,28 +188,64 @@ struct type_info {
|
||||
#endif
|
||||
|
||||
#define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \
|
||||
PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND "__"
|
||||
PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
|
||||
|
||||
#define PYBIND11_MODULE_LOCAL_ID "__pybind11_module_local_v" \
|
||||
PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND "__"
|
||||
PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
|
||||
|
||||
/// Each module locally stores a pointer to the `internals` data. The data
|
||||
/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`.
|
||||
inline internals *&get_internals_ptr() {
|
||||
static internals *internals_ptr = nullptr;
|
||||
return internals_ptr;
|
||||
inline internals **&get_internals_pp() {
|
||||
static internals **internals_pp = nullptr;
|
||||
return internals_pp;
|
||||
}
|
||||
|
||||
inline void translate_exception(std::exception_ptr p) {
|
||||
try {
|
||||
if (p) std::rethrow_exception(p);
|
||||
} catch (error_already_set &e) { e.restore(); return;
|
||||
} catch (const builtin_exception &e) { e.set_error(); return;
|
||||
} catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return;
|
||||
} catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
|
||||
} catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
|
||||
} catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
|
||||
} catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return;
|
||||
} catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
|
||||
} catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return;
|
||||
} catch (...) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(__GLIBCXX__)
|
||||
inline void translate_local_exception(std::exception_ptr p) {
|
||||
try {
|
||||
if (p) std::rethrow_exception(p);
|
||||
} catch (error_already_set &e) { e.restore(); return;
|
||||
} catch (const builtin_exception &e) { e.set_error(); return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Return a reference to the current `internals` data
|
||||
PYBIND11_NOINLINE inline internals &get_internals() {
|
||||
auto *&internals_ptr = get_internals_ptr();
|
||||
if (internals_ptr)
|
||||
return *internals_ptr;
|
||||
auto **&internals_pp = get_internals_pp();
|
||||
if (internals_pp && *internals_pp)
|
||||
return **internals_pp;
|
||||
|
||||
// Ensure that the GIL is held since we will need to make Python calls.
|
||||
// Cannot use py::gil_scoped_acquire here since that constructor calls get_internals.
|
||||
struct gil_scoped_acquire_local {
|
||||
gil_scoped_acquire_local() : state (PyGILState_Ensure()) {}
|
||||
~gil_scoped_acquire_local() { PyGILState_Release(state); }
|
||||
const PyGILState_STATE state;
|
||||
} gil;
|
||||
|
||||
constexpr auto *id = PYBIND11_INTERNALS_ID;
|
||||
auto builtins = handle(PyEval_GetBuiltins());
|
||||
if (builtins.contains(id) && isinstance<capsule>(builtins[id])) {
|
||||
internals_ptr = *static_cast<internals **>(capsule(builtins[id]));
|
||||
internals_pp = static_cast<internals **>(capsule(builtins[id]));
|
||||
|
||||
// We loaded builtins through python's builtins, which means that our `error_already_set`
|
||||
// and `builtin_exception` may be different local classes than the ones set up in the
|
||||
@@ -149,50 +253,35 @@ PYBIND11_NOINLINE inline internals &get_internals() {
|
||||
//
|
||||
// libstdc++ doesn't require this (types there are identified only by name)
|
||||
#if !defined(__GLIBCXX__)
|
||||
internals_ptr->registered_exception_translators.push_front(
|
||||
[](std::exception_ptr p) -> void {
|
||||
try {
|
||||
if (p) std::rethrow_exception(p);
|
||||
} catch (error_already_set &e) { e.restore(); return;
|
||||
} catch (const builtin_exception &e) { e.set_error(); return;
|
||||
}
|
||||
}
|
||||
);
|
||||
(*internals_pp)->registered_exception_translators.push_front(&translate_local_exception);
|
||||
#endif
|
||||
} else {
|
||||
if (!internals_pp) internals_pp = new internals*();
|
||||
auto *&internals_ptr = *internals_pp;
|
||||
internals_ptr = new internals();
|
||||
#if defined(WITH_THREAD)
|
||||
PyEval_InitThreads();
|
||||
PyThreadState *tstate = PyThreadState_Get();
|
||||
internals_ptr->tstate = PyThread_create_key();
|
||||
PyThread_set_key_value(internals_ptr->tstate, tstate);
|
||||
#if PY_VERSION_HEX >= 0x03070000
|
||||
internals_ptr->tstate = PyThread_tss_alloc();
|
||||
if (!internals_ptr->tstate || PyThread_tss_create(internals_ptr->tstate))
|
||||
pybind11_fail("get_internals: could not successfully initialize the TSS key!");
|
||||
PyThread_tss_set(internals_ptr->tstate, tstate);
|
||||
#else
|
||||
internals_ptr->tstate = PyThread_create_key();
|
||||
if (internals_ptr->tstate == -1)
|
||||
pybind11_fail("get_internals: could not successfully initialize the TLS key!");
|
||||
PyThread_set_key_value(internals_ptr->tstate, tstate);
|
||||
#endif
|
||||
internals_ptr->istate = tstate->interp;
|
||||
#endif
|
||||
builtins[id] = capsule(&internals_ptr);
|
||||
internals_ptr->registered_exception_translators.push_front(
|
||||
[](std::exception_ptr p) -> void {
|
||||
try {
|
||||
if (p) std::rethrow_exception(p);
|
||||
} catch (error_already_set &e) { e.restore(); return;
|
||||
} catch (const builtin_exception &e) { e.set_error(); return;
|
||||
} catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return;
|
||||
} catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
|
||||
} catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
|
||||
} catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
|
||||
} catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return;
|
||||
} catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
|
||||
} catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return;
|
||||
} catch (...) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
);
|
||||
builtins[id] = capsule(internals_pp);
|
||||
internals_ptr->registered_exception_translators.push_front(&translate_exception);
|
||||
internals_ptr->static_property_type = make_static_property_type();
|
||||
internals_ptr->default_metaclass = make_default_metaclass();
|
||||
internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass);
|
||||
}
|
||||
return *internals_ptr;
|
||||
return **internals_pp;
|
||||
}
|
||||
|
||||
/// Works like `internals.registered_types_cpp`, but for module-local registered types:
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
|
||||
NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
NAMESPACE_BEGIN(detail)
|
||||
/// Erase all occurrences of a substring
|
||||
|
||||
@@ -17,19 +17,25 @@
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wconversion"
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
# ifdef __clang__
|
||||
// Eigen generates a bunch of implicit-copy-constructor-is-deprecated warnings with -Wdeprecated
|
||||
// under Clang, so disable that warning here:
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated"
|
||||
# endif
|
||||
# if __GNUC__ >= 7
|
||||
# pragma GCC diagnostic ignored "-Wint-in-bool-context"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/SparseCore>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
||||
# pragma warning(disable: 4996) // warning C4996: std::unary_negate is deprecated in C++17
|
||||
#endif
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/SparseCore>
|
||||
|
||||
// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit
|
||||
// move constructors that break things. We could detect this an explicitly copy, but an extra copy
|
||||
// of matrices seems highly undesirable.
|
||||
@@ -180,28 +186,26 @@ template <typename Type_> struct EigenProps {
|
||||
}
|
||||
}
|
||||
|
||||
static PYBIND11_DESCR descriptor() {
|
||||
constexpr bool show_writeable = is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value;
|
||||
constexpr bool show_order = is_eigen_dense_map<Type>::value;
|
||||
constexpr bool show_c_contiguous = show_order && requires_row_major;
|
||||
constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major;
|
||||
static constexpr bool show_writeable = is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value;
|
||||
static constexpr bool show_order = is_eigen_dense_map<Type>::value;
|
||||
static constexpr bool show_c_contiguous = show_order && requires_row_major;
|
||||
static constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major;
|
||||
|
||||
return type_descr(_("numpy.ndarray[") + npy_format_descriptor<Scalar>::name() +
|
||||
_("[") + _<fixed_rows>(_<(size_t) rows>(), _("m")) +
|
||||
_(", ") + _<fixed_cols>(_<(size_t) cols>(), _("n")) +
|
||||
_("]") +
|
||||
// For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to be
|
||||
// satisfied: writeable=True (for a mutable reference), and, depending on the map's stride
|
||||
// options, possibly f_contiguous or c_contiguous. We include them in the descriptor output
|
||||
// to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to
|
||||
// see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you
|
||||
// *gave* a numpy.ndarray of the right type and dimensions.
|
||||
_<show_writeable>(", flags.writeable", "") +
|
||||
_<show_c_contiguous>(", flags.c_contiguous", "") +
|
||||
_<show_f_contiguous>(", flags.f_contiguous", "") +
|
||||
_("]")
|
||||
);
|
||||
}
|
||||
static constexpr auto descriptor =
|
||||
_("numpy.ndarray[") + npy_format_descriptor<Scalar>::name +
|
||||
_("[") + _<fixed_rows>(_<(size_t) rows>(), _("m")) +
|
||||
_(", ") + _<fixed_cols>(_<(size_t) cols>(), _("n")) +
|
||||
_("]") +
|
||||
// For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to be
|
||||
// satisfied: writeable=True (for a mutable reference), and, depending on the map's stride
|
||||
// options, possibly f_contiguous or c_contiguous. We include them in the descriptor output
|
||||
// to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to
|
||||
// see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you
|
||||
// *gave* a numpy.ndarray of the right type and dimensions.
|
||||
_<show_writeable>(", flags.writeable", "") +
|
||||
_<show_c_contiguous>(", flags.c_contiguous", "") +
|
||||
_<show_f_contiguous>(", flags.f_contiguous", "") +
|
||||
_("]");
|
||||
};
|
||||
|
||||
// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data,
|
||||
@@ -272,6 +276,7 @@ struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
|
||||
value = Type(fits.rows, fits.cols);
|
||||
auto ref = reinterpret_steal<array>(eigen_ref_array<props>(value));
|
||||
if (dims == 1) ref = ref.squeeze();
|
||||
else if (ref.ndim() == 1) buf = buf.squeeze();
|
||||
|
||||
int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr());
|
||||
|
||||
@@ -337,7 +342,7 @@ public:
|
||||
return cast_impl(src, policy, parent);
|
||||
}
|
||||
|
||||
static PYBIND11_DESCR name() { return props::descriptor(); }
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
operator Type*() { return &value; }
|
||||
operator Type&() { return value; }
|
||||
@@ -348,14 +353,6 @@ private:
|
||||
Type value;
|
||||
};
|
||||
|
||||
// Eigen Ref/Map classes have slightly different policy requirements, meaning we don't want to force
|
||||
// `move` when a Ref/Map rvalue is returned; we treat Ref<> sort of like a pointer (we care about
|
||||
// the underlying data, not the outer shell).
|
||||
template <typename Return>
|
||||
struct return_value_policy_override<Return, enable_if_t<is_eigen_dense_map<Return>::value>> {
|
||||
static return_value_policy policy(return_value_policy p) { return p; }
|
||||
};
|
||||
|
||||
// Base class for casting reference/map/block/etc. objects back to python.
|
||||
template <typename MapType> struct eigen_map_caster {
|
||||
private:
|
||||
@@ -385,7 +382,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
static PYBIND11_DESCR name() { return props::descriptor(); }
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
// Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return
|
||||
// types but not bound arguments). We still provide them (with an explicitly delete) so that
|
||||
@@ -530,7 +527,7 @@ public:
|
||||
}
|
||||
static handle cast(const Type *src, return_value_policy policy, handle parent) { return cast(*src, policy, parent); }
|
||||
|
||||
static PYBIND11_DESCR name() { return props::descriptor(); }
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
// Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return
|
||||
// types but not bound arguments). We still provide them (with an explicitly delete) so that
|
||||
@@ -597,7 +594,7 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(Type, _<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[")
|
||||
+ npy_format_descriptor<Scalar>::name() + _("]"));
|
||||
+ npy_format_descriptor<Scalar>::name + _("]"));
|
||||
};
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
@@ -90,8 +90,14 @@ NAMESPACE_END(detail)
|
||||
Initialize the Python interpreter. No other pybind11 or CPython API functions can be
|
||||
called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The
|
||||
optional parameter can be used to skip the registration of signal handlers (see the
|
||||
Python documentation for details). Calling this function again after the interpreter
|
||||
`Python documentation`_ for details). Calling this function again after the interpreter
|
||||
has already been initialized is a fatal error.
|
||||
|
||||
If initializing the Python interpreter fails, then the program is terminated. (This
|
||||
is controlled by the CPython runtime and is an exception to pybind11's normal behavior
|
||||
of throwing exceptions on errors.)
|
||||
|
||||
.. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx
|
||||
\endrst */
|
||||
inline void initialize_interpreter(bool init_signal_handlers = true) {
|
||||
if (Py_IsInitialized())
|
||||
@@ -145,7 +151,7 @@ inline void finalize_interpreter() {
|
||||
// Get the internals pointer (without creating it if it doesn't exist). It's possible for the
|
||||
// internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()`
|
||||
// during destruction), so we get the pointer-pointer here and check it after Py_Finalize().
|
||||
detail::internals **internals_ptr_ptr = &detail::get_internals_ptr();
|
||||
detail::internals **internals_ptr_ptr = detail::get_internals_pp();
|
||||
// It could also be stashed in builtins, so look there too:
|
||||
if (builtins.contains(id) && isinstance<capsule>(builtins[id]))
|
||||
internals_ptr_ptr = capsule(builtins[id]);
|
||||
|
||||
@@ -54,12 +54,30 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
value = [func](Args... args) -> Return {
|
||||
gil_scoped_acquire acq;
|
||||
object retval(func(std::forward<Args>(args)...));
|
||||
/* Visual studio 2015 parser issue: need parentheses around this expression */
|
||||
return (retval.template cast<Return>());
|
||||
// ensure GIL is held during functor destruction
|
||||
struct func_handle {
|
||||
function f;
|
||||
func_handle(function&& f_) : f(std::move(f_)) {}
|
||||
func_handle(const func_handle&) = default;
|
||||
~func_handle() {
|
||||
gil_scoped_acquire acq;
|
||||
function kill_f(std::move(f));
|
||||
}
|
||||
};
|
||||
|
||||
// to emulate 'move initialization capture' in C++11
|
||||
struct func_wrapper {
|
||||
func_handle hfunc;
|
||||
func_wrapper(func_handle&& hf): hfunc(std::move(hf)) {}
|
||||
Return operator()(Args... args) const {
|
||||
gil_scoped_acquire acq;
|
||||
object retval(hfunc.f(std::forward<Args>(args)...));
|
||||
/* Visual studio 2015 parser issue: need parentheses around this expression */
|
||||
return (retval.template cast<Return>());
|
||||
}
|
||||
};
|
||||
|
||||
value = func_wrapper(func_handle(std::move(func)));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -75,10 +93,8 @@ public:
|
||||
return cpp_function(std::forward<Func>(f_), policy).release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(type, _("Callable[[") +
|
||||
argument_loader<Args...>::arg_names() + _("], ") +
|
||||
make_caster<retval_type>::name() +
|
||||
_("]"));
|
||||
PYBIND11_TYPE_CASTER(type, _("Callable[[") + concat(make_caster<Args>::name...) + _("], ")
|
||||
+ make_caster<retval_type>::name + _("]"));
|
||||
};
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
@@ -25,7 +25,8 @@ class pythonbuf : public std::streambuf {
|
||||
private:
|
||||
using traits_type = std::streambuf::traits_type;
|
||||
|
||||
char d_buffer[1024];
|
||||
const size_t buf_size;
|
||||
std::unique_ptr<char[]> d_buffer;
|
||||
object pywrite;
|
||||
object pyflush;
|
||||
|
||||
@@ -34,7 +35,7 @@ private:
|
||||
*pptr() = traits_type::to_char_type(c);
|
||||
pbump(1);
|
||||
}
|
||||
return sync() ? traits_type::not_eof(c) : traits_type::eof();
|
||||
return sync() == 0 ? traits_type::not_eof(c) : traits_type::eof();
|
||||
}
|
||||
|
||||
int sync() {
|
||||
@@ -42,8 +43,11 @@ private:
|
||||
// This subtraction cannot be negative, so dropping the sign
|
||||
str line(pbase(), static_cast<size_t>(pptr() - pbase()));
|
||||
|
||||
pywrite(line);
|
||||
pyflush();
|
||||
{
|
||||
gil_scoped_acquire tmp;
|
||||
pywrite(line);
|
||||
pyflush();
|
||||
}
|
||||
|
||||
setp(pbase(), epptr());
|
||||
}
|
||||
@@ -51,12 +55,17 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
pythonbuf(object pyostream)
|
||||
: pywrite(pyostream.attr("write")),
|
||||
|
||||
pythonbuf(object pyostream, size_t buffer_size = 1024)
|
||||
: buf_size(buffer_size),
|
||||
d_buffer(new char[buf_size]),
|
||||
pywrite(pyostream.attr("write")),
|
||||
pyflush(pyostream.attr("flush")) {
|
||||
setp(d_buffer, d_buffer + sizeof(d_buffer) - 1);
|
||||
setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
|
||||
}
|
||||
|
||||
pythonbuf(pythonbuf&&) = default;
|
||||
|
||||
/// Sync before destroy
|
||||
~pythonbuf() {
|
||||
sync();
|
||||
@@ -194,7 +203,7 @@ inline class_<detail::OstreamRedirect> add_ostream_redirect(module m, std::strin
|
||||
return class_<detail::OstreamRedirect>(m, name.c_str(), module_local())
|
||||
.def(init<bool,bool>(), arg("stdout")=true, arg("stderr")=true)
|
||||
.def("__enter__", &detail::OstreamRedirect::enter)
|
||||
.def("__exit__", [](detail::OstreamRedirect &self, args) { self.exit(); });
|
||||
.def("__exit__", [](detail::OstreamRedirect &self_, args) { self_.exit(); });
|
||||
}
|
||||
|
||||
NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
@@ -14,13 +14,14 @@
|
||||
#include <numeric>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <initializer_list>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <typeindex>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
@@ -108,6 +109,18 @@ inline numpy_internals& get_numpy_internals() {
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
template <typename T> struct same_size {
|
||||
template <typename U> using as = bool_constant<sizeof(T) == sizeof(U)>;
|
||||
};
|
||||
|
||||
// Lookup a type according to its size, and return a value corresponding to the NumPy typenum.
|
||||
template <typename Concrete, typename... Check, typename... Int>
|
||||
constexpr int platform_lookup(Int... codes) {
|
||||
using code_index = std::integral_constant<int, constexpr_first<same_size<Concrete>::template as, Check...>()>;
|
||||
static_assert(code_index::value != sizeof...(Check), "Unable to match type on this platform");
|
||||
return std::get<code_index::value>(std::make_tuple(codes...));
|
||||
}
|
||||
|
||||
struct npy_api {
|
||||
enum constants {
|
||||
NPY_ARRAY_C_CONTIGUOUS_ = 0x0001,
|
||||
@@ -126,7 +139,23 @@ struct npy_api {
|
||||
NPY_FLOAT_, NPY_DOUBLE_, NPY_LONGDOUBLE_,
|
||||
NPY_CFLOAT_, NPY_CDOUBLE_, NPY_CLONGDOUBLE_,
|
||||
NPY_OBJECT_ = 17,
|
||||
NPY_STRING_, NPY_UNICODE_, NPY_VOID_
|
||||
NPY_STRING_, NPY_UNICODE_, NPY_VOID_,
|
||||
// Platform-dependent normalization
|
||||
NPY_INT8_ = NPY_BYTE_,
|
||||
NPY_UINT8_ = NPY_UBYTE_,
|
||||
NPY_INT16_ = NPY_SHORT_,
|
||||
NPY_UINT16_ = NPY_USHORT_,
|
||||
// `npy_common.h` defines the integer aliases. In order, it checks:
|
||||
// NPY_BITSOF_LONG, NPY_BITSOF_LONGLONG, NPY_BITSOF_INT, NPY_BITSOF_SHORT, NPY_BITSOF_CHAR
|
||||
// and assigns the alias to the first matching size, so we should check in this order.
|
||||
NPY_INT32_ = platform_lookup<std::int32_t, long, int, short>(
|
||||
NPY_LONG_, NPY_INT_, NPY_SHORT_),
|
||||
NPY_UINT32_ = platform_lookup<std::uint32_t, unsigned long, unsigned int, unsigned short>(
|
||||
NPY_ULONG_, NPY_UINT_, NPY_USHORT_),
|
||||
NPY_INT64_ = platform_lookup<std::int64_t, long, long long, int>(
|
||||
NPY_LONG_, NPY_LONGLONG_, NPY_INT_),
|
||||
NPY_UINT64_ = platform_lookup<std::uint64_t, unsigned long, unsigned long long, unsigned int>(
|
||||
NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_),
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@@ -250,7 +279,7 @@ template <typename T> struct array_info_scalar {
|
||||
typedef T type;
|
||||
static constexpr bool is_array = false;
|
||||
static constexpr bool is_empty = false;
|
||||
static PYBIND11_DESCR extents() { return _(""); }
|
||||
static constexpr auto extents = _("");
|
||||
static void append_extents(list& /* shape */) { }
|
||||
};
|
||||
// Computes underlying type and a comma-separated list of extents for array
|
||||
@@ -269,15 +298,9 @@ template <typename T, size_t N> struct array_info<std::array<T, N>> {
|
||||
array_info<T>::append_extents(shape);
|
||||
}
|
||||
|
||||
template<typename T2 = T, enable_if_t<!array_info<T2>::is_array, int> = 0>
|
||||
static PYBIND11_DESCR extents() {
|
||||
return _<N>();
|
||||
}
|
||||
|
||||
template<typename T2 = T, enable_if_t<array_info<T2>::is_array, int> = 0>
|
||||
static PYBIND11_DESCR extents() {
|
||||
return concat(_<N>(), array_info<T>::extents());
|
||||
}
|
||||
static constexpr auto extents = _<array_info<T>::is_array>(
|
||||
concat(_<N>(), array_info<T>::extents), _<N>()
|
||||
);
|
||||
};
|
||||
// For numpy we have special handling for arrays of characters, so we don't include
|
||||
// the size in the array extents.
|
||||
@@ -446,7 +469,7 @@ public:
|
||||
/// This is essentially the same as calling numpy.dtype(args) in Python.
|
||||
static dtype from_args(object args) {
|
||||
PyObject *ptr = nullptr;
|
||||
if (!detail::npy_api::get().PyArray_DescrConverter_(args.release().ptr(), &ptr) || !ptr)
|
||||
if (!detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) || !ptr)
|
||||
throw error_already_set();
|
||||
return reinterpret_steal<dtype>(ptr);
|
||||
}
|
||||
@@ -764,8 +787,9 @@ protected:
|
||||
static std::vector<ssize_t> c_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) {
|
||||
auto ndim = shape.size();
|
||||
std::vector<ssize_t> strides(ndim, itemsize);
|
||||
for (size_t i = ndim - 1; i > 0; --i)
|
||||
strides[i - 1] = strides[i] * shape[i];
|
||||
if (ndim > 0)
|
||||
for (size_t i = ndim - 1; i > 0; --i)
|
||||
strides[i - 1] = strides[i] * shape[i];
|
||||
return strides;
|
||||
}
|
||||
|
||||
@@ -860,14 +884,14 @@ public:
|
||||
|
||||
// Reference to element at a given index
|
||||
template<typename... Ix> const T& at(Ix... index) const {
|
||||
if (sizeof...(index) != ndim())
|
||||
if ((ssize_t) sizeof...(index) != ndim())
|
||||
fail_dim_check(sizeof...(index), "index dimension mismatch");
|
||||
return *(static_cast<const T*>(array::data()) + byte_offset(ssize_t(index)...) / itemsize());
|
||||
}
|
||||
|
||||
// Mutable reference to element at a given index
|
||||
template<typename... Ix> T& mutable_at(Ix... index) {
|
||||
if (sizeof...(index) != ndim())
|
||||
if ((ssize_t) sizeof...(index) != ndim())
|
||||
fail_dim_check(sizeof...(index), "index dimension mismatch");
|
||||
return *(static_cast<T*>(array::mutable_data()) + byte_offset(ssize_t(index)...) / itemsize());
|
||||
}
|
||||
@@ -946,9 +970,9 @@ struct format_descriptor<T, detail::enable_if_t<std::is_enum<T>::value>> {
|
||||
template <typename T>
|
||||
struct format_descriptor<T, detail::enable_if_t<detail::array_info<T>::is_array>> {
|
||||
static std::string format() {
|
||||
using detail::_;
|
||||
PYBIND11_DESCR extents = _("(") + detail::array_info<T>::extents() + _(")");
|
||||
return extents.text() + format_descriptor<detail::remove_all_extents_t<T>>::format();
|
||||
using namespace detail;
|
||||
static constexpr auto extents = _("(") + array_info<T>::extents + _(")");
|
||||
return extents.text + format_descriptor<remove_all_extents_t<T>>::format();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -967,7 +991,7 @@ struct pyobject_caster<array_t<T, ExtraFlags>> {
|
||||
static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
return src.inc_ref();
|
||||
}
|
||||
PYBIND11_TYPE_CASTER(type, handle_type_name<type>::name());
|
||||
PYBIND11_TYPE_CASTER(type, handle_type_name<type>::name);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -977,13 +1001,40 @@ struct compare_buffer_info<T, detail::enable_if_t<detail::is_pod_struct<T>::valu
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct npy_format_descriptor<T, enable_if_t<satisfies_any_of<T, std::is_arithmetic, is_complex>::value>> {
|
||||
template <typename T, typename = void>
|
||||
struct npy_format_descriptor_name;
|
||||
|
||||
template <typename T>
|
||||
struct npy_format_descriptor_name<T, enable_if_t<std::is_integral<T>::value>> {
|
||||
static constexpr auto name = _<std::is_same<T, bool>::value>(
|
||||
_("bool"), _<std::is_signed<T>::value>("int", "uint") + _<sizeof(T)*8>()
|
||||
);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct npy_format_descriptor_name<T, enable_if_t<std::is_floating_point<T>::value>> {
|
||||
static constexpr auto name = _<std::is_same<T, float>::value || std::is_same<T, double>::value>(
|
||||
_("float") + _<sizeof(T)*8>(), _("longdouble")
|
||||
);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct npy_format_descriptor_name<T, enable_if_t<is_complex<T>::value>> {
|
||||
static constexpr auto name = _<std::is_same<typename T::value_type, float>::value
|
||||
|| std::is_same<typename T::value_type, double>::value>(
|
||||
_("complex") + _<sizeof(typename T::value_type)*16>(), _("longcomplex")
|
||||
);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct npy_format_descriptor<T, enable_if_t<satisfies_any_of<T, std::is_arithmetic, is_complex>::value>>
|
||||
: npy_format_descriptor_name<T> {
|
||||
private:
|
||||
// NB: the order here must match the one in common.h
|
||||
constexpr static const int values[15] = {
|
||||
npy_api::NPY_BOOL_,
|
||||
npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_SHORT_, npy_api::NPY_USHORT_,
|
||||
npy_api::NPY_INT_, npy_api::NPY_UINT_, npy_api::NPY_LONGLONG_, npy_api::NPY_ULONGLONG_,
|
||||
npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_INT16_, npy_api::NPY_UINT16_,
|
||||
npy_api::NPY_INT32_, npy_api::NPY_UINT32_, npy_api::NPY_INT64_, npy_api::NPY_UINT64_,
|
||||
npy_api::NPY_FLOAT_, npy_api::NPY_DOUBLE_, npy_api::NPY_LONGDOUBLE_,
|
||||
npy_api::NPY_CFLOAT_, npy_api::NPY_CDOUBLE_, npy_api::NPY_CLONGDOUBLE_
|
||||
};
|
||||
@@ -993,28 +1044,13 @@ public:
|
||||
|
||||
static pybind11::dtype dtype() {
|
||||
if (auto ptr = npy_api::get().PyArray_DescrFromType_(value))
|
||||
return reinterpret_borrow<pybind11::dtype>(ptr);
|
||||
return reinterpret_steal<pybind11::dtype>(ptr);
|
||||
pybind11_fail("Unsupported buffer format!");
|
||||
}
|
||||
template <typename T2 = T, enable_if_t<std::is_integral<T2>::value, int> = 0>
|
||||
static PYBIND11_DESCR name() {
|
||||
return _<std::is_same<T, bool>::value>(_("bool"),
|
||||
_<std::is_signed<T>::value>("int", "uint") + _<sizeof(T)*8>());
|
||||
}
|
||||
template <typename T2 = T, enable_if_t<std::is_floating_point<T2>::value, int> = 0>
|
||||
static PYBIND11_DESCR name() {
|
||||
return _<std::is_same<T, float>::value || std::is_same<T, double>::value>(
|
||||
_("float") + _<sizeof(T)*8>(), _("longdouble"));
|
||||
}
|
||||
template <typename T2 = T, enable_if_t<is_complex<T2>::value, int> = 0>
|
||||
static PYBIND11_DESCR name() {
|
||||
return _<std::is_same<typename T2::value_type, float>::value || std::is_same<typename T2::value_type, double>::value>(
|
||||
_("complex") + _<sizeof(typename T2::value_type)*16>(), _("longcomplex"));
|
||||
}
|
||||
};
|
||||
|
||||
#define PYBIND11_DECL_CHAR_FMT \
|
||||
static PYBIND11_DESCR name() { return _("S") + _<N>(); } \
|
||||
static constexpr auto name = _("S") + _<N>(); \
|
||||
static pybind11::dtype dtype() { return pybind11::dtype(std::string("S") + std::to_string(N)); }
|
||||
template <size_t N> struct npy_format_descriptor<char[N]> { PYBIND11_DECL_CHAR_FMT };
|
||||
template <size_t N> struct npy_format_descriptor<std::array<char, N>> { PYBIND11_DECL_CHAR_FMT };
|
||||
@@ -1026,7 +1062,7 @@ private:
|
||||
public:
|
||||
static_assert(!array_info<T>::is_empty, "Zero-sized arrays are not supported");
|
||||
|
||||
static PYBIND11_DESCR name() { return _("(") + array_info<T>::extents() + _(")") + base_descr::name(); }
|
||||
static constexpr auto name = _("(") + array_info<T>::extents + _(")") + base_descr::name;
|
||||
static pybind11::dtype dtype() {
|
||||
list shape;
|
||||
array_info<T>::append_extents(shape);
|
||||
@@ -1038,7 +1074,7 @@ template<typename T> struct npy_format_descriptor<T, enable_if_t<std::is_enum<T>
|
||||
private:
|
||||
using base_descr = npy_format_descriptor<typename std::underlying_type<T>::type>;
|
||||
public:
|
||||
static PYBIND11_DESCR name() { return base_descr::name(); }
|
||||
static constexpr auto name = base_descr::name;
|
||||
static pybind11::dtype dtype() { return base_descr::dtype(); }
|
||||
};
|
||||
|
||||
@@ -1051,7 +1087,7 @@ struct field_descriptor {
|
||||
};
|
||||
|
||||
inline PYBIND11_NOINLINE void register_structured_dtype(
|
||||
const std::initializer_list<field_descriptor>& fields,
|
||||
any_container<field_descriptor> fields,
|
||||
const std::type_info& tinfo, ssize_t itemsize,
|
||||
bool (*direct_converter)(PyObject *, void *&)) {
|
||||
|
||||
@@ -1059,8 +1095,14 @@ inline PYBIND11_NOINLINE void register_structured_dtype(
|
||||
if (numpy_internals.get_type_info(tinfo, false))
|
||||
pybind11_fail("NumPy: dtype is already registered");
|
||||
|
||||
// Use ordered fields because order matters as of NumPy 1.14:
|
||||
// https://docs.scipy.org/doc/numpy/release.html#multiple-field-indexing-assignment-of-structured-arrays
|
||||
std::vector<field_descriptor> ordered_fields(std::move(fields));
|
||||
std::sort(ordered_fields.begin(), ordered_fields.end(),
|
||||
[](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; });
|
||||
|
||||
list names, formats, offsets;
|
||||
for (auto field : fields) {
|
||||
for (auto& field : ordered_fields) {
|
||||
if (!field.descr)
|
||||
pybind11_fail(std::string("NumPy: unsupported field dtype: `") +
|
||||
field.name + "` @ " + tinfo.name());
|
||||
@@ -1077,9 +1119,6 @@ inline PYBIND11_NOINLINE void register_structured_dtype(
|
||||
// - https://github.com/numpy/numpy/pull/7798
|
||||
// Because of this, we won't use numpy's logic to generate buffer format
|
||||
// strings and will just do it ourselves.
|
||||
std::vector<field_descriptor> ordered_fields(fields);
|
||||
std::sort(ordered_fields.begin(), ordered_fields.end(),
|
||||
[](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; });
|
||||
ssize_t offset = 0;
|
||||
std::ostringstream oss;
|
||||
// mark the structure as unaligned with '^', because numpy and C++ don't
|
||||
@@ -1113,7 +1152,7 @@ inline PYBIND11_NOINLINE void register_structured_dtype(
|
||||
template <typename T, typename SFINAE> struct npy_format_descriptor {
|
||||
static_assert(is_pod_struct<T>::value, "Attempt to use a non-POD or unimplemented POD type as a numpy dtype");
|
||||
|
||||
static PYBIND11_DESCR name() { return make_caster<T>::name(); }
|
||||
static constexpr auto name = make_caster<T>::name;
|
||||
|
||||
static pybind11::dtype dtype() {
|
||||
return reinterpret_borrow<pybind11::dtype>(dtype_ptr());
|
||||
@@ -1124,8 +1163,8 @@ template <typename T, typename SFINAE> struct npy_format_descriptor {
|
||||
return format_str;
|
||||
}
|
||||
|
||||
static void register_dtype(const std::initializer_list<field_descriptor>& fields) {
|
||||
register_structured_dtype(fields, typeid(typename std::remove_cv<T>::type),
|
||||
static void register_dtype(any_container<field_descriptor> fields) {
|
||||
register_structured_dtype(std::move(fields), typeid(typename std::remove_cv<T>::type),
|
||||
sizeof(T), &direct_converter);
|
||||
}
|
||||
|
||||
@@ -1198,7 +1237,8 @@ private:
|
||||
|
||||
#define PYBIND11_NUMPY_DTYPE(Type, ...) \
|
||||
::pybind11::detail::npy_format_descriptor<Type>::register_dtype \
|
||||
({PYBIND11_MAP_LIST (PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)})
|
||||
(::std::vector<::pybind11::detail::field_descriptor> \
|
||||
{PYBIND11_MAP_LIST (PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)})
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define PYBIND11_MAP2_LIST_NEXT1(test, next) \
|
||||
@@ -1219,7 +1259,8 @@ private:
|
||||
|
||||
#define PYBIND11_NUMPY_DTYPE_EX(Type, ...) \
|
||||
::pybind11::detail::npy_format_descriptor<Type>::register_dtype \
|
||||
({PYBIND11_MAP2_LIST (PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)})
|
||||
(::std::vector<::pybind11::detail::field_descriptor> \
|
||||
{PYBIND11_MAP2_LIST (PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)})
|
||||
|
||||
#endif // __CLION_IDE__
|
||||
|
||||
@@ -1457,7 +1498,10 @@ public:
|
||||
private:
|
||||
remove_reference_t<Func> f;
|
||||
|
||||
template <size_t Index> using param_n_t = typename pack_element<Index, typename vectorize_arg<Args>::call_type...>::type;
|
||||
// Internal compiler error in MSVC 19.16.27025.1 (Visual Studio 2017 15.9.4), when compiling with "/permissive-" flag
|
||||
// when arg_call_types is manually inlined.
|
||||
using arg_call_types = std::tuple<typename vectorize_arg<Args>::call_type...>;
|
||||
template <size_t Index> using param_n_t = typename std::tuple_element<Index, arg_call_types>::type;
|
||||
|
||||
// Runs a vectorized function given arguments tuple and three index sequences:
|
||||
// - Index is the full set of 0 ... (N-1) argument indices;
|
||||
@@ -1497,7 +1541,7 @@ private:
|
||||
if (trivial == broadcast_trivial::f_trivial) result = array_t<Return, array::f_style>(shape);
|
||||
else result = array_t<Return>(shape);
|
||||
|
||||
if (size == 0) return result;
|
||||
if (size == 0) return std::move(result);
|
||||
|
||||
/* Call the function */
|
||||
if (trivial == broadcast_trivial::non_trivial)
|
||||
@@ -1505,7 +1549,7 @@ private:
|
||||
else
|
||||
apply_trivial(buffers, params, result.mutable_data(), size, i_seq, vi_seq, bi_seq);
|
||||
|
||||
return result;
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
template <size_t... Index, size_t... VIndex, size_t... BIndex>
|
||||
@@ -1558,9 +1602,7 @@ vectorize_extractor(const Func &f, Return (*) (Args ...)) {
|
||||
}
|
||||
|
||||
template <typename T, int Flags> struct handle_type_name<array_t<T, Flags>> {
|
||||
static PYBIND11_DESCR name() {
|
||||
return _("numpy.ndarray[") + npy_format_descriptor<T>::name() + _("]");
|
||||
}
|
||||
static constexpr auto name = _("numpy.ndarray[") + npy_format_descriptor<T>::name + _("]");
|
||||
};
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
@@ -1586,7 +1628,7 @@ Helper vectorize(Return (Class::*f)(Args...)) {
|
||||
return Helper(std::mem_fn(f));
|
||||
}
|
||||
|
||||
// Vectorize a class method (non-const):
|
||||
// Vectorize a class method (const):
|
||||
template <typename Return, typename Class, typename... Args,
|
||||
typename Helper = detail::vectorize_helper<decltype(std::mem_fn(std::declval<Return (Class::*)(Args...) const>())), Return, const Class *, Args...>>
|
||||
Helper vectorize(Return (Class::*f)(Args...) const) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -114,6 +114,35 @@ public:
|
||||
bool is(object_api const& other) const { return derived().ptr() == other.derived().ptr(); }
|
||||
/// Equivalent to ``obj is None`` in Python.
|
||||
bool is_none() const { return derived().ptr() == Py_None; }
|
||||
/// Equivalent to obj == other in Python
|
||||
bool equal(object_api const &other) const { return rich_compare(other, Py_EQ); }
|
||||
bool not_equal(object_api const &other) const { return rich_compare(other, Py_NE); }
|
||||
bool operator<(object_api const &other) const { return rich_compare(other, Py_LT); }
|
||||
bool operator<=(object_api const &other) const { return rich_compare(other, Py_LE); }
|
||||
bool operator>(object_api const &other) const { return rich_compare(other, Py_GT); }
|
||||
bool operator>=(object_api const &other) const { return rich_compare(other, Py_GE); }
|
||||
|
||||
object operator-() const;
|
||||
object operator~() const;
|
||||
object operator+(object_api const &other) const;
|
||||
object operator+=(object_api const &other) const;
|
||||
object operator-(object_api const &other) const;
|
||||
object operator-=(object_api const &other) const;
|
||||
object operator*(object_api const &other) const;
|
||||
object operator*=(object_api const &other) const;
|
||||
object operator/(object_api const &other) const;
|
||||
object operator/=(object_api const &other) const;
|
||||
object operator|(object_api const &other) const;
|
||||
object operator|=(object_api const &other) const;
|
||||
object operator&(object_api const &other) const;
|
||||
object operator&=(object_api const &other) const;
|
||||
object operator^(object_api const &other) const;
|
||||
object operator^=(object_api const &other) const;
|
||||
object operator<<(object_api const &other) const;
|
||||
object operator<<=(object_api const &other) const;
|
||||
object operator>>(object_api const &other) const;
|
||||
object operator>>=(object_api const &other) const;
|
||||
|
||||
PYBIND11_DEPRECATED("Use py::str(obj) instead")
|
||||
pybind11::str str() const;
|
||||
|
||||
@@ -124,6 +153,9 @@ public:
|
||||
int ref_count() const { return static_cast<int>(Py_REFCNT(derived().ptr())); }
|
||||
/// Return a handle to the Python type object underlying the instance
|
||||
handle get_type() const;
|
||||
|
||||
private:
|
||||
bool rich_compare(object_api const &other, int value) const;
|
||||
};
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
@@ -292,15 +324,18 @@ public:
|
||||
/// Constructs a new exception from the current Python error indicator, if any. The current
|
||||
/// Python error indicator will be cleared.
|
||||
error_already_set() : std::runtime_error(detail::error_string()) {
|
||||
PyErr_Fetch(&type.ptr(), &value.ptr(), &trace.ptr());
|
||||
PyErr_Fetch(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr());
|
||||
}
|
||||
|
||||
error_already_set(const error_already_set &) = default;
|
||||
error_already_set(error_already_set &&) = default;
|
||||
|
||||
inline ~error_already_set();
|
||||
|
||||
/// Give the currently-held error back to Python, if any. If there is currently a Python error
|
||||
/// already set it is cleared first. After this call, the current object no longer stores the
|
||||
/// error variables (but the `.what()` string is still available).
|
||||
void restore() { PyErr_Restore(type.release().ptr(), value.release().ptr(), trace.release().ptr()); }
|
||||
void restore() { PyErr_Restore(m_type.release().ptr(), m_value.release().ptr(), m_trace.release().ptr()); }
|
||||
|
||||
// Does nothing; provided for backwards compatibility.
|
||||
PYBIND11_DEPRECATED("Use of error_already_set.clear() is deprecated")
|
||||
@@ -309,10 +344,14 @@ public:
|
||||
/// Check if the currently trapped error type matches the given Python exception class (or a
|
||||
/// subclass thereof). May also be passed a tuple to search for any exception class matches in
|
||||
/// the given tuple.
|
||||
bool matches(handle ex) const { return PyErr_GivenExceptionMatches(ex.ptr(), type.ptr()); }
|
||||
bool matches(handle exc) const { return PyErr_GivenExceptionMatches(m_type.ptr(), exc.ptr()); }
|
||||
|
||||
const object& type() const { return m_type; }
|
||||
const object& value() const { return m_value; }
|
||||
const object& trace() const { return m_trace; }
|
||||
|
||||
private:
|
||||
object type, value, trace;
|
||||
object m_type, m_value, m_trace;
|
||||
};
|
||||
|
||||
/** \defgroup python_builtins _
|
||||
@@ -353,6 +392,14 @@ inline bool hasattr(handle obj, const char *name) {
|
||||
return PyObject_HasAttrString(obj.ptr(), name) == 1;
|
||||
}
|
||||
|
||||
inline void delattr(handle obj, handle name) {
|
||||
if (PyObject_DelAttr(obj.ptr(), name.ptr()) != 0) { throw error_already_set(); }
|
||||
}
|
||||
|
||||
inline void delattr(handle obj, const char *name) {
|
||||
if (PyObject_DelAttrString(obj.ptr(), name) != 0) { throw error_already_set(); }
|
||||
}
|
||||
|
||||
inline object getattr(handle obj, handle name) {
|
||||
PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr());
|
||||
if (!result) { throw error_already_set(); }
|
||||
@@ -424,7 +471,6 @@ object object_or_cast(T &&o);
|
||||
// Match a PyObject*, which we want to convert directly to handle via its converting constructor
|
||||
inline handle object_or_cast(PyObject *ptr) { return ptr; }
|
||||
|
||||
|
||||
template <typename Policy>
|
||||
class accessor : public object_api<accessor<Policy>> {
|
||||
using key_type = typename Policy::key_type;
|
||||
@@ -662,7 +708,7 @@ protected:
|
||||
|
||||
private:
|
||||
handle obj;
|
||||
PyObject *key, *value;
|
||||
PyObject *key = nullptr, *value = nullptr;
|
||||
ssize_t pos = -1;
|
||||
};
|
||||
NAMESPACE_END(iterator_policies)
|
||||
@@ -690,9 +736,14 @@ inline bool PyIterable_Check(PyObject *obj) {
|
||||
}
|
||||
|
||||
inline bool PyNone_Check(PyObject *o) { return o == Py_None; }
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
inline bool PyEllipsis_Check(PyObject *o) { return o == Py_Ellipsis; }
|
||||
#endif
|
||||
|
||||
inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); }
|
||||
|
||||
inline bool PyStaticMethod_Check(PyObject *o) { return o->ob_type == &PyStaticMethod_Type; }
|
||||
|
||||
class kwargs_proxy : public handle {
|
||||
public:
|
||||
explicit kwargs_proxy(handle h) : handle(h) { }
|
||||
@@ -964,6 +1015,14 @@ public:
|
||||
none() : object(Py_None, borrowed_t{}) { }
|
||||
};
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
class ellipsis : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT(ellipsis, object, detail::PyEllipsis_Check)
|
||||
ellipsis() : object(Py_Ellipsis, borrowed_t{}) { }
|
||||
};
|
||||
#endif
|
||||
|
||||
class bool_ : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool)
|
||||
@@ -1074,6 +1133,13 @@ public:
|
||||
(ssize_t *) stop, (ssize_t *) step,
|
||||
(ssize_t *) slicelength) == 0;
|
||||
}
|
||||
bool compute(ssize_t length, ssize_t *start, ssize_t *stop, ssize_t *step,
|
||||
ssize_t *slicelength) const {
|
||||
return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr,
|
||||
length, start,
|
||||
stop, step,
|
||||
slicelength) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
class capsule : public object {
|
||||
@@ -1136,7 +1202,9 @@ public:
|
||||
if (!m_ptr) pybind11_fail("Could not allocate tuple object!");
|
||||
}
|
||||
size_t size() const { return (size_t) PyTuple_Size(m_ptr); }
|
||||
bool empty() const { return size() == 0; }
|
||||
detail::tuple_accessor operator[](size_t index) const { return {*this, index}; }
|
||||
detail::item_accessor operator[](handle h) const { return object::operator[](h); }
|
||||
detail::tuple_iterator begin() const { return {*this, 0}; }
|
||||
detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; }
|
||||
};
|
||||
@@ -1154,11 +1222,13 @@ public:
|
||||
explicit dict(Args &&...args) : dict(collector(std::forward<Args>(args)...).kwargs()) { }
|
||||
|
||||
size_t size() const { return (size_t) PyDict_Size(m_ptr); }
|
||||
bool empty() const { return size() == 0; }
|
||||
detail::dict_iterator begin() const { return {*this, 0}; }
|
||||
detail::dict_iterator end() const { return {}; }
|
||||
void clear() const { PyDict_Clear(ptr()); }
|
||||
bool contains(handle key) const { return PyDict_Contains(ptr(), key.ptr()) == 1; }
|
||||
bool contains(const char *key) const { return PyDict_Contains(ptr(), pybind11::str(key).ptr()) == 1; }
|
||||
template <typename T> bool contains(T &&key) const {
|
||||
return PyDict_Contains(m_ptr, detail::object_or_cast(std::forward<T>(key)).ptr()) == 1;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Call the `dict` Python type -- always returns a new reference
|
||||
@@ -1173,7 +1243,9 @@ class sequence : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check)
|
||||
size_t size() const { return (size_t) PySequence_Size(m_ptr); }
|
||||
bool empty() const { return size() == 0; }
|
||||
detail::sequence_accessor operator[](size_t index) const { return {*this, index}; }
|
||||
detail::item_accessor operator[](handle h) const { return object::operator[](h); }
|
||||
detail::sequence_iterator begin() const { return {*this, 0}; }
|
||||
detail::sequence_iterator end() const { return {*this, PySequence_Size(m_ptr)}; }
|
||||
};
|
||||
@@ -1185,12 +1257,18 @@ public:
|
||||
if (!m_ptr) pybind11_fail("Could not allocate list object!");
|
||||
}
|
||||
size_t size() const { return (size_t) PyList_Size(m_ptr); }
|
||||
bool empty() const { return size() == 0; }
|
||||
detail::list_accessor operator[](size_t index) const { return {*this, index}; }
|
||||
detail::item_accessor operator[](handle h) const { return object::operator[](h); }
|
||||
detail::list_iterator begin() const { return {*this, 0}; }
|
||||
detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; }
|
||||
template <typename T> void append(T &&val) const {
|
||||
PyList_Append(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr());
|
||||
}
|
||||
template <typename T> void insert(size_t index, T &&val) const {
|
||||
PyList_Insert(m_ptr, static_cast<ssize_t>(index),
|
||||
detail::object_or_cast(std::forward<T>(val)).ptr());
|
||||
}
|
||||
};
|
||||
|
||||
class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) };
|
||||
@@ -1203,10 +1281,14 @@ public:
|
||||
if (!m_ptr) pybind11_fail("Could not allocate set object!");
|
||||
}
|
||||
size_t size() const { return (size_t) PySet_Size(m_ptr); }
|
||||
bool empty() const { return size() == 0; }
|
||||
template <typename T> bool add(T &&val) const {
|
||||
return PySet_Add(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 0;
|
||||
}
|
||||
void clear() const { PySet_Clear(m_ptr); }
|
||||
template <typename T> bool contains(T &&val) const {
|
||||
return PySet_Contains(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 1;
|
||||
}
|
||||
};
|
||||
|
||||
class function : public object {
|
||||
@@ -1221,11 +1303,16 @@ public:
|
||||
bool is_cpp_function() const { return (bool) cpp_function(); }
|
||||
};
|
||||
|
||||
class staticmethod : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(staticmethod, object, detail::PyStaticMethod_Check, PyStaticMethod_New)
|
||||
};
|
||||
|
||||
class buffer : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer)
|
||||
|
||||
buffer_info request(bool writable = false) {
|
||||
buffer_info request(bool writable = false) const {
|
||||
int flags = PyBUF_STRIDES | PyBUF_FORMAT;
|
||||
if (writable) flags |= PyBUF_WRITABLE;
|
||||
Py_buffer *view = new Py_buffer();
|
||||
@@ -1279,6 +1366,21 @@ inline size_t len(handle h) {
|
||||
return (size_t) result;
|
||||
}
|
||||
|
||||
inline size_t len_hint(handle h) {
|
||||
#if PY_VERSION_HEX >= 0x03040000
|
||||
ssize_t result = PyObject_LengthHint(h.ptr(), 0);
|
||||
#else
|
||||
ssize_t result = PyObject_Length(h.ptr());
|
||||
#endif
|
||||
if (result < 0) {
|
||||
// Sometimes a length can't be determined at all (eg generators)
|
||||
// In which case simply return 0
|
||||
PyErr_Clear();
|
||||
return 0;
|
||||
}
|
||||
return (size_t) result;
|
||||
}
|
||||
|
||||
inline str repr(handle h) {
|
||||
PyObject *str_value = PyObject_Repr(h.ptr());
|
||||
if (!str_value) throw error_already_set();
|
||||
@@ -1328,5 +1430,55 @@ str_attr_accessor object_api<D>::doc() const { return attr("__doc__"); }
|
||||
template <typename D>
|
||||
handle object_api<D>::get_type() const { return (PyObject *) Py_TYPE(derived().ptr()); }
|
||||
|
||||
template <typename D>
|
||||
bool object_api<D>::rich_compare(object_api const &other, int value) const {
|
||||
int rv = PyObject_RichCompareBool(derived().ptr(), other.derived().ptr(), value);
|
||||
if (rv == -1)
|
||||
throw error_already_set();
|
||||
return rv == 1;
|
||||
}
|
||||
|
||||
#define PYBIND11_MATH_OPERATOR_UNARY(op, fn) \
|
||||
template <typename D> object object_api<D>::op() const { \
|
||||
object result = reinterpret_steal<object>(fn(derived().ptr())); \
|
||||
if (!result.ptr()) \
|
||||
throw error_already_set(); \
|
||||
return result; \
|
||||
}
|
||||
|
||||
#define PYBIND11_MATH_OPERATOR_BINARY(op, fn) \
|
||||
template <typename D> \
|
||||
object object_api<D>::op(object_api const &other) const { \
|
||||
object result = reinterpret_steal<object>( \
|
||||
fn(derived().ptr(), other.derived().ptr())); \
|
||||
if (!result.ptr()) \
|
||||
throw error_already_set(); \
|
||||
return result; \
|
||||
}
|
||||
|
||||
PYBIND11_MATH_OPERATOR_UNARY (operator~, PyNumber_Invert)
|
||||
PYBIND11_MATH_OPERATOR_UNARY (operator-, PyNumber_Negative)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator+, PyNumber_Add)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator+=, PyNumber_InPlaceAdd)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator-, PyNumber_Subtract)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator-=, PyNumber_InPlaceSubtract)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator*, PyNumber_Multiply)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator*=, PyNumber_InPlaceMultiply)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator/, PyNumber_TrueDivide)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator/=, PyNumber_InPlaceTrueDivide)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator|, PyNumber_Or)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator|=, PyNumber_InPlaceOr)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator&, PyNumber_And)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator&=, PyNumber_InPlaceAnd)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator^, PyNumber_Xor)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator^=, PyNumber_InPlaceXor)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator<<, PyNumber_Lshift)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator<<=, PyNumber_InPlaceLshift)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator>>, PyNumber_Rshift)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator>>=, PyNumber_InPlaceRshift)
|
||||
|
||||
#undef PYBIND11_MATH_OPERATOR_UNARY
|
||||
#undef PYBIND11_MATH_OPERATOR_BINARY
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <deque>
|
||||
#include <valarray>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
@@ -30,7 +31,8 @@
|
||||
# define PYBIND11_HAS_OPTIONAL 1
|
||||
# endif
|
||||
// std::experimental::optional (but not allowed in c++11 mode)
|
||||
# if defined(PYBIND11_CPP14) && __has_include(<experimental/optional>)
|
||||
# if defined(PYBIND11_CPP14) && (__has_include(<experimental/optional>) && \
|
||||
!__has_include(<optional>))
|
||||
# include <experimental/optional>
|
||||
# define PYBIND11_HAS_EXP_OPTIONAL 1
|
||||
# endif
|
||||
@@ -82,6 +84,8 @@ template <typename Type, typename Key> struct set_caster {
|
||||
|
||||
template <typename T>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
if (!std::is_lvalue_reference<T>::value)
|
||||
policy = return_value_policy_override<Key>::policy(policy);
|
||||
pybind11::set s;
|
||||
for (auto &&value : src) {
|
||||
auto value_ = reinterpret_steal<object>(key_conv::cast(forward_like<T>(value), policy, parent));
|
||||
@@ -91,7 +95,7 @@ template <typename Type, typename Key> struct set_caster {
|
||||
return s.release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(type, _("Set[") + key_conv::name() + _("]"));
|
||||
PYBIND11_TYPE_CASTER(type, _("Set[") + key_conv::name + _("]"));
|
||||
};
|
||||
|
||||
template <typename Type, typename Key, typename Value> struct map_caster {
|
||||
@@ -117,9 +121,15 @@ template <typename Type, typename Key, typename Value> struct map_caster {
|
||||
template <typename T>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
dict d;
|
||||
return_value_policy policy_key = policy;
|
||||
return_value_policy policy_value = policy;
|
||||
if (!std::is_lvalue_reference<T>::value) {
|
||||
policy_key = return_value_policy_override<Key>::policy(policy_key);
|
||||
policy_value = return_value_policy_override<Value>::policy(policy_value);
|
||||
}
|
||||
for (auto &&kv : src) {
|
||||
auto key = reinterpret_steal<object>(key_conv::cast(forward_like<T>(kv.first), policy, parent));
|
||||
auto value = reinterpret_steal<object>(value_conv::cast(forward_like<T>(kv.second), policy, parent));
|
||||
auto key = reinterpret_steal<object>(key_conv::cast(forward_like<T>(kv.first), policy_key, parent));
|
||||
auto value = reinterpret_steal<object>(value_conv::cast(forward_like<T>(kv.second), policy_value, parent));
|
||||
if (!key || !value)
|
||||
return handle();
|
||||
d[key] = value;
|
||||
@@ -127,14 +137,14 @@ template <typename Type, typename Key, typename Value> struct map_caster {
|
||||
return d.release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(Type, _("Dict[") + key_conv::name() + _(", ") + value_conv::name() + _("]"));
|
||||
PYBIND11_TYPE_CASTER(Type, _("Dict[") + key_conv::name + _(", ") + value_conv::name + _("]"));
|
||||
};
|
||||
|
||||
template <typename Type, typename Value> struct list_caster {
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<sequence>(src))
|
||||
if (!isinstance<sequence>(src) || isinstance<str>(src))
|
||||
return false;
|
||||
auto s = reinterpret_borrow<sequence>(src);
|
||||
value.clear();
|
||||
@@ -157,6 +167,8 @@ private:
|
||||
public:
|
||||
template <typename T>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
if (!std::is_lvalue_reference<T>::value)
|
||||
policy = return_value_policy_override<Value>::policy(policy);
|
||||
list l(src.size());
|
||||
size_t index = 0;
|
||||
for (auto &&value : src) {
|
||||
@@ -168,12 +180,15 @@ public:
|
||||
return l.release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(Type, _("List[") + value_conv::name() + _("]"));
|
||||
PYBIND11_TYPE_CASTER(Type, _("List[") + value_conv::name + _("]"));
|
||||
};
|
||||
|
||||
template <typename Type, typename Alloc> struct type_caster<std::vector<Type, Alloc>>
|
||||
: list_caster<std::vector<Type, Alloc>, Type> { };
|
||||
|
||||
template <typename Type, typename Alloc> struct type_caster<std::deque<Type, Alloc>>
|
||||
: list_caster<std::deque<Type, Alloc>, Type> { };
|
||||
|
||||
template <typename Type, typename Alloc> struct type_caster<std::list<Type, Alloc>>
|
||||
: list_caster<std::list<Type, Alloc>, Type> { };
|
||||
|
||||
@@ -194,9 +209,9 @@ private:
|
||||
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<list>(src))
|
||||
if (!isinstance<sequence>(src))
|
||||
return false;
|
||||
auto l = reinterpret_borrow<list>(src);
|
||||
auto l = reinterpret_borrow<sequence>(src);
|
||||
if (!require_size(l.size()))
|
||||
return false;
|
||||
size_t ctr = 0;
|
||||
@@ -222,7 +237,7 @@ public:
|
||||
return l.release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(ArrayType, _("List[") + value_conv::name() + _<Resizable>(_(""), _("[") + _<Size>() + _("]")) + _("]"));
|
||||
PYBIND11_TYPE_CASTER(ArrayType, _("List[") + value_conv::name + _<Resizable>(_(""), _("[") + _<Size>() + _("]")) + _("]"));
|
||||
};
|
||||
|
||||
template <typename Type, size_t Size> struct type_caster<std::array<Type, Size>>
|
||||
@@ -251,6 +266,7 @@ template<typename T> 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<typename T::value_type>::policy(policy);
|
||||
return value_conv::cast(*std::forward<T_>(src), policy, parent);
|
||||
}
|
||||
|
||||
@@ -268,7 +284,7 @@ template<typename T> struct optional_caster {
|
||||
return true;
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name() + _("]"));
|
||||
PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name + _("]"));
|
||||
};
|
||||
|
||||
#ifdef PYBIND11_HAS_OPTIONAL
|
||||
@@ -348,13 +364,14 @@ struct variant_caster<V<Ts...>> {
|
||||
}
|
||||
|
||||
using Type = V<Ts...>;
|
||||
PYBIND11_TYPE_CASTER(Type, _("Union[") + detail::concat(make_caster<Ts>::name()...) + _("]"));
|
||||
PYBIND11_TYPE_CASTER(Type, _("Union[") + detail::concat(make_caster<Ts>::name...) + _("]"));
|
||||
};
|
||||
|
||||
#ifdef PYBIND11_HAS_VARIANT
|
||||
template <typename... Ts>
|
||||
struct type_caster<std::variant<Ts...>> : variant_caster<std::variant<Ts...>> { };
|
||||
#endif
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &os, const handle &obj) {
|
||||
|
||||
@@ -115,6 +115,14 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
using SizeType = typename Vector::size_type;
|
||||
using DiffType = typename Vector::difference_type;
|
||||
|
||||
auto wrap_i = [](DiffType i, SizeType n) {
|
||||
if (i < 0)
|
||||
i += n;
|
||||
if (i < 0 || (SizeType)i >= n)
|
||||
throw index_error();
|
||||
return i;
|
||||
};
|
||||
|
||||
cl.def("append",
|
||||
[](Vector &v, const T &value) { v.push_back(value); },
|
||||
arg("x"),
|
||||
@@ -122,7 +130,7 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
|
||||
cl.def(init([](iterable it) {
|
||||
auto v = std::unique_ptr<Vector>(new Vector());
|
||||
v->reserve(len(it));
|
||||
v->reserve(len_hint(it));
|
||||
for (handle h : it)
|
||||
v->push_back(h.cast<T>());
|
||||
return v.release();
|
||||
@@ -136,11 +144,36 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
"Extend the list by appending all the items in the given list"
|
||||
);
|
||||
|
||||
cl.def("extend",
|
||||
[](Vector &v, iterable it) {
|
||||
const size_t old_size = v.size();
|
||||
v.reserve(old_size + len_hint(it));
|
||||
try {
|
||||
for (handle h : it) {
|
||||
v.push_back(h.cast<T>());
|
||||
}
|
||||
} catch (const cast_error &) {
|
||||
v.erase(v.begin() + static_cast<typename Vector::difference_type>(old_size), v.end());
|
||||
try {
|
||||
v.shrink_to_fit();
|
||||
} catch (const std::exception &) {
|
||||
// Do nothing
|
||||
}
|
||||
throw;
|
||||
}
|
||||
},
|
||||
arg("L"),
|
||||
"Extend the list by appending all the items in the given list"
|
||||
);
|
||||
|
||||
cl.def("insert",
|
||||
[](Vector &v, SizeType i, const T &x) {
|
||||
if (i > v.size())
|
||||
[](Vector &v, DiffType i, const T &x) {
|
||||
// Can't use wrap_i; i == v.size() is OK
|
||||
if (i < 0)
|
||||
i += v.size();
|
||||
if (i < 0 || (SizeType)i > v.size())
|
||||
throw index_error();
|
||||
v.insert(v.begin() + (DiffType) i, x);
|
||||
v.insert(v.begin() + i, x);
|
||||
},
|
||||
arg("i") , arg("x"),
|
||||
"Insert an item at a given position."
|
||||
@@ -158,11 +191,10 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
);
|
||||
|
||||
cl.def("pop",
|
||||
[](Vector &v, SizeType i) {
|
||||
if (i >= v.size())
|
||||
throw index_error();
|
||||
T t = v[i];
|
||||
v.erase(v.begin() + (DiffType) i);
|
||||
[wrap_i](Vector &v, DiffType i) {
|
||||
i = wrap_i(i, v.size());
|
||||
T t = v[(SizeType) i];
|
||||
v.erase(v.begin() + i);
|
||||
return t;
|
||||
},
|
||||
arg("i"),
|
||||
@@ -170,10 +202,9 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
);
|
||||
|
||||
cl.def("__setitem__",
|
||||
[](Vector &v, SizeType i, const T &t) {
|
||||
if (i >= v.size())
|
||||
throw index_error();
|
||||
v[i] = t;
|
||||
[wrap_i](Vector &v, DiffType i, const T &t) {
|
||||
i = wrap_i(i, v.size());
|
||||
v[(SizeType)i] = t;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -216,10 +247,9 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
);
|
||||
|
||||
cl.def("__delitem__",
|
||||
[](Vector &v, SizeType i) {
|
||||
if (i >= v.size())
|
||||
throw index_error();
|
||||
v.erase(v.begin() + DiffType(i));
|
||||
[wrap_i](Vector &v, DiffType i) {
|
||||
i = wrap_i(i, v.size());
|
||||
v.erase(v.begin() + i);
|
||||
},
|
||||
"Delete the list elements at index ``i``"
|
||||
);
|
||||
@@ -255,13 +285,21 @@ template <typename Vector, typename Class_>
|
||||
void vector_accessor(enable_if_t<!vector_needs_copy<Vector>::value, Class_> &cl) {
|
||||
using T = typename Vector::value_type;
|
||||
using SizeType = typename Vector::size_type;
|
||||
using DiffType = typename Vector::difference_type;
|
||||
using ItType = typename Vector::iterator;
|
||||
|
||||
auto wrap_i = [](DiffType i, SizeType n) {
|
||||
if (i < 0)
|
||||
i += n;
|
||||
if (i < 0 || (SizeType)i >= n)
|
||||
throw index_error();
|
||||
return i;
|
||||
};
|
||||
|
||||
cl.def("__getitem__",
|
||||
[](Vector &v, SizeType i) -> T & {
|
||||
if (i >= v.size())
|
||||
throw index_error();
|
||||
return v[i];
|
||||
[wrap_i](Vector &v, DiffType i) -> T & {
|
||||
i = wrap_i(i, v.size());
|
||||
return v[(SizeType)i];
|
||||
},
|
||||
return_value_policy::reference_internal // ref + keepalive
|
||||
);
|
||||
@@ -281,12 +319,15 @@ template <typename Vector, typename Class_>
|
||||
void vector_accessor(enable_if_t<vector_needs_copy<Vector>::value, Class_> &cl) {
|
||||
using T = typename Vector::value_type;
|
||||
using SizeType = typename Vector::size_type;
|
||||
using DiffType = typename Vector::difference_type;
|
||||
using ItType = typename Vector::iterator;
|
||||
cl.def("__getitem__",
|
||||
[](const Vector &v, SizeType i) -> T {
|
||||
if (i >= v.size())
|
||||
[](const Vector &v, DiffType i) -> T {
|
||||
if (i < 0 && (i += v.size()) < 0)
|
||||
throw index_error();
|
||||
return v[i];
|
||||
if ((SizeType)i >= v.size())
|
||||
throw index_error();
|
||||
return v[(SizeType)i];
|
||||
}
|
||||
);
|
||||
|
||||
@@ -579,6 +620,15 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
|
||||
return_value_policy::reference_internal // ref + keepalive
|
||||
);
|
||||
|
||||
cl.def("__contains__",
|
||||
[](Map &m, const KeyType &k) -> bool {
|
||||
auto it = m.find(k);
|
||||
if (it == m.end())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
// Assignment provided only if the type is copyable
|
||||
detail::map_assignment<Map, Class_>(cl);
|
||||
|
||||
@@ -587,7 +637,7 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
|
||||
auto it = m.find(k);
|
||||
if (it == m.end())
|
||||
throw key_error();
|
||||
return m.erase(it);
|
||||
m.erase(it);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -1,11 +1,36 @@
|
||||
from ._version import version_info, __version__ # noqa: F401 imported but unused
|
||||
|
||||
|
||||
def get_include(*args, **kwargs):
|
||||
def get_include(user=False):
|
||||
from distutils.dist import Distribution
|
||||
import os
|
||||
try:
|
||||
from pip import locations
|
||||
return os.path.dirname(
|
||||
locations.distutils_scheme('pybind11', *args, **kwargs)['headers'])
|
||||
except ImportError:
|
||||
return 'include'
|
||||
import sys
|
||||
|
||||
# Are we running in a virtual environment?
|
||||
virtualenv = hasattr(sys, 'real_prefix') or \
|
||||
sys.prefix != getattr(sys, "base_prefix", sys.prefix)
|
||||
|
||||
# Are we running in a conda environment?
|
||||
conda = os.path.exists(os.path.join(sys.prefix, 'conda-meta'))
|
||||
|
||||
if virtualenv:
|
||||
return os.path.join(sys.prefix, 'include', 'site',
|
||||
'python' + sys.version[:3])
|
||||
elif conda:
|
||||
if os.name == 'nt':
|
||||
return os.path.join(sys.prefix, 'Library', 'include')
|
||||
else:
|
||||
return os.path.join(sys.prefix, 'include')
|
||||
else:
|
||||
dist = Distribution({'name': 'pybind11'})
|
||||
dist.parse_config_files()
|
||||
|
||||
dist_cobj = dist.get_command_obj('install', create=True)
|
||||
|
||||
# Search for packages in user's home directory?
|
||||
if user:
|
||||
dist_cobj.user = user
|
||||
dist_cobj.prefix = ""
|
||||
dist_cobj.finalize_options()
|
||||
|
||||
return os.path.dirname(dist_cobj.install_headers)
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
version_info = (2, 2, 1)
|
||||
version_info = (2, 4, 1)
|
||||
__version__ = '.'.join(map(str, version_info))
|
||||
|
||||
@@ -6,5 +6,7 @@ max-line-length = 99
|
||||
show_source = True
|
||||
exclude = .git, __pycache__, build, dist, docs, tools, venv
|
||||
ignore =
|
||||
# required for pretty matrix formating: multiple spaces after `,` and `[`
|
||||
E201, E241
|
||||
# required for pretty matrix formatting: multiple spaces after `,` and `[`
|
||||
E201, E241, W504,
|
||||
# camelcase 'cPickle' imported as lowercase 'pickle'
|
||||
N813
|
||||
|
||||
@@ -61,8 +61,8 @@ setup(
|
||||
description='Seamless operability between C++11 and Python',
|
||||
author='Wenzel Jakob',
|
||||
author_email='wenzel.jakob@epfl.ch',
|
||||
url='https://github.com/wjakob/pybind11',
|
||||
download_url='https://github.com/wjakob/pybind11/tarball/v' + __version__,
|
||||
url='https://github.com/pybind/pybind11',
|
||||
download_url='https://github.com/pybind/pybind11/tarball/v' + __version__,
|
||||
packages=['pybind11'],
|
||||
license='BSD',
|
||||
headers=headers,
|
||||
|
||||
@@ -26,6 +26,7 @@ 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
|
||||
@@ -40,6 +41,7 @@ set(PYBIND11_TEST_FILES
|
||||
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
|
||||
@@ -57,6 +59,8 @@ set(PYBIND11_TEST_FILES
|
||||
test_smart_ptr.cpp
|
||||
test_stl.cpp
|
||||
test_stl_binders.cpp
|
||||
test_tagbased_polymorphic.cpp
|
||||
test_union.cpp
|
||||
test_virtual_functions.cpp
|
||||
)
|
||||
|
||||
@@ -68,6 +72,13 @@ 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")
|
||||
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_ASYNC_I})
|
||||
endif()
|
||||
|
||||
string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_FILES}")
|
||||
|
||||
# Contains the set of test files that require pybind11_cross_module_tests to be
|
||||
@@ -80,6 +91,10 @@ set(PYBIND11_CROSS_MODULE_TESTS
|
||||
test_stl_binders.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"
|
||||
# skip message).
|
||||
@@ -89,7 +104,7 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
||||
# 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)
|
||||
find_package(Eigen3 QUIET CONFIG)
|
||||
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)
|
||||
@@ -99,7 +114,7 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
||||
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 QUIET)
|
||||
find_package(Eigen3 3.2.7 QUIET)
|
||||
endif()
|
||||
|
||||
if(EIGEN3_FOUND)
|
||||
@@ -123,14 +138,14 @@ find_package(Boost 1.56)
|
||||
function(pybind11_enable_warnings target_name)
|
||||
if(MSVC)
|
||||
target_compile_options(${target_name} PRIVATE /W4)
|
||||
else()
|
||||
target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wconversion -Wcast-qual)
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)")
|
||||
target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wconversion -Wcast-qual -Wdeprecated)
|
||||
endif()
|
||||
|
||||
if(PYBIND11_WERROR)
|
||||
if(MSVC)
|
||||
target_compile_options(${target_name} PRIVATE /WX)
|
||||
else()
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)")
|
||||
target_compile_options(${target_name} PRIVATE -Werror)
|
||||
endif()
|
||||
endif()
|
||||
@@ -147,6 +162,14 @@ foreach(t ${PYBIND11_CROSS_MODULE_TESTS})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
foreach(t ${PYBIND11_CROSS_MODULE_GIL_TESTS})
|
||||
list(FIND PYBIND11_PYTEST_FILES ${t} i)
|
||||
if (i GREATER -1)
|
||||
list(APPEND test_targets cross_module_gil_utils)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(testdir ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
foreach(target ${test_targets})
|
||||
set(test_files ${PYBIND11_TEST_FILES})
|
||||
|
||||
@@ -17,6 +17,11 @@ _unicode_marker = re.compile(r'u(\'[^\']*\')')
|
||||
_long_marker = re.compile(r'([0-9])L')
|
||||
_hexadecimal = re.compile(r'0x[0-9a-fA-F]+')
|
||||
|
||||
# test_async.py requires support for async and await
|
||||
collect_ignore = []
|
||||
if sys.version_info[:2] < (3, 5):
|
||||
collect_ignore.append("test_async.py")
|
||||
|
||||
|
||||
def _strip_and_dedent(s):
|
||||
"""For triple-quote strings"""
|
||||
@@ -75,7 +80,7 @@ class Capture(object):
|
||||
self.capfd.readouterr()
|
||||
return self
|
||||
|
||||
def __exit__(self, *_):
|
||||
def __exit__(self, *args):
|
||||
self.out, self.err = self.capfd.readouterr()
|
||||
|
||||
def __eq__(self, other):
|
||||
@@ -185,7 +190,7 @@ def gc_collect():
|
||||
gc.collect()
|
||||
|
||||
|
||||
def pytest_namespace():
|
||||
def pytest_configure():
|
||||
"""Add import suppression and test requirements to `pytest` namespace"""
|
||||
try:
|
||||
import numpy as np
|
||||
@@ -202,19 +207,17 @@ def pytest_namespace():
|
||||
pypy = platform.python_implementation() == "PyPy"
|
||||
|
||||
skipif = pytest.mark.skipif
|
||||
return {
|
||||
'suppress': suppress,
|
||||
'requires_numpy': skipif(not np, reason="numpy is not installed"),
|
||||
'requires_scipy': skipif(not np, reason="scipy is not installed"),
|
||||
'requires_eigen_and_numpy': skipif(not have_eigen or not np,
|
||||
reason="eigen and/or numpy are not installed"),
|
||||
'requires_eigen_and_scipy': skipif(not have_eigen or not scipy,
|
||||
reason="eigen and/or scipy are not installed"),
|
||||
'unsupported_on_pypy': skipif(pypy, reason="unsupported on PyPy"),
|
||||
'unsupported_on_py2': skipif(sys.version_info.major < 3,
|
||||
reason="unsupported on Python 2.x"),
|
||||
'gc_collect': gc_collect
|
||||
}
|
||||
pytest.suppress = suppress
|
||||
pytest.requires_numpy = skipif(not np, reason="numpy is not installed")
|
||||
pytest.requires_scipy = skipif(not np, reason="scipy is not installed")
|
||||
pytest.requires_eigen_and_numpy = skipif(not have_eigen or not np,
|
||||
reason="eigen and/or numpy are not installed")
|
||||
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.unsupported_on_py2 = skipif(sys.version_info.major < 3,
|
||||
reason="unsupported on Python 2.x")
|
||||
pytest.gc_collect = gc_collect
|
||||
|
||||
|
||||
def _test_import_pybind11():
|
||||
|
||||
@@ -180,7 +180,7 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::out_of_range) {}
|
||||
catch (const std::out_of_range &) {}
|
||||
if (!t1) throw std::runtime_error("Unknown class passed to ConstructorStats::get()");
|
||||
auto &cs1 = get(*t1);
|
||||
// If we have both a t1 and t2 match, one is probably the trampoline class; return whichever
|
||||
|
||||
73
ext/pybind11/tests/cross_module_gil_utils.cpp
Normal file
73
ext/pybind11/tests/cross_module_gil_utils.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
tests/cross_module_gil_utils.cpp -- tools for acquiring GIL from a different module
|
||||
|
||||
Copyright (c) 2019 Google LLC
|
||||
|
||||
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/pybind11.h>
|
||||
#include <cstdint>
|
||||
|
||||
// This file mimics a DSO that makes pybind11 calls but does not define a
|
||||
// PYBIND11_MODULE. The purpose is to test that such a DSO can create a
|
||||
// py::gil_scoped_acquire when the running thread is in a GIL-released state.
|
||||
//
|
||||
// Note that we define a Python module here for convenience, but in general
|
||||
// this need not be the case. The typical scenario would be a DSO that implements
|
||||
// shared logic used internally by multiple pybind11 modules.
|
||||
|
||||
namespace {
|
||||
|
||||
namespace py = pybind11;
|
||||
void gil_acquire() { py::gil_scoped_acquire gil; }
|
||||
|
||||
constexpr char kModuleName[] = "cross_module_gil_utils";
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
kModuleName,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
#else
|
||||
PyMethodDef module_methods[] = {
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" PYBIND11_EXPORT
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyObject* PyInit_cross_module_gil_utils()
|
||||
#else
|
||||
void initcross_module_gil_utils()
|
||||
#endif
|
||||
{
|
||||
|
||||
PyObject* m =
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyModule_Create(&moduledef);
|
||||
#else
|
||||
Py_InitModule(kModuleName, module_methods);
|
||||
#endif
|
||||
|
||||
if (m != NULL) {
|
||||
static_assert(
|
||||
sizeof(&gil_acquire) == sizeof(void*),
|
||||
"Function pointer must have the same size as void*");
|
||||
PyModule_AddObject(m, "gil_acquire_funcaddr",
|
||||
PyLong_FromVoidPtr(reinterpret_cast<void*>(&gil_acquire)));
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return m;
|
||||
#endif
|
||||
}
|
||||
@@ -13,3 +13,4 @@ filterwarnings =
|
||||
ignore::ImportWarning
|
||||
# bogus numpy ABI warning (see numpy/#432)
|
||||
ignore:.*numpy.dtype size changed.*:RuntimeWarning
|
||||
ignore:.*numpy.ufunc size changed.*:RuntimeWarning
|
||||
|
||||
26
ext/pybind11/tests/test_async.cpp
Normal file
26
ext/pybind11/tests/test_async.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
tests/test_async.cpp -- __await__ support
|
||||
|
||||
Copyright (c) 2019 Google Inc.
|
||||
|
||||
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"
|
||||
|
||||
TEST_SUBMODULE(async_module, m) {
|
||||
struct DoesNotSupportAsync {};
|
||||
py::class_<DoesNotSupportAsync>(m, "DoesNotSupportAsync")
|
||||
.def(py::init<>());
|
||||
struct SupportsAsync {};
|
||||
py::class_<SupportsAsync>(m, "SupportsAsync")
|
||||
.def(py::init<>())
|
||||
.def("__await__", [](const SupportsAsync& self) -> py::object {
|
||||
static_cast<void>(self);
|
||||
py::object loop = py::module::import("asyncio.events").attr("get_event_loop")();
|
||||
py::object f = loop.attr("create_future")();
|
||||
f.attr("set_result")(5);
|
||||
return f.attr("__await__")();
|
||||
});
|
||||
}
|
||||
23
ext/pybind11/tests/test_async.py
Normal file
23
ext/pybind11/tests/test_async.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import asyncio
|
||||
import pytest
|
||||
from pybind11_tests import async_module as m
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def event_loop():
|
||||
loop = asyncio.new_event_loop()
|
||||
yield loop
|
||||
loop.close()
|
||||
|
||||
|
||||
async def get_await_result(x):
|
||||
return await x
|
||||
|
||||
|
||||
def test_await(event_loop):
|
||||
assert 5 == event_loop.run_until_complete(get_await_result(m.SupportsAsync()))
|
||||
|
||||
|
||||
def test_await_missing(event_loop):
|
||||
with pytest.raises(TypeError):
|
||||
event_loop.run_until_complete(get_await_result(m.DoesNotSupportAsync()))
|
||||
@@ -78,7 +78,7 @@ TEST_SUBMODULE(buffers, m) {
|
||||
py::class_<Matrix>(m, "Matrix", py::buffer_protocol())
|
||||
.def(py::init<ssize_t, ssize_t>())
|
||||
/// Construct from a buffer
|
||||
.def(py::init([](py::buffer b) {
|
||||
.def(py::init([](py::buffer const b) {
|
||||
py::buffer_info info = b.request();
|
||||
if (info.format != py::format_descriptor<float>::format() || info.ndim != 2)
|
||||
throw std::runtime_error("Incompatible buffer format!");
|
||||
@@ -107,7 +107,7 @@ TEST_SUBMODULE(buffers, m) {
|
||||
return py::buffer_info(
|
||||
m.data(), /* Pointer to buffer */
|
||||
{ m.rows(), m.cols() }, /* Buffer dimensions */
|
||||
{ sizeof(float) * size_t(m.rows()), /* Strides (in bytes) for each index */
|
||||
{ sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */
|
||||
sizeof(float) }
|
||||
);
|
||||
})
|
||||
|
||||
@@ -36,17 +36,21 @@ def test_from_python():
|
||||
# https://bitbucket.org/pypy/pypy/issues/2444
|
||||
@pytest.unsupported_on_pypy
|
||||
def test_to_python():
|
||||
mat = m.Matrix(5, 5)
|
||||
assert memoryview(mat).shape == (5, 5)
|
||||
mat = m.Matrix(5, 4)
|
||||
assert memoryview(mat).shape == (5, 4)
|
||||
|
||||
assert mat[2, 3] == 0
|
||||
mat[2, 3] = 4
|
||||
mat[2, 3] = 4.0
|
||||
mat[3, 2] = 7.0
|
||||
assert mat[2, 3] == 4
|
||||
assert mat[3, 2] == 7
|
||||
assert struct.unpack_from('f', mat, (3 * 4 + 2) * 4) == (7, )
|
||||
assert struct.unpack_from('f', mat, (2 * 4 + 3) * 4) == (4, )
|
||||
|
||||
mat2 = np.array(mat, copy=False)
|
||||
assert mat2.shape == (5, 5)
|
||||
assert abs(mat2).sum() == 4
|
||||
assert mat2[2, 3] == 4
|
||||
assert mat2.shape == (5, 4)
|
||||
assert abs(mat2).sum() == 11
|
||||
assert mat2[2, 3] == 4 and mat2[3, 2] == 7
|
||||
mat2[2, 3] = 5
|
||||
assert mat2[2, 3] == 5
|
||||
|
||||
@@ -58,7 +62,7 @@ def test_to_python():
|
||||
del mat2 # holds a mat reference
|
||||
pytest.gc_collect()
|
||||
assert cstats.alive() == 0
|
||||
assert cstats.values() == ["5x5 matrix"]
|
||||
assert cstats.values() == ["5x4 matrix"]
|
||||
assert cstats.copy_constructions == 0
|
||||
# assert cstats.move_constructions >= 0 # Don't invoke any
|
||||
assert cstats.copy_assignments == 0
|
||||
|
||||
@@ -50,7 +50,9 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
// test_single_char_arguments
|
||||
m.attr("wchar_size") = py::cast(sizeof(wchar_t));
|
||||
m.def("ord_char", [](char c) -> int { return static_cast<unsigned char>(c); });
|
||||
m.def("ord_char_lv", [](char &c) -> int { return static_cast<unsigned char>(c); });
|
||||
m.def("ord_char16", [](char16_t c) -> uint16_t { return c; });
|
||||
m.def("ord_char16_lv", [](char16_t &c) -> uint16_t { return c; });
|
||||
m.def("ord_char32", [](char32_t c) -> uint32_t { return c; });
|
||||
m.def("ord_wchar", [](wchar_t c) -> int { return c; });
|
||||
|
||||
@@ -153,4 +155,16 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
// test_complex
|
||||
m.def("complex_cast", [](float x) { return "{}"_s.format(x); });
|
||||
m.def("complex_cast", [](std::complex<float> x) { return "({}, {})"_s.format(x.real(), x.imag()); });
|
||||
|
||||
// test int vs. long (Python 2)
|
||||
m.def("int_cast", []() {return (int) 42;});
|
||||
m.def("long_cast", []() {return (long) 42;});
|
||||
m.def("longlong_cast", []() {return ULLONG_MAX;});
|
||||
|
||||
/// test void* cast operator
|
||||
m.def("test_void_caster", []() -> bool {
|
||||
void *v = (void *) 0xabcd;
|
||||
py::object o = py::cast(v);
|
||||
return py::cast<void *>(o) == v;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ def test_single_char_arguments():
|
||||
toolong_message = "Expected a character, but multi-character string found"
|
||||
|
||||
assert m.ord_char(u'a') == 0x61 # simple ASCII
|
||||
assert m.ord_char_lv(u'b') == 0x62
|
||||
assert m.ord_char(u'é') == 0xE9 # requires 2 bytes in utf-8, but can be stuffed in a char
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char(u'Ā') == 0x100 # requires 2 bytes, doesn't fit in a char
|
||||
@@ -54,9 +55,11 @@ def test_single_char_arguments():
|
||||
|
||||
assert m.ord_char16(u'a') == 0x61
|
||||
assert m.ord_char16(u'é') == 0xE9
|
||||
assert m.ord_char16_lv(u'ê') == 0xEA
|
||||
assert m.ord_char16(u'Ā') == 0x100
|
||||
assert m.ord_char16(u'‽') == 0x203d
|
||||
assert m.ord_char16(u'♥') == 0x2665
|
||||
assert m.ord_char16_lv(u'♡') == 0x2661
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char16(u'🎂') == 0x1F382 # requires surrogate pair
|
||||
assert str(excinfo.value) == toobig_message(0x10000)
|
||||
@@ -320,3 +323,20 @@ def test_numpy_bool():
|
||||
assert convert(np.bool_(False)) is False
|
||||
assert noconvert(np.bool_(True)) is True
|
||||
assert noconvert(np.bool_(False)) is False
|
||||
|
||||
|
||||
def test_int_long():
|
||||
"""In Python 2, a C++ int should return a Python int rather than long
|
||||
if possible: longs are not always accepted where ints are used (such
|
||||
as the argument to sys.exit()). A C++ long long is always a Python
|
||||
long."""
|
||||
|
||||
import sys
|
||||
must_be_long = type(getattr(sys, 'maxint', 1) + 1)
|
||||
assert isinstance(m.int_cast(), int)
|
||||
assert isinstance(m.long_cast(), int)
|
||||
assert isinstance(m.longlong_cast(), must_be_long)
|
||||
|
||||
|
||||
def test_void_caster_2():
|
||||
assert m.test_void_caster()
|
||||
|
||||
@@ -36,6 +36,8 @@ TEST_SUBMODULE(call_policies, m) {
|
||||
class Child {
|
||||
public:
|
||||
Child() { py::print("Allocating child."); }
|
||||
Child(const Child &) = default;
|
||||
Child(Child &&) = default;
|
||||
~Child() { py::print("Releasing child."); }
|
||||
};
|
||||
py::class_<Child>(m, "Child")
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
#include <pybind11/functional.h>
|
||||
#include <thread>
|
||||
|
||||
|
||||
int dummy_function(int i) { return i + 1; }
|
||||
@@ -146,4 +147,22 @@ TEST_SUBMODULE(callbacks, m) {
|
||||
py::class_<CppBoundMethodTest>(m, "CppBoundMethodTest")
|
||||
.def(py::init<>())
|
||||
.def("triple", [](CppBoundMethodTest &, int val) { return 3 * val; });
|
||||
|
||||
// test async Python callbacks
|
||||
using callback_f = std::function<void(int)>;
|
||||
m.def("test_async_callback", [](callback_f f, py::list work) {
|
||||
// make detached thread that calls `f` with piece of work after a little delay
|
||||
auto start_f = [f](int j) {
|
||||
auto invoke_f = [f, j] {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
f(j);
|
||||
};
|
||||
auto t = std::thread(std::move(invoke_f));
|
||||
t.detach();
|
||||
};
|
||||
|
||||
// spawn worker threads
|
||||
for (auto i : work)
|
||||
start_f(py::cast<int>(i));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import pytest
|
||||
from pybind11_tests import callbacks as m
|
||||
from threading import Thread
|
||||
|
||||
|
||||
def test_callbacks():
|
||||
@@ -105,3 +106,31 @@ def test_function_signatures(doc):
|
||||
|
||||
def test_movable_object():
|
||||
assert m.callback_with_movable(lambda _: None) is True
|
||||
|
||||
|
||||
def test_async_callbacks():
|
||||
# serves as state for async callback
|
||||
class Item:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
res = []
|
||||
|
||||
# generate stateful lambda that will store result in `res`
|
||||
def gen_f():
|
||||
s = Item(3)
|
||||
return lambda j: res.append(s.value + j)
|
||||
|
||||
# do some work async
|
||||
work = [1, 2, 3, 4]
|
||||
m.test_async_callback(gen_f(), work)
|
||||
# wait until work is done
|
||||
from time import sleep
|
||||
sleep(0.5)
|
||||
assert sum(res) == sum([x + 3 for x in work])
|
||||
|
||||
|
||||
def test_async_async_callbacks():
|
||||
t = Thread(target=test_async_callbacks)
|
||||
t.start()
|
||||
t.join()
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
TEST_SUBMODULE(chrono, m) {
|
||||
using system_time = std::chrono::system_clock::time_point;
|
||||
using steady_time = std::chrono::steady_clock::time_point;
|
||||
|
||||
using timespan = std::chrono::duration<int64_t, std::nano>;
|
||||
using timestamp = std::chrono::time_point<std::chrono::system_clock, timespan>;
|
||||
|
||||
// test_chrono_system_clock
|
||||
// Return the current time off the wall clock
|
||||
m.def("test_chrono1", []() { return std::chrono::system_clock::now(); });
|
||||
@@ -44,4 +48,8 @@ TEST_SUBMODULE(chrono, m) {
|
||||
// Float durations (issue #719)
|
||||
m.def("test_chrono_float_diff", [](std::chrono::duration<float> a, std::chrono::duration<float> b) {
|
||||
return a - b; });
|
||||
|
||||
m.def("test_nano_timepoint", [](timestamp start, timespan delta) -> timestamp {
|
||||
return start + delta;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -40,6 +40,62 @@ def test_chrono_system_clock_roundtrip():
|
||||
assert diff.microseconds == 0
|
||||
|
||||
|
||||
def test_chrono_system_clock_roundtrip_date():
|
||||
date1 = datetime.date.today()
|
||||
|
||||
# Roundtrip the time
|
||||
datetime2 = m.test_chrono2(date1)
|
||||
date2 = datetime2.date()
|
||||
time2 = datetime2.time()
|
||||
|
||||
# The returned value should be a datetime
|
||||
assert isinstance(datetime2, datetime.datetime)
|
||||
assert isinstance(date2, datetime.date)
|
||||
assert isinstance(time2, datetime.time)
|
||||
|
||||
# They should be identical (no information lost on roundtrip)
|
||||
diff = abs(date1 - date2)
|
||||
assert diff.days == 0
|
||||
assert diff.seconds == 0
|
||||
assert diff.microseconds == 0
|
||||
|
||||
# Year, Month & Day should be the same after the round trip
|
||||
assert date1.year == date2.year
|
||||
assert date1.month == date2.month
|
||||
assert date1.day == date2.day
|
||||
|
||||
# There should be no time information
|
||||
assert time2.hour == 0
|
||||
assert time2.minute == 0
|
||||
assert time2.second == 0
|
||||
assert time2.microsecond == 0
|
||||
|
||||
|
||||
def test_chrono_system_clock_roundtrip_time():
|
||||
time1 = datetime.datetime.today().time()
|
||||
|
||||
# Roundtrip the time
|
||||
datetime2 = m.test_chrono2(time1)
|
||||
date2 = datetime2.date()
|
||||
time2 = datetime2.time()
|
||||
|
||||
# The returned value should be a datetime
|
||||
assert isinstance(datetime2, datetime.datetime)
|
||||
assert isinstance(date2, datetime.date)
|
||||
assert isinstance(time2, datetime.time)
|
||||
|
||||
# Hour, Minute, Second & Microsecond should be the same after the round trip
|
||||
assert time1.hour == time2.hour
|
||||
assert time1.minute == time2.minute
|
||||
assert time1.second == time2.second
|
||||
assert time1.microsecond == time2.microsecond
|
||||
|
||||
# There should be no date information (i.e. date = python base date)
|
||||
assert date2.year == 1970
|
||||
assert date2.month == 1
|
||||
assert date2.day == 1
|
||||
|
||||
|
||||
def test_chrono_duration_roundtrip():
|
||||
|
||||
# Get the difference between two times (a timedelta)
|
||||
@@ -70,6 +126,19 @@ def test_chrono_duration_subtraction_equivalence():
|
||||
assert cpp_diff.microseconds == diff.microseconds
|
||||
|
||||
|
||||
def test_chrono_duration_subtraction_equivalence_date():
|
||||
|
||||
date1 = datetime.date.today()
|
||||
date2 = datetime.date.today()
|
||||
|
||||
diff = date2 - date1
|
||||
cpp_diff = m.test_chrono4(date2, date1)
|
||||
|
||||
assert cpp_diff.days == diff.days
|
||||
assert cpp_diff.seconds == diff.seconds
|
||||
assert cpp_diff.microseconds == diff.microseconds
|
||||
|
||||
|
||||
def test_chrono_steady_clock():
|
||||
time1 = m.test_chrono5()
|
||||
assert isinstance(time1, datetime.timedelta)
|
||||
@@ -99,3 +168,9 @@ def test_floating_point_duration():
|
||||
diff = m.test_chrono_float_diff(43.789012, 1.123456)
|
||||
assert diff.seconds == 42
|
||||
assert 665556 <= diff.microseconds <= 665557
|
||||
|
||||
|
||||
def test_nano_timepoint():
|
||||
time = datetime.datetime.now()
|
||||
time1 = m.test_nano_timepoint(time, datetime.timedelta(seconds=60))
|
||||
assert(time1 == time + datetime.timedelta(seconds=60))
|
||||
|
||||
@@ -10,10 +10,27 @@
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
#include "local_bindings.h"
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(disable: 4324) // warning C4324: structure was padded due to alignment specifier
|
||||
#endif
|
||||
|
||||
// test_brace_initialization
|
||||
struct NoBraceInitialization {
|
||||
NoBraceInitialization(std::vector<int> v) : vec{std::move(v)} {}
|
||||
template <typename T>
|
||||
NoBraceInitialization(std::initializer_list<T> l) : vec(l) {}
|
||||
|
||||
std::vector<int> vec;
|
||||
};
|
||||
|
||||
TEST_SUBMODULE(class_, m) {
|
||||
// test_instance
|
||||
struct NoConstructor {
|
||||
NoConstructor() = default;
|
||||
NoConstructor(const NoConstructor &) = default;
|
||||
NoConstructor(NoConstructor &&) = default;
|
||||
static NoConstructor *new_instance() {
|
||||
auto *ptr = new NoConstructor();
|
||||
print_created(ptr, "via new_instance");
|
||||
@@ -82,7 +99,12 @@ TEST_SUBMODULE(class_, m) {
|
||||
m.def("dog_bark", [](const Dog &dog) { return dog.bark(); });
|
||||
|
||||
// test_automatic_upcasting
|
||||
struct BaseClass { virtual ~BaseClass() {} };
|
||||
struct BaseClass {
|
||||
BaseClass() = default;
|
||||
BaseClass(const BaseClass &) = default;
|
||||
BaseClass(BaseClass &&) = default;
|
||||
virtual ~BaseClass() {}
|
||||
};
|
||||
struct DerivedClass1 : BaseClass { };
|
||||
struct DerivedClass2 : BaseClass { };
|
||||
|
||||
@@ -291,6 +313,12 @@ TEST_SUBMODULE(class_, m) {
|
||||
.def(py::init<int, const std::string &>())
|
||||
.def_readwrite("field1", &BraceInitialization::field1)
|
||||
.def_readwrite("field2", &BraceInitialization::field2);
|
||||
// We *don't* want to construct using braces when the given constructor argument maps to a
|
||||
// constructor, because brace initialization could go to the wrong place (in particular when
|
||||
// there is also an `initializer_list<T>`-accept constructor):
|
||||
py::class_<NoBraceInitialization>(m, "NoBraceInitialization")
|
||||
.def(py::init<std::vector<int>>())
|
||||
.def_readonly("vec", &NoBraceInitialization::vec);
|
||||
|
||||
// test_reentrant_implicit_conversion_failure
|
||||
// #1035: issue with runaway reentrant implicit conversion
|
||||
@@ -302,6 +330,43 @@ TEST_SUBMODULE(class_, m) {
|
||||
.def(py::init<const BogusImplicitConversion &>());
|
||||
|
||||
py::implicitly_convertible<int, BogusImplicitConversion>();
|
||||
|
||||
// test_qualname
|
||||
// #1166: nested class docstring doesn't show nested name
|
||||
// Also related: tests that __qualname__ is set properly
|
||||
struct NestBase {};
|
||||
struct Nested {};
|
||||
py::class_<NestBase> base(m, "NestBase");
|
||||
base.def(py::init<>());
|
||||
py::class_<Nested>(base, "Nested")
|
||||
.def(py::init<>())
|
||||
.def("fn", [](Nested &, int, NestBase &, Nested &) {})
|
||||
.def("fa", [](Nested &, int, NestBase &, Nested &) {},
|
||||
"a"_a, "b"_a, "c"_a);
|
||||
base.def("g", [](NestBase &, Nested &) {});
|
||||
base.def("h", []() { return NestBase(); });
|
||||
|
||||
// test_error_after_conversion
|
||||
// The second-pass path through dispatcher() previously didn't
|
||||
// remember which overload was used, and would crash trying to
|
||||
// generate a useful error message
|
||||
|
||||
struct NotRegistered {};
|
||||
struct StringWrapper { std::string str; };
|
||||
m.def("test_error_after_conversions", [](int) {});
|
||||
m.def("test_error_after_conversions",
|
||||
[](StringWrapper) -> NotRegistered { return {}; });
|
||||
py::class_<StringWrapper>(m, "StringWrapper").def(py::init<std::string>());
|
||||
py::implicitly_convertible<std::string, StringWrapper>();
|
||||
|
||||
#if defined(PYBIND11_CPP17)
|
||||
struct alignas(1024) Aligned {
|
||||
std::uintptr_t ptr() const { return (uintptr_t) this; }
|
||||
};
|
||||
py::class_<Aligned>(m, "Aligned")
|
||||
.def(py::init<>())
|
||||
.def("ptr", &Aligned::ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <int N> class BreaksBase { public: virtual ~BreaksBase() = default; };
|
||||
|
||||
@@ -44,6 +44,31 @@ def test_docstrings(doc):
|
||||
"""
|
||||
|
||||
|
||||
def test_qualname(doc):
|
||||
"""Tests that a properly qualified name is set in __qualname__ (even in pre-3.3, where we
|
||||
backport the attribute) and that generated docstrings properly use it and the module name"""
|
||||
assert m.NestBase.__qualname__ == "NestBase"
|
||||
assert m.NestBase.Nested.__qualname__ == "NestBase.Nested"
|
||||
|
||||
assert doc(m.NestBase.__init__) == """
|
||||
__init__(self: m.class_.NestBase) -> None
|
||||
"""
|
||||
assert doc(m.NestBase.g) == """
|
||||
g(self: m.class_.NestBase, arg0: m.class_.NestBase.Nested) -> None
|
||||
"""
|
||||
assert doc(m.NestBase.Nested.__init__) == """
|
||||
__init__(self: m.class_.NestBase.Nested) -> None
|
||||
"""
|
||||
assert doc(m.NestBase.Nested.fn) == """
|
||||
fn(self: m.class_.NestBase.Nested, arg0: int, arg1: m.class_.NestBase, arg2: m.class_.NestBase.Nested) -> None
|
||||
""" # noqa: E501 line too long
|
||||
assert doc(m.NestBase.Nested.fa) == """
|
||||
fa(self: m.class_.NestBase.Nested, a: int, b: m.class_.NestBase, c: m.class_.NestBase.Nested) -> None
|
||||
""" # noqa: E501 line too long
|
||||
assert m.NestBase.__module__ == "pybind11_tests.class_"
|
||||
assert m.NestBase.Nested.__module__ == "pybind11_tests.class_"
|
||||
|
||||
|
||||
def test_inheritance(msg):
|
||||
roger = m.Rabbit('Rabbit')
|
||||
assert roger.name() + " is a " + roger.species() == "Rabbit is a parrot"
|
||||
@@ -203,6 +228,12 @@ def test_brace_initialization():
|
||||
assert a.field1 == 123
|
||||
assert a.field2 == "test"
|
||||
|
||||
# Tests that a non-simple class doesn't get brace initialization (if the
|
||||
# class defines an initializer_list constructor, in particular, it would
|
||||
# win over the expected constructor).
|
||||
b = m.NoBraceInitialization([123, 456])
|
||||
assert b.vec == [123, 456]
|
||||
|
||||
|
||||
@pytest.unsupported_on_pypy
|
||||
def test_class_refcount():
|
||||
@@ -229,7 +260,22 @@ def test_reentrant_implicit_conversion_failure(msg):
|
||||
# ensure that there is no runaway reentrant implicit conversion (#1035)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.BogusImplicitConversion(0)
|
||||
assert msg(excinfo.value) == '''__init__(): incompatible constructor arguments. The following argument types are supported:
|
||||
1. m.class_.BogusImplicitConversion(arg0: m.class_.BogusImplicitConversion)
|
||||
assert msg(excinfo.value) == '''
|
||||
__init__(): incompatible constructor arguments. The following argument types are supported:
|
||||
1. m.class_.BogusImplicitConversion(arg0: m.class_.BogusImplicitConversion)
|
||||
|
||||
Invoked with: 0'''
|
||||
Invoked with: 0
|
||||
'''
|
||||
|
||||
|
||||
def test_error_after_conversions():
|
||||
with pytest.raises(TypeError) as exc_info:
|
||||
m.test_error_after_conversions("hello")
|
||||
assert str(exc_info.value).startswith(
|
||||
"Unable to convert function return value to a Python type!")
|
||||
|
||||
|
||||
def test_aligned():
|
||||
if hasattr(m, "Aligned"):
|
||||
p = m.Aligned().ptr()
|
||||
assert p % 1024 == 0
|
||||
|
||||
@@ -49,7 +49,14 @@ namespace test_exc_sp {
|
||||
int f1(int x) noexcept { return x+1; }
|
||||
int f2(int x) noexcept(true) { return x+2; }
|
||||
int f3(int x) noexcept(false) { return x+3; }
|
||||
#if defined(__GNUG__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated"
|
||||
#endif
|
||||
int f4(int x) throw() { return x+4; } // Deprecated equivalent to noexcept(true)
|
||||
#if defined(__GNUG__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
struct C {
|
||||
int m1(int x) noexcept { return x-1; }
|
||||
int m2(int x) const noexcept { return x-2; }
|
||||
@@ -57,8 +64,15 @@ struct C {
|
||||
int m4(int x) const noexcept(true) { return x-4; }
|
||||
int m5(int x) noexcept(false) { return x-5; }
|
||||
int m6(int x) const noexcept(false) { return x-6; }
|
||||
#if defined(__GNUG__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated"
|
||||
#endif
|
||||
int m7(int x) throw() { return x-7; }
|
||||
int m8(int x) const throw() { return x-8; }
|
||||
#if defined(__GNUG__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ template <> struct type_caster<CopyOnlyInt> {
|
||||
protected:
|
||||
CopyOnlyInt value;
|
||||
public:
|
||||
static PYBIND11_DESCR name() { return _("CopyOnlyInt"); }
|
||||
static constexpr auto name = _("CopyOnlyInt");
|
||||
bool load(handle src, bool) { value = CopyOnlyInt(src.cast<int>()); return true; }
|
||||
static handle cast(const CopyOnlyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); }
|
||||
static handle cast(const CopyOnlyInt *src, return_value_policy policy, handle parent) {
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
#include "constructor_stats.h"
|
||||
#include <pybind11/eigen.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(disable: 4996) // C4996: std::unary_negation is deprecated
|
||||
#endif
|
||||
|
||||
#include <Eigen/Cholesky>
|
||||
|
||||
using MatrixXdR = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
|
||||
@@ -119,7 +124,7 @@ TEST_SUBMODULE(eigen, m) {
|
||||
// This one accepts a matrix of any stride:
|
||||
m.def("add_any", [](py::EigenDRef<Eigen::MatrixXd> x, int r, int c, double v) { x(r,c) += v; });
|
||||
|
||||
// Return mutable references (numpy maps into eigen varibles)
|
||||
// Return mutable references (numpy maps into eigen variables)
|
||||
m.def("get_cm_ref", []() { return Eigen::Ref<Eigen::MatrixXd>(get_cm()); });
|
||||
m.def("get_rm_ref", []() { return Eigen::Ref<MatrixXdR>(get_rm()); });
|
||||
// The same references, but non-mutable (numpy maps into eigen variables, but is !writeable)
|
||||
@@ -288,6 +293,13 @@ TEST_SUBMODULE(eigen, m) {
|
||||
m.def("iss738_f1", &adjust_matrix<const Eigen::Ref<const Eigen::MatrixXd> &>, py::arg().noconvert());
|
||||
m.def("iss738_f2", &adjust_matrix<const Eigen::Ref<const Eigen::Matrix<double, -1, -1, Eigen::RowMajor>> &>, py::arg().noconvert());
|
||||
|
||||
// test_issue1105
|
||||
// Issue #1105: when converting from a numpy two-dimensional (Nx1) or (1xN) value into a dense
|
||||
// eigen Vector or RowVector, the argument would fail to load because the numpy copy would fail:
|
||||
// numpy won't broadcast a Nx1 into a 1-dimensional vector.
|
||||
m.def("iss1105_col", [](Eigen::VectorXd) { return true; });
|
||||
m.def("iss1105_row", [](Eigen::RowVectorXd) { return true; });
|
||||
|
||||
// test_named_arguments
|
||||
// Make sure named arguments are working properly:
|
||||
m.def("matrix_multiply", [](const py::EigenDRef<const Eigen::MatrixXd> A, const py::EigenDRef<const Eigen::MatrixXd> B)
|
||||
|
||||
@@ -19,7 +19,7 @@ def assert_equal_ref(mat):
|
||||
|
||||
|
||||
def assert_sparse_equal_ref(sparse_mat):
|
||||
assert_equal_ref(sparse_mat.todense())
|
||||
assert_equal_ref(sparse_mat.toarray())
|
||||
|
||||
|
||||
def test_fixed():
|
||||
@@ -181,8 +181,7 @@ def test_negative_stride_from_python(msg):
|
||||
double_threer(): incompatible function arguments. The following argument types are supported:
|
||||
1. (arg0: numpy.ndarray[float32[1, 3], flags.writeable]) -> None
|
||||
|
||||
Invoked with: array([ 5., 4., 3.], dtype=float32)
|
||||
""" # noqa: E501 line too long
|
||||
Invoked with: """ + repr(np.array([ 5., 4., 3.], dtype='float32')) # noqa: E501 line too long
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.double_threec(second_col)
|
||||
@@ -190,8 +189,7 @@ def test_negative_stride_from_python(msg):
|
||||
double_threec(): incompatible function arguments. The following argument types are supported:
|
||||
1. (arg0: numpy.ndarray[float32[3, 1], flags.writeable]) -> None
|
||||
|
||||
Invoked with: array([ 7., 4., 1.], dtype=float32)
|
||||
""" # noqa: E501 line too long
|
||||
Invoked with: """ + repr(np.array([ 7., 4., 1.], dtype='float32')) # noqa: E501 line too long
|
||||
|
||||
|
||||
def test_nonunit_stride_to_python():
|
||||
@@ -672,6 +670,21 @@ def test_issue738():
|
||||
assert np.all(m.iss738_f2(np.array([[1.], [2], [3]])) == np.array([[1.], [12], [23]]))
|
||||
|
||||
|
||||
def test_issue1105():
|
||||
"""Issue 1105: 1xN or Nx1 input arrays weren't accepted for eigen
|
||||
compile-time row vectors or column vector"""
|
||||
assert m.iss1105_row(np.ones((1, 7)))
|
||||
assert m.iss1105_col(np.ones((7, 1)))
|
||||
|
||||
# These should still fail (incompatible dimensions):
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.iss1105_row(np.ones((7, 1)))
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.iss1105_col(np.ones((1, 7)))
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_custom_operator_new():
|
||||
"""Using Eigen types as member variables requires a class-specific
|
||||
operator new with proper alignment"""
|
||||
|
||||
@@ -5,7 +5,9 @@ if(${PYTHON_MODULE_EXTENSION} MATCHES "pypy")
|
||||
endif()
|
||||
|
||||
find_package(Catch 1.9.3)
|
||||
if(NOT CATCH_FOUND)
|
||||
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.")
|
||||
return()
|
||||
@@ -31,4 +33,9 @@ target_link_libraries(test_embed PUBLIC ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
add_custom_target(cpptest COMMAND $<TARGET_FILE:test_embed>
|
||||
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})
|
||||
add_dependencies(cpptest external_module)
|
||||
|
||||
add_dependencies(check cpptest)
|
||||
|
||||
@@ -3,12 +3,18 @@
|
||||
|
||||
#include <pybind11/embed.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to catch
|
||||
// 2.0.1; this should be fixed in the next catch release after 2.0.1).
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
|
||||
#define CATCH_CONFIG_RUNNER
|
||||
#include <catch.hpp>
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
int main(int argc, char *argv[]) {
|
||||
py::scoped_interpreter guard{};
|
||||
auto result = Catch::Session().run(argc, argv);
|
||||
|
||||
|
||||
23
ext/pybind11/tests/test_embed/external_module.cpp
Normal file
23
ext/pybind11/tests/test_embed/external_module.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
/* Simple test module/test class to check that the referenced internals data of external pybind11
|
||||
* modules aren't preserved over a finalize/initialize.
|
||||
*/
|
||||
|
||||
PYBIND11_MODULE(external_module, m) {
|
||||
class A {
|
||||
public:
|
||||
A(int value) : v{value} {};
|
||||
int v;
|
||||
};
|
||||
|
||||
py::class_<A>(m, "A")
|
||||
.def(py::init<int>())
|
||||
.def_readwrite("value", &A::v);
|
||||
|
||||
m.def("internals_at", []() {
|
||||
return reinterpret_cast<uintptr_t>(&py::detail::get_internals());
|
||||
});
|
||||
}
|
||||
@@ -1,4 +1,11 @@
|
||||
#include <pybind11/embed.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to catch
|
||||
// 2.0.1; this should be fixed in the next catch release after 2.0.1).
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <thread>
|
||||
@@ -94,7 +101,8 @@ bool has_pybind11_internals_builtin() {
|
||||
};
|
||||
|
||||
bool has_pybind11_internals_static() {
|
||||
return py::detail::get_internals_ptr() != nullptr;
|
||||
auto **&ipp = py::detail::get_internals_pp();
|
||||
return ipp && *ipp;
|
||||
}
|
||||
|
||||
TEST_CASE("Restart the interpreter") {
|
||||
@@ -102,6 +110,11 @@ TEST_CASE("Restart the interpreter") {
|
||||
REQUIRE(py::module::import("widget_module").attr("add")(1, 2).cast<int>() == 3);
|
||||
REQUIRE(has_pybind11_internals_builtin());
|
||||
REQUIRE(has_pybind11_internals_static());
|
||||
REQUIRE(py::module::import("external_module").attr("A")(123).attr("value").cast<int>() == 123);
|
||||
|
||||
// local and foreign module internals should point to the same internals:
|
||||
REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp()) ==
|
||||
py::module::import("external_module").attr("internals_at")().cast<uintptr_t>());
|
||||
|
||||
// Restart the interpreter.
|
||||
py::finalize_interpreter();
|
||||
@@ -116,6 +129,8 @@ TEST_CASE("Restart the interpreter") {
|
||||
pybind11::detail::get_internals();
|
||||
REQUIRE(has_pybind11_internals_builtin());
|
||||
REQUIRE(has_pybind11_internals_static());
|
||||
REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp()) ==
|
||||
py::module::import("external_module").attr("internals_at")().cast<uintptr_t>());
|
||||
|
||||
// Make sure that an interpreter with no get_internals() created until finalize still gets the
|
||||
// internals destroyed
|
||||
|
||||
@@ -13,11 +13,13 @@ TEST_SUBMODULE(enums, m) {
|
||||
// test_unscoped_enum
|
||||
enum UnscopedEnum {
|
||||
EOne = 1,
|
||||
ETwo
|
||||
ETwo,
|
||||
EThree
|
||||
};
|
||||
py::enum_<UnscopedEnum>(m, "UnscopedEnum", py::arithmetic())
|
||||
.value("EOne", EOne)
|
||||
.value("ETwo", ETwo)
|
||||
py::enum_<UnscopedEnum>(m, "UnscopedEnum", py::arithmetic(), "An unscoped enumeration")
|
||||
.value("EOne", EOne, "Docstring for EOne")
|
||||
.value("ETwo", ETwo, "Docstring for ETwo")
|
||||
.value("EThree", EThree, "Docstring for EThree")
|
||||
.export_values();
|
||||
|
||||
// test_scoped_enum
|
||||
@@ -68,4 +70,18 @@ TEST_SUBMODULE(enums, m) {
|
||||
m.def("test_enum_to_int", [](int) { });
|
||||
m.def("test_enum_to_uint", [](uint32_t) { });
|
||||
m.def("test_enum_to_long_long", [](long long) { });
|
||||
|
||||
// test_duplicate_enum_name
|
||||
enum SimpleEnum
|
||||
{
|
||||
ONE, TWO, THREE
|
||||
};
|
||||
|
||||
m.def("register_bad_enum", [m]() {
|
||||
py::enum_<SimpleEnum>(m, "SimpleEnum")
|
||||
.value("ONE", SimpleEnum::ONE) //NOTE: all value function calls are called with the same first parameter value
|
||||
.value("ONE", SimpleEnum::TWO)
|
||||
.value("ONE", SimpleEnum::THREE)
|
||||
.export_values();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,9 +6,22 @@ def test_unscoped_enum():
|
||||
assert str(m.UnscopedEnum.EOne) == "UnscopedEnum.EOne"
|
||||
assert str(m.UnscopedEnum.ETwo) == "UnscopedEnum.ETwo"
|
||||
assert str(m.EOne) == "UnscopedEnum.EOne"
|
||||
|
||||
# name property
|
||||
assert m.UnscopedEnum.EOne.name == "EOne"
|
||||
assert m.UnscopedEnum.ETwo.name == "ETwo"
|
||||
assert m.EOne.name == "EOne"
|
||||
# name readonly
|
||||
with pytest.raises(AttributeError):
|
||||
m.UnscopedEnum.EOne.name = ""
|
||||
# name returns a copy
|
||||
foo = m.UnscopedEnum.EOne.name
|
||||
foo = "bar"
|
||||
assert m.UnscopedEnum.EOne.name == "EOne"
|
||||
|
||||
# __members__ property
|
||||
assert m.UnscopedEnum.__members__ == \
|
||||
{"EOne": m.UnscopedEnum.EOne, "ETwo": m.UnscopedEnum.ETwo}
|
||||
{"EOne": m.UnscopedEnum.EOne, "ETwo": m.UnscopedEnum.ETwo, "EThree": m.UnscopedEnum.EThree}
|
||||
# __members__ readonly
|
||||
with pytest.raises(AttributeError):
|
||||
m.UnscopedEnum.__members__ = {}
|
||||
@@ -16,12 +29,57 @@ def test_unscoped_enum():
|
||||
foo = m.UnscopedEnum.__members__
|
||||
foo["bar"] = "baz"
|
||||
assert m.UnscopedEnum.__members__ == \
|
||||
{"EOne": m.UnscopedEnum.EOne, "ETwo": m.UnscopedEnum.ETwo}
|
||||
{"EOne": m.UnscopedEnum.EOne, "ETwo": m.UnscopedEnum.ETwo, "EThree": m.UnscopedEnum.EThree}
|
||||
|
||||
# no TypeError exception for unscoped enum ==/!= int comparisons
|
||||
for docstring_line in '''An unscoped enumeration
|
||||
|
||||
Members:
|
||||
|
||||
EOne : Docstring for EOne
|
||||
|
||||
ETwo : Docstring for ETwo
|
||||
|
||||
EThree : Docstring for EThree'''.split('\n'):
|
||||
assert docstring_line in m.UnscopedEnum.__doc__
|
||||
|
||||
# Unscoped enums will accept ==/!= int comparisons
|
||||
y = m.UnscopedEnum.ETwo
|
||||
assert y == 2
|
||||
assert 2 == y
|
||||
assert y != 3
|
||||
assert 3 != y
|
||||
# Compare with None
|
||||
assert (y != None) # noqa: E711
|
||||
assert not (y == None) # noqa: E711
|
||||
# Compare with an object
|
||||
assert (y != object())
|
||||
assert not (y == object())
|
||||
# Compare with string
|
||||
assert y != "2"
|
||||
assert "2" != y
|
||||
assert not ("2" == y)
|
||||
assert not (y == "2")
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
y < object()
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
y <= object()
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
y > object()
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
y >= object()
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
y | object()
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
y & object()
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
y ^ object()
|
||||
|
||||
assert int(m.UnscopedEnum.ETwo) == 2
|
||||
assert str(m.UnscopedEnum(2)) == "UnscopedEnum.ETwo"
|
||||
@@ -40,17 +98,37 @@ def test_unscoped_enum():
|
||||
assert not (m.UnscopedEnum.ETwo < m.UnscopedEnum.EOne)
|
||||
assert not (2 < m.UnscopedEnum.EOne)
|
||||
|
||||
# arithmetic
|
||||
assert m.UnscopedEnum.EOne & m.UnscopedEnum.EThree == m.UnscopedEnum.EOne
|
||||
assert m.UnscopedEnum.EOne | m.UnscopedEnum.ETwo == m.UnscopedEnum.EThree
|
||||
assert m.UnscopedEnum.EOne ^ m.UnscopedEnum.EThree == m.UnscopedEnum.ETwo
|
||||
|
||||
|
||||
def test_scoped_enum():
|
||||
assert m.test_scoped_enum(m.ScopedEnum.Three) == "ScopedEnum::Three"
|
||||
z = m.ScopedEnum.Two
|
||||
assert m.test_scoped_enum(z) == "ScopedEnum::Two"
|
||||
|
||||
# expected TypeError exceptions for scoped enum ==/!= int comparisons
|
||||
# Scoped enums will *NOT* accept ==/!= int comparisons (Will always return False)
|
||||
assert not z == 3
|
||||
assert not 3 == z
|
||||
assert z != 3
|
||||
assert 3 != z
|
||||
# Compare with None
|
||||
assert (z != None) # noqa: E711
|
||||
assert not (z == None) # noqa: E711
|
||||
# Compare with an object
|
||||
assert (z != object())
|
||||
assert not (z == object())
|
||||
# Scoped enums will *NOT* accept >, <, >= and <= int comparisons (Will throw exceptions)
|
||||
with pytest.raises(TypeError):
|
||||
assert z == 2
|
||||
z > 3
|
||||
with pytest.raises(TypeError):
|
||||
assert z != 3
|
||||
z < 3
|
||||
with pytest.raises(TypeError):
|
||||
z >= 3
|
||||
with pytest.raises(TypeError):
|
||||
z <= 3
|
||||
|
||||
# order
|
||||
assert m.ScopedEnum.Two < m.ScopedEnum.Three
|
||||
@@ -100,6 +178,7 @@ def test_binary_operators():
|
||||
assert int(m.Flags.Read | m.Flags.Execute) == 5
|
||||
assert int(m.Flags.Write | m.Flags.Execute) == 3
|
||||
assert int(m.Flags.Write | 1) == 3
|
||||
assert ~m.Flags.Write == -3
|
||||
|
||||
state = m.Flags.Read | m.Flags.Write
|
||||
assert (state & m.Flags.Read) != 0
|
||||
@@ -119,3 +198,9 @@ def test_enum_to_int():
|
||||
m.test_enum_to_uint(m.ClassWithUnscopedEnum.EMode.EFirstMode)
|
||||
m.test_enum_to_long_long(m.Flags.Read)
|
||||
m.test_enum_to_long_long(m.ClassWithUnscopedEnum.EMode.EFirstMode)
|
||||
|
||||
|
||||
def test_duplicate_enum_name():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
m.register_bad_enum()
|
||||
assert str(excinfo.value) == 'SimpleEnum: element "ONE" already exists!'
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
// A type that should be raised as an exeption in Python
|
||||
// A type that should be raised as an exception in Python
|
||||
class MyException : public std::exception {
|
||||
public:
|
||||
explicit MyException(const char * m) : message{m} {}
|
||||
@@ -118,10 +118,38 @@ TEST_SUBMODULE(exceptions, m) {
|
||||
m.def("throws_logic_error", []() { throw std::logic_error("this error should fall through to the standard handler"); });
|
||||
m.def("exception_matches", []() {
|
||||
py::dict foo;
|
||||
try { foo["bar"]; }
|
||||
try {
|
||||
// Assign to a py::object to force read access of nonexistent dict entry
|
||||
py::object o = foo["bar"];
|
||||
}
|
||||
catch (py::error_already_set& ex) {
|
||||
if (!ex.matches(PyExc_KeyError)) throw;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
m.def("exception_matches_base", []() {
|
||||
py::dict foo;
|
||||
try {
|
||||
// Assign to a py::object to force read access of nonexistent dict entry
|
||||
py::object o = foo["bar"];
|
||||
}
|
||||
catch (py::error_already_set &ex) {
|
||||
if (!ex.matches(PyExc_Exception)) throw;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
m.def("modulenotfound_exception_matches_base", []() {
|
||||
try {
|
||||
// On Python >= 3.6, this raises a ModuleNotFoundError, a subclass of ImportError
|
||||
py::module::import("nonexistent");
|
||||
}
|
||||
catch (py::error_already_set &ex) {
|
||||
if (!ex.matches(PyExc_ImportError)) throw;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
m.def("throw_already_set", [](bool err) {
|
||||
|
||||
@@ -48,7 +48,9 @@ def test_python_call_in_catch():
|
||||
|
||||
|
||||
def test_exception_matches():
|
||||
m.exception_matches()
|
||||
assert m.exception_matches()
|
||||
assert m.exception_matches_base()
|
||||
assert m.modulenotfound_exception_matches_base()
|
||||
|
||||
|
||||
def test_custom(msg):
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <cmath>
|
||||
|
||||
// Classes for testing python construction via C++ factory function:
|
||||
// Not publically constructible, copyable, or movable:
|
||||
// Not publicly constructible, copyable, or movable:
|
||||
class TestFactory1 {
|
||||
friend class TestFactoryHelper;
|
||||
TestFactory1() : value("(empty)") { print_default_created(this); }
|
||||
@@ -285,6 +285,7 @@ TEST_SUBMODULE(factory_constructors, m) {
|
||||
// test_reallocations
|
||||
// Class that has verbose operator_new/operator_delete calls
|
||||
struct NoisyAlloc {
|
||||
NoisyAlloc(const NoisyAlloc &) = default;
|
||||
NoisyAlloc(int i) { py::print(py::str("NoisyAlloc(int {})").format(i)); }
|
||||
NoisyAlloc(double d) { py::print(py::str("NoisyAlloc(double {})").format(d)); }
|
||||
~NoisyAlloc() { py::print("~NoisyAlloc()"); }
|
||||
|
||||
52
ext/pybind11/tests/test_gil_scoped.cpp
Normal file
52
ext/pybind11/tests/test_gil_scoped.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
tests/test_gil_scoped.cpp -- acquire and release gil
|
||||
|
||||
Copyright (c) 2017 Borja Zarco (Google LLC) <bzarco@google.com>
|
||||
|
||||
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 <pybind11/functional.h>
|
||||
|
||||
|
||||
class VirtClass {
|
||||
public:
|
||||
virtual ~VirtClass() {}
|
||||
virtual void virtual_func() {}
|
||||
virtual void pure_virtual_func() = 0;
|
||||
};
|
||||
|
||||
class PyVirtClass : public VirtClass {
|
||||
void virtual_func() override {
|
||||
PYBIND11_OVERLOAD(void, VirtClass, virtual_func,);
|
||||
}
|
||||
void pure_virtual_func() override {
|
||||
PYBIND11_OVERLOAD_PURE(void, VirtClass, pure_virtual_func,);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_SUBMODULE(gil_scoped, m) {
|
||||
py::class_<VirtClass, PyVirtClass>(m, "VirtClass")
|
||||
.def(py::init<>())
|
||||
.def("virtual_func", &VirtClass::virtual_func)
|
||||
.def("pure_virtual_func", &VirtClass::pure_virtual_func);
|
||||
|
||||
m.def("test_callback_py_obj",
|
||||
[](py::object func) { func(); });
|
||||
m.def("test_callback_std_func",
|
||||
[](const std::function<void()> &func) { func(); });
|
||||
m.def("test_callback_virtual_func",
|
||||
[](VirtClass &virt) { virt.virtual_func(); });
|
||||
m.def("test_callback_pure_virtual_func",
|
||||
[](VirtClass &virt) { virt.pure_virtual_func(); });
|
||||
m.def("test_cross_module_gil",
|
||||
[]() {
|
||||
auto cm = py::module::import("cross_module_gil_utils");
|
||||
auto gil_acquire = reinterpret_cast<void (*)()>(
|
||||
PyLong_AsVoidPtr(cm.attr("gil_acquire_funcaddr").ptr()));
|
||||
py::gil_scoped_release gil_release;
|
||||
gil_acquire();
|
||||
});
|
||||
}
|
||||
85
ext/pybind11/tests/test_gil_scoped.py
Normal file
85
ext/pybind11/tests/test_gil_scoped.py
Normal file
@@ -0,0 +1,85 @@
|
||||
import multiprocessing
|
||||
import threading
|
||||
from pybind11_tests import gil_scoped as m
|
||||
|
||||
|
||||
def _run_in_process(target, *args, **kwargs):
|
||||
"""Runs target in process and returns its exitcode after 10s (None if still alive)."""
|
||||
process = multiprocessing.Process(target=target, args=args, kwargs=kwargs)
|
||||
process.daemon = True
|
||||
try:
|
||||
process.start()
|
||||
# Do not need to wait much, 10s should be more than enough.
|
||||
process.join(timeout=10)
|
||||
return process.exitcode
|
||||
finally:
|
||||
if process.is_alive():
|
||||
process.terminate()
|
||||
|
||||
|
||||
def _python_to_cpp_to_python():
|
||||
"""Calls different C++ functions that come back to Python."""
|
||||
class ExtendedVirtClass(m.VirtClass):
|
||||
def virtual_func(self):
|
||||
pass
|
||||
|
||||
def pure_virtual_func(self):
|
||||
pass
|
||||
|
||||
extended = ExtendedVirtClass()
|
||||
m.test_callback_py_obj(lambda: None)
|
||||
m.test_callback_std_func(lambda: None)
|
||||
m.test_callback_virtual_func(extended)
|
||||
m.test_callback_pure_virtual_func(extended)
|
||||
|
||||
|
||||
def _python_to_cpp_to_python_from_threads(num_threads, parallel=False):
|
||||
"""Calls different C++ functions that come back to Python, from Python threads."""
|
||||
threads = []
|
||||
for _ in range(num_threads):
|
||||
thread = threading.Thread(target=_python_to_cpp_to_python)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
if parallel:
|
||||
threads.append(thread)
|
||||
else:
|
||||
thread.join()
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
|
||||
|
||||
def test_python_to_cpp_to_python_from_thread():
|
||||
"""Makes sure there is no GIL deadlock when running in a thread.
|
||||
|
||||
It runs in a separate process to be able to stop and assert if it deadlocks.
|
||||
"""
|
||||
assert _run_in_process(_python_to_cpp_to_python_from_threads, 1) == 0
|
||||
|
||||
|
||||
def test_python_to_cpp_to_python_from_thread_multiple_parallel():
|
||||
"""Makes sure there is no GIL deadlock when running in a thread multiple times in parallel.
|
||||
|
||||
It runs in a separate process to be able to stop and assert if it deadlocks.
|
||||
"""
|
||||
assert _run_in_process(_python_to_cpp_to_python_from_threads, 8, parallel=True) == 0
|
||||
|
||||
|
||||
def test_python_to_cpp_to_python_from_thread_multiple_sequential():
|
||||
"""Makes sure there is no GIL deadlock when running in a thread multiple times sequentially.
|
||||
|
||||
It runs in a separate process to be able to stop and assert if it deadlocks.
|
||||
"""
|
||||
assert _run_in_process(_python_to_cpp_to_python_from_threads, 8, parallel=False) == 0
|
||||
|
||||
|
||||
def test_python_to_cpp_to_python_from_process():
|
||||
"""Makes sure there is no GIL deadlock when using processes.
|
||||
|
||||
This test is for completion, but it was never an issue.
|
||||
"""
|
||||
assert _run_in_process(_python_to_cpp_to_python) == 0
|
||||
|
||||
|
||||
def test_cross_module_gil():
|
||||
"""Makes sure that the GIL can be acquired by another module from a GIL-released state."""
|
||||
m.test_cross_module_gil() # Should not raise a SIGSEGV
|
||||
@@ -54,6 +54,17 @@ def test_captured(capsys):
|
||||
assert stderr == msg
|
||||
|
||||
|
||||
def test_captured_large_string(capsys):
|
||||
# Make this bigger than the buffer used on the C++ side: 1024 chars
|
||||
msg = "I've been redirected to Python, I hope!"
|
||||
msg = msg * (1024 // len(msg) + 1)
|
||||
|
||||
m.captured_output_default(msg)
|
||||
stdout, stderr = capsys.readouterr()
|
||||
assert stdout == msg
|
||||
assert stderr == ''
|
||||
|
||||
|
||||
def test_guard_capture(capsys):
|
||||
msg = "I've been redirected to Python, I hope!"
|
||||
m.guard_output(msg)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
TEST_SUBMODULE(kwargs_and_defaults, m) {
|
||||
@@ -33,7 +34,9 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
|
||||
m.def("kw_func_udl_z", kw_func, "x"_a, "y"_a=0);
|
||||
|
||||
// test_args_and_kwargs
|
||||
m.def("args_function", [](py::args args) -> py::tuple { return args; });
|
||||
m.def("args_function", [](py::args args) -> py::tuple {
|
||||
return std::move(args);
|
||||
});
|
||||
m.def("args_kwargs_function", [](py::args args, py::kwargs kwargs) {
|
||||
return py::make_tuple(args, kwargs);
|
||||
});
|
||||
@@ -53,6 +56,34 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
|
||||
m.def("mixed_plus_args_kwargs_defaults", mixed_plus_both,
|
||||
py::arg("i") = 1, py::arg("j") = 3.14159);
|
||||
|
||||
// test_args_refcount
|
||||
// PyPy needs a garbage collection to get the reference count values to match CPython's behaviour
|
||||
#ifdef PYPY_VERSION
|
||||
#define GC_IF_NEEDED ConstructorStats::gc()
|
||||
#else
|
||||
#define GC_IF_NEEDED
|
||||
#endif
|
||||
m.def("arg_refcount_h", [](py::handle h) { GC_IF_NEEDED; return h.ref_count(); });
|
||||
m.def("arg_refcount_h", [](py::handle h, py::handle, py::handle) { GC_IF_NEEDED; return h.ref_count(); });
|
||||
m.def("arg_refcount_o", [](py::object o) { GC_IF_NEEDED; return o.ref_count(); });
|
||||
m.def("args_refcount", [](py::args a) {
|
||||
GC_IF_NEEDED;
|
||||
py::tuple t(a.size());
|
||||
for (size_t i = 0; i < a.size(); i++)
|
||||
// Use raw Python API here to avoid an extra, intermediate incref on the tuple item:
|
||||
t[i] = (int) Py_REFCNT(PyTuple_GET_ITEM(a.ptr(), static_cast<ssize_t>(i)));
|
||||
return t;
|
||||
});
|
||||
m.def("mixed_args_refcount", [](py::object o, py::args a) {
|
||||
GC_IF_NEEDED;
|
||||
py::tuple t(a.size() + 1);
|
||||
t[0] = o.ref_count();
|
||||
for (size_t i = 0; i < a.size(); i++)
|
||||
// Use raw Python API here to avoid an extra, intermediate incref on the tuple item:
|
||||
t[i + 1] = (int) Py_REFCNT(PyTuple_GET_ITEM(a.ptr(), static_cast<ssize_t>(i)));
|
||||
return t;
|
||||
});
|
||||
|
||||
// pybind11 won't allow these to be bound: args and kwargs, if present, must be at the end.
|
||||
// Uncomment these to test that the static_assert is indeed working:
|
||||
// m.def("bad_args1", [](py::args, int) {});
|
||||
|
||||
@@ -5,11 +5,11 @@ from pybind11_tests import kwargs_and_defaults as m
|
||||
def test_function_signatures(doc):
|
||||
assert doc(m.kw_func0) == "kw_func0(arg0: int, arg1: int) -> str"
|
||||
assert doc(m.kw_func1) == "kw_func1(x: int, y: int) -> str"
|
||||
assert doc(m.kw_func2) == "kw_func2(x: int=100, y: int=200) -> str"
|
||||
assert doc(m.kw_func3) == "kw_func3(data: str='Hello world!') -> None"
|
||||
assert doc(m.kw_func4) == "kw_func4(myList: List[int]=[13, 17]) -> str"
|
||||
assert doc(m.kw_func_udl) == "kw_func_udl(x: int, y: int=300) -> str"
|
||||
assert doc(m.kw_func_udl_z) == "kw_func_udl_z(x: int, y: int=0) -> str"
|
||||
assert doc(m.kw_func2) == "kw_func2(x: int = 100, y: int = 200) -> str"
|
||||
assert doc(m.kw_func3) == "kw_func3(data: str = 'Hello world!') -> None"
|
||||
assert doc(m.kw_func4) == "kw_func4(myList: List[int] = [13, 17]) -> str"
|
||||
assert doc(m.kw_func_udl) == "kw_func_udl(x: int, y: int = 300) -> str"
|
||||
assert doc(m.kw_func_udl_z) == "kw_func_udl_z(x: int, y: int = 0) -> str"
|
||||
assert doc(m.args_function) == "args_function(*args) -> tuple"
|
||||
assert doc(m.args_kwargs_function) == "args_kwargs_function(*args, **kwargs) -> tuple"
|
||||
assert doc(m.KWClass.foo0) == \
|
||||
@@ -93,7 +93,7 @@ def test_mixed_args_and_kwargs(msg):
|
||||
assert mpakd(1, i=1)
|
||||
assert msg(excinfo.value) == """
|
||||
mixed_plus_args_kwargs_defaults(): incompatible function arguments. The following argument types are supported:
|
||||
1. (i: int=1, j: float=3.14159, *args, **kwargs) -> tuple
|
||||
1. (i: int = 1, j: float = 3.14159, *args, **kwargs) -> tuple
|
||||
|
||||
Invoked with: 1; kwargs: i=1
|
||||
""" # noqa: E501 line too long
|
||||
@@ -101,7 +101,47 @@ def test_mixed_args_and_kwargs(msg):
|
||||
assert mpakd(1, 2, j=1)
|
||||
assert msg(excinfo.value) == """
|
||||
mixed_plus_args_kwargs_defaults(): incompatible function arguments. The following argument types are supported:
|
||||
1. (i: int=1, j: float=3.14159, *args, **kwargs) -> tuple
|
||||
1. (i: int = 1, j: float = 3.14159, *args, **kwargs) -> tuple
|
||||
|
||||
Invoked with: 1, 2; kwargs: j=1
|
||||
""" # noqa: E501 line too long
|
||||
|
||||
|
||||
def test_args_refcount():
|
||||
"""Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular
|
||||
arguments"""
|
||||
refcount = m.arg_refcount_h
|
||||
|
||||
myval = 54321
|
||||
expected = refcount(myval)
|
||||
assert m.arg_refcount_h(myval) == expected
|
||||
assert m.arg_refcount_o(myval) == expected + 1
|
||||
assert m.arg_refcount_h(myval) == expected
|
||||
assert refcount(myval) == expected
|
||||
|
||||
assert m.mixed_plus_args(1, 2.0, "a", myval) == (1, 2.0, ("a", myval))
|
||||
assert refcount(myval) == expected
|
||||
|
||||
assert m.mixed_plus_kwargs(3, 4.0, a=1, b=myval) == (3, 4.0, {"a": 1, "b": myval})
|
||||
assert refcount(myval) == expected
|
||||
|
||||
assert m.args_function(-1, myval) == (-1, myval)
|
||||
assert refcount(myval) == expected
|
||||
|
||||
assert m.mixed_plus_args_kwargs(5, 6.0, myval, a=myval) == (5, 6.0, (myval,), {"a": myval})
|
||||
assert refcount(myval) == expected
|
||||
|
||||
assert m.args_kwargs_function(7, 8, myval, a=1, b=myval) == \
|
||||
((7, 8, myval), {"a": 1, "b": myval})
|
||||
assert refcount(myval) == expected
|
||||
|
||||
exp3 = refcount(myval, myval, myval)
|
||||
assert m.args_refcount(myval, myval, myval) == (exp3, exp3, exp3)
|
||||
assert refcount(myval) == expected
|
||||
|
||||
# This function takes the first arg as a `py::object` and the rest as a `py::args`. Unlike the
|
||||
# previous case, when we have both positional and `py::args` we need to construct a new tuple
|
||||
# for the `py::args`; in the previous case, we could simply inc_ref and pass on Python's input
|
||||
# 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)
|
||||
|
||||
@@ -220,7 +220,7 @@ def test_cross_module_calls():
|
||||
c, d = m.MixGL2(3), cm.MixGL2(4)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.get_gl_value(c)
|
||||
assert "incompatible function arguments" in str(excinfo)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.get_gl_value(d)
|
||||
assert "incompatible function arguments" in str(excinfo)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
|
||||
#if !defined(PYBIND11_OVERLOAD_CAST)
|
||||
template <typename... Args>
|
||||
using overload_cast_ = pybind11::detail::overload_cast_impl<Args...>;
|
||||
#endif
|
||||
|
||||
class ExampleMandA {
|
||||
public:
|
||||
ExampleMandA() { print_default_created(this); }
|
||||
@@ -242,15 +247,16 @@ TEST_SUBMODULE(methods_and_attributes, m) {
|
||||
.def("overloaded_const", py::overload_cast<int, int>(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const", py::overload_cast<float, float>(&ExampleMandA::overloaded, py::const_))
|
||||
#else
|
||||
.def("overloaded", static_cast<py::str (ExampleMandA::*)()>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", static_cast<py::str (ExampleMandA::*)(int)>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", static_cast<py::str (ExampleMandA::*)(int, float)>(&ExampleMandA::overloaded))
|
||||
// Use both the traditional static_cast method and the C++11 compatible overload_cast_
|
||||
.def("overloaded", overload_cast_<>()(&ExampleMandA::overloaded))
|
||||
.def("overloaded", overload_cast_<int>()(&ExampleMandA::overloaded))
|
||||
.def("overloaded", overload_cast_<int, float>()(&ExampleMandA::overloaded))
|
||||
.def("overloaded", static_cast<py::str (ExampleMandA::*)(float, int)>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", static_cast<py::str (ExampleMandA::*)(int, int)>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", static_cast<py::str (ExampleMandA::*)(float, float)>(&ExampleMandA::overloaded))
|
||||
.def("overloaded_float", static_cast<py::str (ExampleMandA::*)(float, float)>(&ExampleMandA::overloaded))
|
||||
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(int ) const>(&ExampleMandA::overloaded))
|
||||
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(int, float) const>(&ExampleMandA::overloaded))
|
||||
.def("overloaded_float", overload_cast_<float, float>()(&ExampleMandA::overloaded))
|
||||
.def("overloaded_const", overload_cast_<int >()(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const", overload_cast_<int, float>()(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(float, int) const>(&ExampleMandA::overloaded))
|
||||
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(int, int) const>(&ExampleMandA::overloaded))
|
||||
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(float, float) const>(&ExampleMandA::overloaded))
|
||||
@@ -279,12 +285,20 @@ TEST_SUBMODULE(methods_and_attributes, m) {
|
||||
.def(py::init<>())
|
||||
.def_readonly("def_readonly", &TestProperties::value)
|
||||
.def_readwrite("def_readwrite", &TestProperties::value)
|
||||
.def_property("def_writeonly", nullptr,
|
||||
[](TestProperties& s,int v) { s.value = v; } )
|
||||
.def_property("def_property_writeonly", nullptr, &TestProperties::set)
|
||||
.def_property_readonly("def_property_readonly", &TestProperties::get)
|
||||
.def_property("def_property", &TestProperties::get, &TestProperties::set)
|
||||
.def_property("def_property_impossible", nullptr, nullptr)
|
||||
.def_readonly_static("def_readonly_static", &TestProperties::static_value)
|
||||
.def_readwrite_static("def_readwrite_static", &TestProperties::static_value)
|
||||
.def_property_static("def_writeonly_static", nullptr,
|
||||
[](py::object, int v) { TestProperties::static_value = v; })
|
||||
.def_property_readonly_static("def_property_readonly_static",
|
||||
[](py::object) { return TestProperties::static_get(); })
|
||||
.def_property_static("def_property_writeonly_static", nullptr,
|
||||
[](py::object, int v) { return TestProperties::static_set(v); })
|
||||
.def_property_static("def_property_static",
|
||||
[](py::object) { return TestProperties::static_get(); },
|
||||
[](py::object, int v) { TestProperties::static_set(v); })
|
||||
|
||||
@@ -98,23 +98,52 @@ def test_properties():
|
||||
instance.def_property = 3
|
||||
assert instance.def_property == 3
|
||||
|
||||
with pytest.raises(AttributeError) as excinfo:
|
||||
dummy = instance.def_property_writeonly # noqa: F841 unused var
|
||||
assert "unreadable attribute" in str(excinfo.value)
|
||||
|
||||
instance.def_property_writeonly = 4
|
||||
assert instance.def_property_readonly == 4
|
||||
|
||||
with pytest.raises(AttributeError) as excinfo:
|
||||
dummy = instance.def_property_impossible # noqa: F841 unused var
|
||||
assert "unreadable attribute" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(AttributeError) as excinfo:
|
||||
instance.def_property_impossible = 5
|
||||
assert "can't set attribute" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_static_properties():
|
||||
assert m.TestProperties.def_readonly_static == 1
|
||||
with pytest.raises(AttributeError) as excinfo:
|
||||
m.TestProperties.def_readonly_static = 2
|
||||
assert "can't set attribute" in str(excinfo)
|
||||
assert "can't set attribute" in str(excinfo.value)
|
||||
|
||||
m.TestProperties.def_readwrite_static = 2
|
||||
assert m.TestProperties.def_readwrite_static == 2
|
||||
|
||||
assert m.TestProperties.def_property_readonly_static == 2
|
||||
with pytest.raises(AttributeError) as excinfo:
|
||||
m.TestProperties.def_property_readonly_static = 3
|
||||
assert "can't set attribute" in str(excinfo)
|
||||
dummy = m.TestProperties.def_writeonly_static # noqa: F841 unused var
|
||||
assert "unreadable attribute" in str(excinfo.value)
|
||||
|
||||
m.TestProperties.def_property_static = 3
|
||||
assert m.TestProperties.def_property_static == 3
|
||||
m.TestProperties.def_writeonly_static = 3
|
||||
assert m.TestProperties.def_readonly_static == 3
|
||||
|
||||
assert m.TestProperties.def_property_readonly_static == 3
|
||||
with pytest.raises(AttributeError) as excinfo:
|
||||
m.TestProperties.def_property_readonly_static = 99
|
||||
assert "can't set attribute" in str(excinfo.value)
|
||||
|
||||
m.TestProperties.def_property_static = 4
|
||||
assert m.TestProperties.def_property_static == 4
|
||||
|
||||
with pytest.raises(AttributeError) as excinfo:
|
||||
dummy = m.TestProperties.def_property_writeonly_static
|
||||
assert "unreadable attribute" in str(excinfo.value)
|
||||
|
||||
m.TestProperties.def_property_writeonly_static = 5
|
||||
assert m.TestProperties.def_property_static == 5
|
||||
|
||||
# Static property read and write via instance
|
||||
instance = m.TestProperties()
|
||||
@@ -127,6 +156,13 @@ def test_static_properties():
|
||||
assert m.TestProperties.def_readwrite_static == 2
|
||||
assert instance.def_readwrite_static == 2
|
||||
|
||||
with pytest.raises(AttributeError) as excinfo:
|
||||
dummy = instance.def_property_writeonly_static # noqa: F841 unused var
|
||||
assert "unreadable attribute" in str(excinfo.value)
|
||||
|
||||
instance.def_property_writeonly_static = 4
|
||||
assert instance.def_property_static == 4
|
||||
|
||||
# It should be possible to override properties in derived classes
|
||||
assert m.TestPropertiesOverride().def_readonly == 99
|
||||
assert m.TestPropertiesOverride.def_readonly_static == 99
|
||||
|
||||
@@ -130,8 +130,8 @@ TEST_SUBMODULE(multiple_inheritance, m) {
|
||||
// test_mi_unaligned_base
|
||||
// test_mi_base_return
|
||||
// Issue #801: invalid casting to derived type with MI bases
|
||||
struct I801B1 { int a = 1; virtual ~I801B1() = default; };
|
||||
struct I801B2 { int b = 2; virtual ~I801B2() = default; };
|
||||
struct I801B1 { int a = 1; I801B1() = default; I801B1(const I801B1 &) = default; virtual ~I801B1() = default; };
|
||||
struct I801B2 { int b = 2; I801B2() = default; I801B2(const I801B2 &) = default; virtual ~I801B2() = default; };
|
||||
struct I801C : I801B1, I801B2 {};
|
||||
struct I801D : I801C {}; // Indirect MI
|
||||
// Unregistered classes:
|
||||
@@ -205,7 +205,7 @@ TEST_SUBMODULE(multiple_inheritance, m) {
|
||||
// test_diamond_inheritance
|
||||
// Issue #959: segfault when constructing diamond inheritance instance
|
||||
// All of these have int members so that there will be various unequal pointers involved.
|
||||
struct B { int b; virtual ~B() = default; };
|
||||
struct B { int b; B() = default; B(const B&) = default; virtual ~B() = default; };
|
||||
struct C0 : public virtual B { int c0; };
|
||||
struct C1 : public virtual B { int c1; };
|
||||
struct D : public C0, public C1 { int d; };
|
||||
|
||||
@@ -14,6 +14,67 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// Size / dtype checks.
|
||||
struct DtypeCheck {
|
||||
py::dtype numpy{};
|
||||
py::dtype pybind11{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
DtypeCheck get_dtype_check(const char* name) {
|
||||
py::module np = py::module::import("numpy");
|
||||
DtypeCheck check{};
|
||||
check.numpy = np.attr("dtype")(np.attr(name));
|
||||
check.pybind11 = py::dtype::of<T>();
|
||||
return check;
|
||||
}
|
||||
|
||||
std::vector<DtypeCheck> get_concrete_dtype_checks() {
|
||||
return {
|
||||
// Normalization
|
||||
get_dtype_check<std::int8_t>("int8"),
|
||||
get_dtype_check<std::uint8_t>("uint8"),
|
||||
get_dtype_check<std::int16_t>("int16"),
|
||||
get_dtype_check<std::uint16_t>("uint16"),
|
||||
get_dtype_check<std::int32_t>("int32"),
|
||||
get_dtype_check<std::uint32_t>("uint32"),
|
||||
get_dtype_check<std::int64_t>("int64"),
|
||||
get_dtype_check<std::uint64_t>("uint64")
|
||||
};
|
||||
}
|
||||
|
||||
struct DtypeSizeCheck {
|
||||
std::string name{};
|
||||
int size_cpp{};
|
||||
int size_numpy{};
|
||||
// For debugging.
|
||||
py::dtype dtype{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
DtypeSizeCheck get_dtype_size_check() {
|
||||
DtypeSizeCheck check{};
|
||||
check.name = py::type_id<T>();
|
||||
check.size_cpp = sizeof(T);
|
||||
check.dtype = py::dtype::of<T>();
|
||||
check.size_numpy = check.dtype.attr("itemsize").template cast<int>();
|
||||
return check;
|
||||
}
|
||||
|
||||
std::vector<DtypeSizeCheck> get_platform_dtype_size_checks() {
|
||||
return {
|
||||
get_dtype_size_check<short>(),
|
||||
get_dtype_size_check<unsigned short>(),
|
||||
get_dtype_size_check<int>(),
|
||||
get_dtype_size_check<unsigned int>(),
|
||||
get_dtype_size_check<long>(),
|
||||
get_dtype_size_check<unsigned long>(),
|
||||
get_dtype_size_check<long long>(),
|
||||
get_dtype_size_check<unsigned long long>(),
|
||||
};
|
||||
}
|
||||
|
||||
// Arrays.
|
||||
using arr = py::array;
|
||||
using arr_t = py::array_t<uint16_t, 0>;
|
||||
static_assert(std::is_same<arr_t::value_type, uint16_t>::value, "");
|
||||
@@ -68,10 +129,33 @@ template <typename T, typename T2> py::handle auxiliaries(T &&r, T2 &&r2) {
|
||||
return l.release();
|
||||
}
|
||||
|
||||
// note: declaration at local scope would create a dangling reference!
|
||||
static int data_i = 42;
|
||||
|
||||
TEST_SUBMODULE(numpy_array, sm) {
|
||||
try { py::module::import("numpy"); }
|
||||
catch (...) { return; }
|
||||
|
||||
// test_dtypes
|
||||
py::class_<DtypeCheck>(sm, "DtypeCheck")
|
||||
.def_readonly("numpy", &DtypeCheck::numpy)
|
||||
.def_readonly("pybind11", &DtypeCheck::pybind11)
|
||||
.def("__repr__", [](const DtypeCheck& self) {
|
||||
return py::str("<DtypeCheck numpy={} pybind11={}>").format(
|
||||
self.numpy, self.pybind11);
|
||||
});
|
||||
sm.def("get_concrete_dtype_checks", &get_concrete_dtype_checks);
|
||||
|
||||
py::class_<DtypeSizeCheck>(sm, "DtypeSizeCheck")
|
||||
.def_readonly("name", &DtypeSizeCheck::name)
|
||||
.def_readonly("size_cpp", &DtypeSizeCheck::size_cpp)
|
||||
.def_readonly("size_numpy", &DtypeSizeCheck::size_numpy)
|
||||
.def("__repr__", [](const DtypeSizeCheck& self) {
|
||||
return py::str("<DtypeSizeCheck name='{}' size_cpp={} size_numpy={} dtype={}>").format(
|
||||
self.name, self.size_cpp, self.size_numpy, self.dtype);
|
||||
});
|
||||
sm.def("get_platform_dtype_size_checks", &get_platform_dtype_size_checks);
|
||||
|
||||
// test_array_attributes
|
||||
sm.def("ndim", [](const arr& a) { return a.ndim(); });
|
||||
sm.def("shape", [](const arr& a) { return arr(a.ndim(), a.shape()); });
|
||||
@@ -102,6 +186,11 @@ TEST_SUBMODULE(numpy_array, sm) {
|
||||
sm.def("make_f_array", [] { return py::array_t<float>({ 2, 2 }, { 4, 8 }); });
|
||||
sm.def("make_c_array", [] { return py::array_t<float>({ 2, 2 }, { 8, 4 }); });
|
||||
|
||||
// test_empty_shaped_array
|
||||
sm.def("make_empty_shaped_array", [] { return py::array(py::dtype("f"), {}, {}); });
|
||||
// test numpy scalars (empty shape, ndim==0)
|
||||
sm.def("scalar_int", []() { return py::array(py::dtype("i"), {}, {}, &data_i); });
|
||||
|
||||
// test_wrap
|
||||
sm.def("wrap", [](py::array a) {
|
||||
return py::array(
|
||||
@@ -292,4 +381,10 @@ TEST_SUBMODULE(numpy_array, sm) {
|
||||
std::fill(a.mutable_data(), a.mutable_data() + a.size(), 42.);
|
||||
return a;
|
||||
});
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
sm.def("index_using_ellipsis", [](py::array a) {
|
||||
return a[py::make_tuple(0, py::ellipsis(), 0)];
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -7,6 +7,21 @@ with pytest.suppress(ImportError):
|
||||
import numpy as np
|
||||
|
||||
|
||||
def test_dtypes():
|
||||
# See issue #1328.
|
||||
# - Platform-dependent sizes.
|
||||
for size_check in m.get_platform_dtype_size_checks():
|
||||
print(size_check)
|
||||
assert size_check.size_cpp == size_check.size_numpy, size_check
|
||||
# - Concrete sizes.
|
||||
for check in m.get_concrete_dtype_checks():
|
||||
print(check)
|
||||
assert check.numpy == check.pybind11, check
|
||||
if check.numpy.num != check.pybind11.num:
|
||||
print("NOTE: typenum mismatch for {}: {} != {}".format(
|
||||
check, check.numpy.num, check.pybind11.num))
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def arr():
|
||||
return np.array([[1, 2, 3], [4, 5, 6]], '=u2')
|
||||
@@ -135,8 +150,18 @@ def test_make_c_f_array():
|
||||
assert not m.make_f_array().flags.c_contiguous
|
||||
|
||||
|
||||
def test_make_empty_shaped_array():
|
||||
m.make_empty_shaped_array()
|
||||
|
||||
# empty shape means numpy scalar, PEP 3118
|
||||
assert m.scalar_int().ndim == 0
|
||||
assert m.scalar_int().shape == ()
|
||||
assert m.scalar_int() == 42
|
||||
|
||||
|
||||
def test_wrap():
|
||||
def assert_references(a, b, base=None):
|
||||
from distutils.version import LooseVersion
|
||||
if base is None:
|
||||
base = a
|
||||
assert a is not b
|
||||
@@ -147,7 +172,10 @@ def test_wrap():
|
||||
assert a.flags.f_contiguous == b.flags.f_contiguous
|
||||
assert a.flags.writeable == b.flags.writeable
|
||||
assert a.flags.aligned == b.flags.aligned
|
||||
assert a.flags.updateifcopy == b.flags.updateifcopy
|
||||
if LooseVersion(np.__version__) >= LooseVersion("1.14.0"):
|
||||
assert a.flags.writebackifcopy == b.flags.writebackifcopy
|
||||
else:
|
||||
assert a.flags.updateifcopy == b.flags.updateifcopy
|
||||
assert np.all(a == b)
|
||||
assert not b.flags.owndata
|
||||
assert b.base is base
|
||||
@@ -282,17 +310,17 @@ def test_overload_resolution(msg):
|
||||
1. (arg0: numpy.ndarray[int32]) -> str
|
||||
2. (arg0: numpy.ndarray[float64]) -> str
|
||||
|
||||
Invoked with:"""
|
||||
Invoked with: """
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.overloaded3(np.array([1], dtype='uintc'))
|
||||
assert msg(excinfo.value) == expected_exc + " array([1], dtype=uint32)"
|
||||
assert msg(excinfo.value) == expected_exc + repr(np.array([1], dtype='uint32'))
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.overloaded3(np.array([1], dtype='float32'))
|
||||
assert msg(excinfo.value) == expected_exc + " array([ 1.], dtype=float32)"
|
||||
assert msg(excinfo.value) == expected_exc + repr(np.array([1.], dtype='float32'))
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.overloaded3(np.array([1], dtype='complex'))
|
||||
assert msg(excinfo.value) == expected_exc + " array([ 1.+0.j])"
|
||||
assert msg(excinfo.value) == expected_exc + repr(np.array([1. + 0.j]))
|
||||
|
||||
# Exact matches:
|
||||
assert m.overloaded4(np.array([1], dtype='double')) == 'double'
|
||||
@@ -400,3 +428,20 @@ def test_array_create_and_resize(msg):
|
||||
a = m.create_and_resize(2)
|
||||
assert(a.size == 4)
|
||||
assert(np.all(a == 42.))
|
||||
|
||||
|
||||
@pytest.unsupported_on_py2
|
||||
def test_index_using_ellipsis():
|
||||
a = m.index_using_ellipsis(np.zeros((5, 6, 7)))
|
||||
assert a.shape == (6,)
|
||||
|
||||
|
||||
@pytest.unsupported_on_pypy
|
||||
def test_dtype_refcount_leak():
|
||||
from sys import getrefcount
|
||||
dtype = np.dtype(np.float_)
|
||||
a = np.array([1], dtype=dtype)
|
||||
before = getrefcount(dtype)
|
||||
m.ndim(a)
|
||||
after = getrefcount(dtype)
|
||||
assert after == before
|
||||
|
||||
@@ -29,6 +29,13 @@ std::ostream& operator<<(std::ostream& os, const SimpleStruct& v) {
|
||||
return os << "s:" << v.bool_ << "," << v.uint_ << "," << v.float_ << "," << v.ldbl_;
|
||||
}
|
||||
|
||||
struct SimpleStructReordered {
|
||||
bool bool_;
|
||||
float float_;
|
||||
uint32_t uint_;
|
||||
long double ldbl_;
|
||||
};
|
||||
|
||||
PYBIND11_PACKED(struct PackedStruct {
|
||||
bool bool_;
|
||||
uint32_t uint_;
|
||||
@@ -244,6 +251,9 @@ py::list test_dtype_ctors() {
|
||||
return list;
|
||||
}
|
||||
|
||||
struct A {};
|
||||
struct B {};
|
||||
|
||||
TEST_SUBMODULE(numpy_dtypes, m) {
|
||||
try { py::module::import("numpy"); }
|
||||
catch (...) { return; }
|
||||
@@ -252,6 +262,7 @@ TEST_SUBMODULE(numpy_dtypes, m) {
|
||||
py::class_<SimpleStruct>(m, "SimpleStruct");
|
||||
|
||||
PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_);
|
||||
PYBIND11_NUMPY_DTYPE(SimpleStructReordered, bool_, uint_, float_, ldbl_);
|
||||
PYBIND11_NUMPY_DTYPE(PackedStruct, bool_, uint_, float_, ldbl_);
|
||||
PYBIND11_NUMPY_DTYPE(NestedStruct, a, b);
|
||||
PYBIND11_NUMPY_DTYPE(PartialStruct, bool_, uint_, float_, ldbl_);
|
||||
@@ -271,6 +282,15 @@ TEST_SUBMODULE(numpy_dtypes, m) {
|
||||
// struct NotPOD { std::string v; NotPOD() : v("hi") {}; };
|
||||
// PYBIND11_NUMPY_DTYPE(NotPOD, v);
|
||||
|
||||
// Check that dtypes can be registered programmatically, both from
|
||||
// initializer lists of field descriptors and from other containers.
|
||||
py::detail::npy_format_descriptor<A>::register_dtype(
|
||||
{}
|
||||
);
|
||||
py::detail::npy_format_descriptor<B>::register_dtype(
|
||||
std::vector<py::detail::field_descriptor>{}
|
||||
);
|
||||
|
||||
// test_recarray, test_scalar_conversion
|
||||
m.def("create_rec_simple", &create_recarray<SimpleStruct>);
|
||||
m.def("create_rec_packed", &create_recarray<PackedStruct>);
|
||||
@@ -448,4 +468,7 @@ TEST_SUBMODULE(numpy_dtypes, m) {
|
||||
|
||||
// test_register_dtype
|
||||
m.def("register_dtype", []() { PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_); });
|
||||
|
||||
// test_str_leak
|
||||
m.def("dtype_wrapper", [](py::object d) { return py::dtype::from_args(std::move(d)); });
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ def test_dtype(simple_dtype):
|
||||
partial_nested_fmt(),
|
||||
"[('a', 'S3'), ('b', 'S3')]",
|
||||
("{{'names':['a','b','c','d'], " +
|
||||
"'formats':[('S4', (3,)),('<i4', (2,)),('u1', (3,)),('<f4', (4, 2))], " +
|
||||
"'formats':[('S4', (3,)),('" + e + "i4', (2,)),('u1', (3,)),('" + e + "f4', (4, 2))], " +
|
||||
"'offsets':[0,12,20,24], 'itemsize':56}}").format(e=e),
|
||||
"[('e1', '" + e + "i8'), ('e2', 'u1')]",
|
||||
"[('x', 'i1'), ('y', '" + e + "u8')]",
|
||||
@@ -215,7 +215,7 @@ def test_array_array():
|
||||
arr = m.create_array_array(3)
|
||||
assert str(arr.dtype) == (
|
||||
"{{'names':['a','b','c','d'], " +
|
||||
"'formats':[('S4', (3,)),('<i4', (2,)),('u1', (3,)),('{e}f4', (4, 2))], " +
|
||||
"'formats':[('S4', (3,)),('" + e + "i4', (2,)),('u1', (3,)),('{e}f4', (4, 2))], " +
|
||||
"'offsets':[0,12,20,24], 'itemsize':56}}").format(e=e)
|
||||
assert m.print_array_array(arr) == [
|
||||
"a={{A,B,C,D},{K,L,M,N},{U,V,W,X}},b={0,1}," +
|
||||
@@ -293,6 +293,18 @@ def test_register_dtype():
|
||||
assert 'dtype is already registered' in str(excinfo.value)
|
||||
|
||||
|
||||
@pytest.requires_numpy
|
||||
@pytest.unsupported_on_pypy
|
||||
def test_str_leak():
|
||||
from sys import getrefcount
|
||||
fmt = "f4"
|
||||
pytest.gc_collect()
|
||||
start = getrefcount(fmt)
|
||||
d = m.dtype_wrapper(fmt)
|
||||
assert d is np.dtype("f4")
|
||||
del d
|
||||
pytest.gc_collect()
|
||||
assert getrefcount(fmt) == start
|
||||
|
||||
|
||||
def test_compare_buffer_info():
|
||||
assert all(m.compare_buffer_info())
|
||||
|
||||
@@ -11,10 +11,14 @@
|
||||
#include <pybind11/stl.h>
|
||||
#include <vector>
|
||||
|
||||
using StringList = std::vector<std::string>;
|
||||
// IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures
|
||||
//
|
||||
// This also deliberately doesn't use the below StringList type alias to test
|
||||
// that MAKE_OPAQUE can handle a type containing a `,`. (The `std::allocator`
|
||||
// bit is just the default `std::vector` allocator).
|
||||
PYBIND11_MAKE_OPAQUE(std::vector<std::string, std::allocator<std::string>>);
|
||||
|
||||
/* IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures */
|
||||
PYBIND11_MAKE_OPAQUE(StringList);
|
||||
using StringList = std::vector<std::string, std::allocator<std::string>>;
|
||||
|
||||
TEST_SUBMODULE(opaque_types, m) {
|
||||
// test_string_list
|
||||
|
||||
@@ -4,21 +4,21 @@ from pybind11_tests import ConstructorStats, UserType
|
||||
|
||||
|
||||
def test_string_list():
|
||||
l = m.StringList()
|
||||
l.push_back("Element 1")
|
||||
l.push_back("Element 2")
|
||||
assert m.print_opaque_list(l) == "Opaque list: [Element 1, Element 2]"
|
||||
assert l.back() == "Element 2"
|
||||
lst = m.StringList()
|
||||
lst.push_back("Element 1")
|
||||
lst.push_back("Element 2")
|
||||
assert m.print_opaque_list(lst) == "Opaque list: [Element 1, Element 2]"
|
||||
assert lst.back() == "Element 2"
|
||||
|
||||
for i, k in enumerate(l, start=1):
|
||||
for i, k in enumerate(lst, start=1):
|
||||
assert k == "Element {}".format(i)
|
||||
l.pop_back()
|
||||
assert m.print_opaque_list(l) == "Opaque list: [Element 1]"
|
||||
lst.pop_back()
|
||||
assert m.print_opaque_list(lst) == "Opaque list: [Element 1]"
|
||||
|
||||
cvp = m.ClassWithSTLVecProperty()
|
||||
assert m.print_opaque_list(cvp.stringList) == "Opaque list: []"
|
||||
|
||||
cvp.stringList = l
|
||||
cvp.stringList = lst
|
||||
cvp.stringList.push_back("Element 3")
|
||||
assert m.print_opaque_list(cvp.stringList) == "Opaque list: [Element 1, Element 3]"
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ public:
|
||||
|
||||
std::string toString() const { return "[" + std::to_string(x) + ", " + std::to_string(y) + "]"; }
|
||||
|
||||
Vector2 operator-() const { return Vector2(-x, -y); }
|
||||
Vector2 operator+(const Vector2 &v) const { return Vector2(x + v.x, y + v.y); }
|
||||
Vector2 operator-(const Vector2 &v) const { return Vector2(x - v.x, y - v.y); }
|
||||
Vector2 operator-(float value) const { return Vector2(x - value, y - value); }
|
||||
@@ -62,6 +63,25 @@ namespace std {
|
||||
};
|
||||
}
|
||||
|
||||
// MSVC warns about unknown pragmas, and warnings are errors.
|
||||
#ifndef _MSC_VER
|
||||
#pragma GCC diagnostic push
|
||||
// clang 7.0.0 and Apple LLVM 10.0.1 introduce `-Wself-assign-overloaded` to
|
||||
// `-Wall`, which is used here for overloading (e.g. `py::self += py::self `).
|
||||
// Here, we suppress the warning using `#pragma diagnostic`.
|
||||
// Taken from: https://github.com/RobotLocomotion/drake/commit/aaf84b46
|
||||
// TODO(eric): This could be resolved using a function / functor (e.g. `py::self()`).
|
||||
#if (__APPLE__) && (__clang__)
|
||||
#if (__clang_major__ >= 10) && (__clang_minor__ >= 0) && (__clang_patchlevel__ >= 1)
|
||||
#pragma GCC diagnostic ignored "-Wself-assign-overloaded"
|
||||
#endif
|
||||
#elif (__clang__)
|
||||
#if (__clang_major__ >= 7)
|
||||
#pragma GCC diagnostic ignored "-Wself-assign-overloaded"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
TEST_SUBMODULE(operators, m) {
|
||||
|
||||
// test_operator_overloading
|
||||
@@ -85,6 +105,7 @@ TEST_SUBMODULE(operators, m) {
|
||||
.def(float() - py::self)
|
||||
.def(float() * py::self)
|
||||
.def(float() / py::self)
|
||||
.def(-py::self)
|
||||
.def("__str__", &Vector2::toString)
|
||||
.def(hash(py::self))
|
||||
;
|
||||
@@ -144,3 +165,7 @@ TEST_SUBMODULE(operators, m) {
|
||||
.def_readwrite("b", &NestC::b);
|
||||
m.def("get_NestC", [](const NestC &c) { return c.value; });
|
||||
}
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
@@ -9,6 +9,8 @@ def test_operator_overloading():
|
||||
assert str(v1) == "[1.000000, 2.000000]"
|
||||
assert str(v2) == "[3.000000, -1.000000]"
|
||||
|
||||
assert str(-v2) == "[-3.000000, 1.000000]"
|
||||
|
||||
assert str(v1 + v2) == "[4.000000, 1.000000]"
|
||||
assert str(v1 - v2) == "[-2.000000, 3.000000]"
|
||||
assert str(v1 - 8) == "[-7.000000, -6.000000]"
|
||||
@@ -44,13 +46,13 @@ def test_operator_overloading():
|
||||
del v2
|
||||
assert cstats.alive() == 0
|
||||
assert cstats.values() == ['[1.000000, 2.000000]', '[3.000000, -1.000000]',
|
||||
'[4.000000, 1.000000]', '[-2.000000, 3.000000]',
|
||||
'[-7.000000, -6.000000]', '[9.000000, 10.000000]',
|
||||
'[8.000000, 16.000000]', '[0.125000, 0.250000]',
|
||||
'[7.000000, 6.000000]', '[9.000000, 10.000000]',
|
||||
'[8.000000, 16.000000]', '[8.000000, 4.000000]',
|
||||
'[3.000000, -2.000000]', '[3.000000, -0.500000]',
|
||||
'[6.000000, -2.000000]']
|
||||
'[-3.000000, 1.000000]', '[4.000000, 1.000000]',
|
||||
'[-2.000000, 3.000000]', '[-7.000000, -6.000000]',
|
||||
'[9.000000, 10.000000]', '[8.000000, 16.000000]',
|
||||
'[0.125000, 0.250000]', '[7.000000, 6.000000]',
|
||||
'[9.000000, 10.000000]', '[8.000000, 16.000000]',
|
||||
'[8.000000, 4.000000]', '[3.000000, -2.000000]',
|
||||
'[3.000000, -0.500000]', '[6.000000, -2.000000]']
|
||||
assert cstats.default_constructions == 0
|
||||
assert cstats.copy_constructions == 0
|
||||
assert cstats.move_constructions >= 10
|
||||
@@ -98,7 +100,7 @@ def test_nested():
|
||||
|
||||
del c
|
||||
pytest.gc_collect()
|
||||
del a # Should't delete while abase is still alive
|
||||
del a # Shouldn't delete while abase is still alive
|
||||
pytest.gc_collect()
|
||||
|
||||
assert abase.value == 42
|
||||
|
||||
@@ -34,3 +34,9 @@ def test_roundtrip_with_dict(cls_name):
|
||||
assert p2.value == p.value
|
||||
assert p2.extra == p.extra
|
||||
assert p2.dynamic == p.dynamic
|
||||
|
||||
|
||||
def test_enum_pickle():
|
||||
from pybind11_tests import enums as e
|
||||
data = pickle.dumps(e.EOne, 2)
|
||||
assert e.EOne == pickle.loads(data)
|
||||
|
||||
@@ -17,6 +17,8 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
list.append("value");
|
||||
py::print("Entry at position 0:", list[0]);
|
||||
list[0] = py::str("overwritten");
|
||||
list.insert(0, "inserted-0");
|
||||
list.insert(2, "inserted-2");
|
||||
return list;
|
||||
});
|
||||
m.def("print_list", [](py::list list) {
|
||||
@@ -37,6 +39,12 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
for (auto item : set)
|
||||
py::print("key:", item);
|
||||
});
|
||||
m.def("set_contains", [](py::set set, py::object key) {
|
||||
return set.contains(key);
|
||||
});
|
||||
m.def("set_contains", [](py::set set, const char* key) {
|
||||
return set.contains(key);
|
||||
});
|
||||
|
||||
// test_dict
|
||||
m.def("get_dict", []() { return py::dict("key"_a="value"); });
|
||||
@@ -49,6 +57,12 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
auto d2 = py::dict("z"_a=3, **d1);
|
||||
return d2;
|
||||
});
|
||||
m.def("dict_contains", [](py::dict dict, py::object val) {
|
||||
return dict.contains(val);
|
||||
});
|
||||
m.def("dict_contains", [](py::dict dict, const char* val) {
|
||||
return dict.contains(val);
|
||||
});
|
||||
|
||||
// test_str
|
||||
m.def("str_from_string", []() { return py::str(std::string("baz")); });
|
||||
@@ -269,4 +283,28 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
m.def("print_failure", []() { py::print(42, UnregisteredType()); });
|
||||
|
||||
m.def("hash_function", [](py::object obj) { return py::hash(obj); });
|
||||
|
||||
m.def("test_number_protocol", [](py::object a, py::object b) {
|
||||
py::list l;
|
||||
l.append(a.equal(b));
|
||||
l.append(a.not_equal(b));
|
||||
l.append(a < b);
|
||||
l.append(a <= b);
|
||||
l.append(a > b);
|
||||
l.append(a >= b);
|
||||
l.append(a + b);
|
||||
l.append(a - b);
|
||||
l.append(a * b);
|
||||
l.append(a / b);
|
||||
l.append(a | b);
|
||||
l.append(a & b);
|
||||
l.append(a ^ b);
|
||||
l.append(a >> b);
|
||||
l.append(a << b);
|
||||
return l;
|
||||
});
|
||||
|
||||
m.def("test_list_slicing", [](py::list a) {
|
||||
return a[py::slice(0, -1, 2)];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from __future__ import division
|
||||
import pytest
|
||||
import sys
|
||||
|
||||
@@ -7,15 +8,17 @@ from pybind11_tests import debug_enabled
|
||||
|
||||
def test_list(capture, doc):
|
||||
with capture:
|
||||
l = m.get_list()
|
||||
assert l == ["overwritten"]
|
||||
lst = m.get_list()
|
||||
assert lst == ["inserted-0", "overwritten", "inserted-2"]
|
||||
|
||||
l.append("value2")
|
||||
m.print_list(l)
|
||||
lst.append("value2")
|
||||
m.print_list(lst)
|
||||
assert capture.unordered == """
|
||||
Entry at position 0: value
|
||||
list item 0: overwritten
|
||||
list item 1: value2
|
||||
list item 0: inserted-0
|
||||
list item 1: overwritten
|
||||
list item 2: inserted-2
|
||||
list item 3: value2
|
||||
"""
|
||||
|
||||
assert doc(m.get_list) == "get_list() -> list"
|
||||
@@ -36,6 +39,10 @@ def test_set(capture, doc):
|
||||
key: key4
|
||||
"""
|
||||
|
||||
assert not m.set_contains(set([]), 42)
|
||||
assert m.set_contains({42}, 42)
|
||||
assert m.set_contains({"foo"}, "foo")
|
||||
|
||||
assert doc(m.get_list) == "get_list() -> list"
|
||||
assert doc(m.print_list) == "print_list(arg0: list) -> None"
|
||||
|
||||
@@ -52,6 +59,10 @@ def test_dict(capture, doc):
|
||||
key: key2, value=value2
|
||||
"""
|
||||
|
||||
assert not m.dict_contains({}, 42)
|
||||
assert m.dict_contains({42: None}, 42)
|
||||
assert m.dict_contains({"foo": None}, "foo")
|
||||
|
||||
assert doc(m.get_dict) == "get_dict() -> dict"
|
||||
assert doc(m.print_dict) == "print_dict(arg0: dict) -> None"
|
||||
|
||||
@@ -238,3 +249,15 @@ def test_hash():
|
||||
assert m.hash_function(Hashable(42)) == 42
|
||||
with pytest.raises(TypeError):
|
||||
m.hash_function(Unhashable())
|
||||
|
||||
|
||||
def test_number_protocol():
|
||||
for a, b in [(1, 1), (3, 5)]:
|
||||
li = [a == b, a != b, a < b, a <= b, a > b, a >= b, a + b,
|
||||
a - b, a * b, a / b, a | b, a & b, a ^ b, a >> b, a << b]
|
||||
assert m.test_number_protocol(a, b) == li
|
||||
|
||||
|
||||
def test_list_slicing():
|
||||
li = list(range(100))
|
||||
assert li[::2] == m.test_list_slicing(li)
|
||||
|
||||
@@ -71,6 +71,25 @@ py::list test_random_access_iterator(PythonType x) {
|
||||
}
|
||||
|
||||
TEST_SUBMODULE(sequences_and_iterators, m) {
|
||||
// test_sliceable
|
||||
class Sliceable{
|
||||
public:
|
||||
Sliceable(int n): size(n) {}
|
||||
int start,stop,step;
|
||||
int size;
|
||||
};
|
||||
py::class_<Sliceable>(m,"Sliceable")
|
||||
.def(py::init<int>())
|
||||
.def("__getitem__",[](const Sliceable &s, py::slice slice) {
|
||||
ssize_t start, stop, step, slicelength;
|
||||
if (!slice.compute(s.size, &start, &stop, &step, &slicelength))
|
||||
throw py::error_already_set();
|
||||
int istart = static_cast<int>(start);
|
||||
int istop = static_cast<int>(stop);
|
||||
int istep = static_cast<int>(step);
|
||||
return std::make_tuple(istart,istop,istep);
|
||||
})
|
||||
;
|
||||
|
||||
// test_sequence
|
||||
class Sequence {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user