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:
Bobby R. Bruce
2019-09-23 13:52:58 -07:00
parent 9235ae56c2
commit f97cf54db7
117 changed files with 4325 additions and 1141 deletions

View File

@@ -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%

View File

@@ -27,6 +27,7 @@ MANIFEST
*.py[co]
*.egg-info
*~
.*.swp
.DS_Store
/dist
/build

View File

@@ -1,3 +1,3 @@
[submodule "tools/clang"]
path = tools/clang
url = https://github.com/wjakob/clang-cindex-python3
url = ../../wjakob/clang-cindex-python3

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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` |

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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
=====================================

View File

@@ -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)];

View File

@@ -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

View File

@@ -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_``

View File

@@ -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

View File

@@ -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.

View File

@@ -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}
}

View File

@@ -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>`_ )

View File

@@ -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
==========

View File

@@ -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);

View File

@@ -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)

View File

@@ -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);

View File

@@ -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>> {

View File

@@ -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;
}

View File

@@ -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");

View File

@@ -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)

View File

@@ -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...);
}
};

View File

@@ -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:

View File

@@ -16,6 +16,8 @@
#include <cxxabi.h>
#endif
#include "common.h"
NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
NAMESPACE_BEGIN(detail)
/// Erase all occurrences of a substring

View File

@@ -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)

View File

@@ -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]);

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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);
}
);

View File

@@ -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)

View File

@@ -1,2 +1,2 @@
version_info = (2, 2, 1)
version_info = (2, 4, 1)
__version__ = '.'.join(map(str, version_info))

View File

@@ -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

View File

@@ -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,

View File

@@ -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})

View File

@@ -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():

View File

@@ -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

View 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
}

View File

@@ -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

View 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__")();
});
}

View 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()))

View File

@@ -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) }
);
})

View File

@@ -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

View File

@@ -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;
});
}

View File

@@ -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()

View File

@@ -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")

View File

@@ -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));
});
}

View File

@@ -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()

View File

@@ -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;
});
}

View File

@@ -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))

View File

@@ -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; };

View File

@@ -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

View File

@@ -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
};
}

View File

@@ -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) {

View File

@@ -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)

View File

@@ -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"""

View File

@@ -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)

View File

@@ -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);

View 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());
});
}

View File

@@ -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

View File

@@ -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();
});
}

View File

@@ -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!'

View File

@@ -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) {

View File

@@ -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):

View File

@@ -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()"); }

View 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();
});
}

View 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

View File

@@ -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)

View File

@@ -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) {});

View File

@@ -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)

View File

@@ -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)

View File

@@ -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); })

View File

@@ -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

View File

@@ -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; };

View File

@@ -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
}

View File

@@ -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

View File

@@ -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)); });
}

View File

@@ -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())

View File

@@ -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

View File

@@ -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]"

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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)];
});
}

View File

@@ -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)

View File

@@ -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