- Visual Studio 2017
- Visual Studio 2015
test: off
+skip_branch_with_pr: true
build:
parallel: true
platform:
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: |
-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%
*.py[co]
*.egg-info
*~
+.*.swp
.DS_Store
/dist
/build
[submodule "tools/clang"]
path = tools/clang
- url = https://github.com/wjakob/clang-cindex-python3
+ url = ../../wjakob/clang-cindex-python3
language: cpp
-dist: trusty
-sudo: false
matrix:
include:
# This config does a few things:
# - 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
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
+ sources:
+ - deadsnakes
+ packages:
+ - python3.6-dev
+ - python3.6-venv
+ - cmake=2.\*
+ - cmake-data=2.\*
+ - os: linux
+ dist: trusty
env: PYTHON=2.7 CPP=14 GCC=6 CMAKE=1
- - sudo: true
- services: docker
- env: PYTHON=3.5 CPP=14 GCC=6 DEBUG=1
- - sudo: true
+ name: Python 2.7, c++14, gcc 4.8, CMake test
+ addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - g++-6
+ - os: linux
+ dist: trusty
+ name: Python 3.5, c++14, gcc 6, Debug build
+ # N.B. `ensurepip` could be installed transitively by `python3.5-venv`, but
+ # seems to have apt conflicts (at least for Trusty). Use Docker instead.
services: docker
+ env: DOCKER=debian:stretch PYTHON=3.5 CPP=14 GCC=6 DEBUG=1
+ - os: linux
+ dist: xenial
env: PYTHON=3.6 CPP=17 GCC=7
+ name: Python 3.6, c++17, gcc 7
+ addons:
+ apt:
+ sources:
+ - deadsnakes
+ - ubuntu-toolchain-r-test
+ packages:
+ - g++-7
+ - python3.6-dev
+ - python3.6-venv
- os: linux
- env: PYTHON=3.6 CPP=17 CLANG=5.0
+ dist: xenial
+ env: PYTHON=3.6 CPP=17 CLANG=7
+ name: Python 3.6, c++17, Clang 7
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:
+ - 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
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
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
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
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
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.
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.
- 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
[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.
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
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,
Jason Rhinelander,
Boris Schäling,
Pim Schellart,
+Henry Schreiner,
Ivan Smirnov, and
Patrick Stewart.
.. 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.
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.
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
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
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
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
+------------------------------------+---------------------------+-------------------------------+
| ``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` |
====================
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<>``
}, 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
--------------------
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
.. 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);
}
};
-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
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);
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
.. 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!"
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, ); }
};
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
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
.def(py::self *= float())
.def(float() * py::self)
.def(py::self * float())
+ .def(-py::self)
.def("__repr__", &Vector2::toString);
}
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:
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:
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.
.. 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
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.
.. 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.
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
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
==========================================
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:
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:
// 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
=====================================
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) }
);
});
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");
/* 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,
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)];
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)
-----------------------------------------------------
* 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>`_.
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>`_.
<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
.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`,
.. _inheritance:
-Inheritance and automatic upcasting
-===================================
+Inheritance and automatic downcasting
+=====================================
Suppose now that the example consists of two data structures with an
inheritance relationship:
>>> 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'
>>> 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
.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.
>>> 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_``
.. 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
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
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
# 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.
"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``"
========================================================================
cmake -DPYTHON_EXECUTABLE:FILEPATH=<path-to-python-executable> .
+.. _faq_reference_arguments:
+
Limitations involving reference arguments
=========================================
.. 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; });
}
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}
+ }
- 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
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.
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>`_ )
.. 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
==========
/// 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;
/// 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;
}
};
-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);
#include <array>
#include <limits>
#include <tuple>
+#include <type_traits>
#if defined(PYBIND11_CPP17)
# if defined(__has_include)
}
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) :
{}
// 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} {}
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)
// 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;
}
// 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)
: 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) { }
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) {
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); }
"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)
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(); \
}
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
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"));
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;
};
#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
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;
}
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");
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)");
// 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
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;
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
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>) "
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>
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 {
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>
// 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;
}
};
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;
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);
/// 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;
/// 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;
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{});
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)
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());
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 */) {
// 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);
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>> {
#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;
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
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;
}
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
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;
}
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
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;
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");
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) {
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;
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;
if (module) // Needed by pydoc
setattr((PyObject *) type, "__module__", module);
+ PYBIND11_SET_OLDPY_QUALNAME(type, qualname);
+
return (PyObject *) type;
}
# 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
// 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)
#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
#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
#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
#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
#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
#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(); \
} \
#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***
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()
#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)
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;
* (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 (&
: 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> { };
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)
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...> *);
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
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;
/// 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 {
};
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");
/*
- 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>
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
-
-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>()) { }
-
- constexpr const char *text() const { return m_text; }
- constexpr const std::type_info * const * types() const { return m_types; }
-
- 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>());
- }
+#if !defined(_MSC_VER)
+# define PYBIND11_DESCR_CONSTEXPR static constexpr
+#else
+# define PYBIND11_DESCR_CONSTEXPR const
+#endif
-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 }
- );
- }
+/* Concatenate type signatures at compile time */
+template <size_t N, typename... Ts>
+struct descr {
+ char text[N + 1];
+
+ constexpr descr() : text{'\0'} { }
+ constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence<N>()) { }
+
+ template <size_t... Is>
+ constexpr descr(char const (&s)[N+1], index_sequence<Is...>) : text{s[Is]..., '\0'} { }
-protected:
- char m_text[Size1 + 1];
- const std::type_info * m_types[Size2 + 1];
+ 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}};
+ }
};
-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 });
-}
-
-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 *));
+template <typename Type> constexpr descr<1, Type> _() { return {'%'}; }
- delete[] m_text; delete[] m_types;
- delete[] d2.m_text; delete[] d2.m_types;
-
- return r;
- }
+constexpr descr<0> concat() { return {}; }
- char *text() { return m_text; }
- const std::type_info * * types() { return m_types; }
+template <size_t N, typename... Ts>
+constexpr descr<N, Ts...> concat(const descr<N, Ts...> &descr) { return descr; }
-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 <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 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...);
}
-template <size_t Size> PYBIND11_NOINLINE descr _() {
- const std::type_info *types[1] = { nullptr };
- return descr(std::to_string(Size).c_str(), types);
+template <size_t N, typename... Ts>
+constexpr descr<N + 2, Ts...> type_descr(const descr<N, Ts...> &descr) {
+ return _("{") + descr + _("}");
}
-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)
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;
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
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...);
}
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...);
}
!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...);
}
};
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...);
}
};
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
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
};
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);
};
/// 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 ""
#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
//
// 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:
#include <cxxabi.h>
#endif
+#include "common.h"
+
NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
NAMESPACE_BEGIN(detail)
/// Erase all occurrences of a substring
# 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.
}
}
- 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;
-
- 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 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;
+
+ 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,
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());
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; }
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:
}
}
- 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
}
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
}
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)
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())
// 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]);
}
}
- 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;
}
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)
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;
*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() {
// 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());
}
}
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();
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)
#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)
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,
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 {
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
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.
/// 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);
}
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;
}
// 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());
}
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();
}
};
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>
}
};
-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_
};
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 };
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);
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(); }
};
};
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 *&)) {
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());
// - 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
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());
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);
}
#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) \
#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__
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;
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)
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>
}
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)
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) {
#pragma once
-#if defined(_MSC_VER)
+#if defined(__INTEL_COMPILER)
+# pragma warning push
+# pragma warning disable 68 // integer conversion resulted in a change of sign
+# pragma warning disable 186 // pointless comparison of unsigned integer with zero
+# pragma warning disable 878 // incompatible exception specifications
+# pragma warning disable 1334 // the "template" keyword used for syntactic disambiguation may only be used within a template
+# pragma warning disable 1682 // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem)
+# pragma warning disable 1786 // function "strdup" was declared deprecated
+# pragma warning disable 1875 // offsetof applied to non-POD (Plain Old Data) types is nonstandard
+# pragma warning disable 2196 // warning #2196: routine is both "inline" and "noinline"
+#elif defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name
# pragma warning(disable: 4702) // warning C4702: unreachable code
# pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified
-#elif defined(__INTEL_COMPILER)
-# pragma warning(push)
-# pragma warning(disable: 68) // integer conversion resulted in a change of sign
-# pragma warning(disable: 186) // pointless comparison of unsigned integer with zero
-# pragma warning(disable: 878) // incompatible exception specifications
-# pragma warning(disable: 1334) // the "template" keyword used for syntactic disambiguation may only be used within a template
-# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem)
-# pragma warning(disable: 1875) // offsetof applied to non-POD (Plain Old Data) types is nonstandard
-# pragma warning(disable: 2196) // warning #2196: routine is both "inline" and "noinline"
#elif defined(__GNUG__) && !defined(__clang__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "detail/class.h"
#include "detail/init.h"
+#if defined(__GNUG__) && !defined(__clang__)
+# include <cxxabi.h>
+#endif
+
NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object
class cpp_function : public function {
public:
cpp_function() { }
+ cpp_function(std::nullptr_t) { }
/// Construct a cpp_function from a vanilla function pointer
template <typename Return, typename... Args, typename... Extra>
/// Special internal constructor for functors, lambda functions, etc.
template <typename Func, typename Return, typename... Args, typename... Extra>
void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) {
-
- struct capture { detail::remove_reference_t<Func> f; };
+ using namespace detail;
+ struct capture { remove_reference_t<Func> f; };
/* Store the function including any extra state it might have (e.g. a lambda capture object) */
auto rec = make_function_record();
# pragma GCC diagnostic pop
#endif
if (!std::is_trivially_destructible<Func>::value)
- rec->free_data = [](detail::function_record *r) { ((capture *) &r->data)->~capture(); };
+ rec->free_data = [](function_record *r) { ((capture *) &r->data)->~capture(); };
} else {
rec->data[0] = new capture { std::forward<Func>(f) };
- rec->free_data = [](detail::function_record *r) { delete ((capture *) r->data[0]); };
+ rec->free_data = [](function_record *r) { delete ((capture *) r->data[0]); };
}
/* Type casters for the function arguments and return value */
- using cast_in = detail::argument_loader<Args...>;
- using cast_out = detail::make_caster<
- detail::conditional_t<std::is_void<Return>::value, detail::void_type, Return>
+ using cast_in = argument_loader<Args...>;
+ using cast_out = make_caster<
+ conditional_t<std::is_void<Return>::value, void_type, Return>
>;
- static_assert(detail::expected_num_args<Extra...>(sizeof...(Args), cast_in::has_args, cast_in::has_kwargs),
+ static_assert(expected_num_args<Extra...>(sizeof...(Args), cast_in::has_args, cast_in::has_kwargs),
"The number of argument annotations does not match the number of function arguments");
/* Dispatch code which converts function arguments and performs the actual function call */
- rec->impl = [](detail::function_call &call) -> handle {
+ rec->impl = [](function_call &call) -> handle {
cast_in args_converter;
/* Try to cast the function arguments into the C++ domain */
return PYBIND11_TRY_NEXT_OVERLOAD;
/* Invoke call policy pre-call hook */
- detail::process_attributes<Extra...>::precall(call);
+ process_attributes<Extra...>::precall(call);
/* Get a pointer to the capture object */
auto data = (sizeof(capture) <= sizeof(call.func.data)
capture *cap = const_cast<capture *>(reinterpret_cast<const capture *>(data));
/* Override policy for rvalues -- usually to enforce rvp::move on an rvalue */
- const auto policy = detail::return_value_policy_override<Return>::policy(call.func.policy);
+ return_value_policy policy = return_value_policy_override<Return>::policy(call.func.policy);
/* Function scope guard -- defaults to the compile-to-nothing `void_type` */
- using Guard = detail::extract_guard_t<Extra...>;
+ using Guard = extract_guard_t<Extra...>;
/* Perform the function call */
handle result = cast_out::cast(
std::move(args_converter).template call<Return, Guard>(cap->f), policy, call.parent);
/* Invoke call policy post-call hook */
- detail::process_attributes<Extra...>::postcall(call, result);
+ process_attributes<Extra...>::postcall(call, result);
return result;
};
/* Process any user-provided function attributes */
- detail::process_attributes<Extra...>::init(extra..., rec);
+ process_attributes<Extra...>::init(extra..., rec);
/* Generate a readable signature describing the function's arguments and return value types */
- using detail::descr; using detail::_;
- PYBIND11_DESCR signature = _("(") + cast_in::arg_names() + _(") -> ") + cast_out::name();
+ static constexpr auto signature = _("(") + cast_in::arg_names + _(") -> ") + cast_out::name;
+ PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types();
/* Register the function with Python from generic (non-templated) code */
- initialize_generic(rec, signature.text(), signature.types(), sizeof...(Args));
+ initialize_generic(rec, signature.text, types.data(), sizeof...(Args));
if (cast_in::has_args) rec->has_args = true;
if (cast_in::has_kwargs) rec->has_kwargs = true;
/* Generate a proper function signature */
std::string signature;
- size_t type_depth = 0, char_index = 0, type_index = 0, arg_index = 0;
- while (true) {
- char c = text[char_index++];
- if (c == '\0')
- break;
+ size_t type_index = 0, arg_index = 0;
+ for (auto *pc = text; *pc != '\0'; ++pc) {
+ const auto c = *pc;
if (c == '{') {
- // Write arg name for everything except *args, **kwargs and return type.
- if (type_depth == 0 && text[char_index] != '*' && arg_index < args) {
- if (!rec->args.empty() && rec->args[arg_index].name) {
- signature += rec->args[arg_index].name;
- } else if (arg_index == 0 && rec->is_method) {
- signature += "self";
- } else {
- signature += "arg" + std::to_string(arg_index - (rec->is_method ? 1 : 0));
- }
- signature += ": ";
+ // Write arg name for everything except *args and **kwargs.
+ if (*(pc + 1) == '*')
+ continue;
+
+ if (arg_index < rec->args.size() && rec->args[arg_index].name) {
+ signature += rec->args[arg_index].name;
+ } else if (arg_index == 0 && rec->is_method) {
+ signature += "self";
+ } else {
+ signature += "arg" + std::to_string(arg_index - (rec->is_method ? 1 : 0));
}
- ++type_depth;
+ signature += ": ";
} else if (c == '}') {
- --type_depth;
- if (type_depth == 0) {
- if (arg_index < rec->args.size() && rec->args[arg_index].descr) {
- signature += "=";
- signature += rec->args[arg_index].descr;
- }
- arg_index++;
+ // Write default value if available.
+ if (arg_index < rec->args.size() && rec->args[arg_index].descr) {
+ signature += " = ";
+ signature += rec->args[arg_index].descr;
}
+ arg_index++;
} else if (c == '%') {
const std::type_info *t = types[type_index++];
if (!t)
pybind11_fail("Internal error while parsing type signature (1)");
if (auto tinfo = detail::get_type_info(*t)) {
-#if defined(PYPY_VERSION)
- signature += handle((PyObject *) tinfo->type)
- .attr("__module__")
- .cast<std::string>() + ".";
-#endif
- signature += tinfo->type->tp_name;
+ handle th((PyObject *) tinfo->type);
+ signature +=
+ th.attr("__module__").cast<std::string>() + "." +
+ th.attr("__qualname__").cast<std::string>(); // Python 3.3+, but we backport it to earlier versions
} else if (rec->is_new_style_constructor && arg_index == 0) {
// A new-style `__init__` takes `self` as `value_and_holder`.
// Rewrite it to the proper class type.
-#if defined(PYPY_VERSION)
- signature += rec->scope.attr("__module__").cast<std::string>() + ".";
-#endif
- signature += ((PyTypeObject *) rec->scope.ptr())->tp_name;
+ signature +=
+ rec->scope.attr("__module__").cast<std::string>() + "." +
+ rec->scope.attr("__qualname__").cast<std::string>();
} else {
std::string tname(t->name());
detail::clean_type_id(tname);
signature += c;
}
}
- if (type_depth != 0 || types[type_index] != nullptr)
+ if (arg_index != args || types[type_index] != nullptr)
pybind11_fail("Internal error while parsing type signature (2)");
- #if !defined(PYBIND11_CONSTEXPR_DESCR)
- delete[] types;
- delete[] text;
- #endif
-
#if PY_MAJOR_VERSION < 3
if (strcmp(rec->name, "__next__") == 0) {
std::free(rec->name);
using namespace detail;
/* Iterator over the list of potentially admissible overloads */
- function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr),
- *it = overloads;
+ const function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr),
+ *it = overloads;
/* Need to know how many arguments + keyword arguments there are to pick the right overload */
const size_t n_args_in = (size_t) PyTuple_GET_SIZE(args_in);
result other than PYBIND11_TRY_NEXT_OVERLOAD.
*/
- function_record &func = *it;
+ const function_record &func = *it;
size_t pos_args = func.nargs; // Number of positional arguments that we need
if (func.has_args) --pos_args; // (but don't count py::args
if (func.has_kwargs) --pos_args; // or py::kwargs)
function_call call(func, parent);
- size_t args_to_copy = std::min(pos_args, n_args_in);
+ size_t args_to_copy = (std::min)(pos_args, n_args_in); // Protect std::min with parentheses
size_t args_copied = 0;
// 0. Inject new-style `self` argument
// 1. Copy any position arguments given.
bool bad_arg = false;
for (; args_copied < args_to_copy; ++args_copied) {
- argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr;
+ const argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr;
if (kwargs_in && arg_rec && arg_rec->name && PyDict_GetItemString(kwargs_in, arg_rec->name)) {
bad_arg = true;
break;
continue; // Unconsumed kwargs, but no py::kwargs argument to accept them
// 4a. If we have a py::args argument, create a new tuple with leftovers
- tuple extra_args;
if (func.has_args) {
+ tuple extra_args;
if (args_to_copy == 0) {
// We didn't copy out any position arguments from the args_in tuple, so we
// can reuse it directly without copying:
size_t args_size = n_args_in - args_copied;
extra_args = tuple(args_size);
for (size_t i = 0; i < args_size; ++i) {
- handle item = PyTuple_GET_ITEM(args_in, args_copied + i);
- extra_args[i] = item.inc_ref().ptr();
+ extra_args[i] = PyTuple_GET_ITEM(args_in, args_copied + i);
}
}
call.args.push_back(extra_args);
call.args_convert.push_back(false);
+ call.args_ref = std::move(extra_args);
}
// 4b. If we have a py::kwargs, pass on any remaining kwargs
kwargs = dict(); // If we didn't get one, send an empty one
call.args.push_back(kwargs);
call.args_convert.push_back(false);
+ call.kwargs_ref = std::move(kwargs);
}
// 5. Put everything in a vector. Not technically step 5, we've been building it
result = PYBIND11_TRY_NEXT_OVERLOAD;
}
- if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD)
+ if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) {
+ // The error reporting logic below expects 'it' to be valid, as it would be
+ // if we'd encountered this failure in the first-pass loop.
+ if (!result)
+ it = &call.func;
break;
+ }
}
}
} catch (error_already_set &e) {
e.restore();
return nullptr;
+#if defined(__GNUG__) && !defined(__clang__)
+ } catch ( abi::__forced_unwind& ) {
+ throw;
+#endif
} catch (...) {
/* When an exception is caught, give each registered exception
translator a chance to translate it to a Python exception
" arguments. The following argument types are supported:\n";
int ctr = 0;
- for (function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) {
+ for (const function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) {
msg += " "+ std::to_string(++ctr) + ". ";
bool wrote_sig = false;
tinfo->type = (PyTypeObject *) m_ptr;
tinfo->cpptype = rec.type;
tinfo->type_size = rec.type_size;
+ tinfo->type_align = rec.type_align;
tinfo->operator_new = rec.operator_new;
tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size);
tinfo->init_instance = rec.init_instance;
tinfo->get_buffer_data = get_buffer_data;
}
+ // rec_func must be set for either fget or fset.
void def_property_static_impl(const char *name,
handle fget, handle fset,
- detail::function_record *rec_fget) {
- const auto is_static = !(rec_fget->is_method && rec_fget->scope);
- const auto has_doc = rec_fget->doc && pybind11::options::show_user_defined_docstrings();
-
+ detail::function_record *rec_func) {
+ const auto is_static = rec_func && !(rec_func->is_method && rec_func->scope);
+ const auto has_doc = rec_func && rec_func->doc && pybind11::options::show_user_defined_docstrings();
auto property = handle((PyObject *) (is_static ? get_internals().static_property_type
: &PyProperty_Type));
attr(name) = property(fget.ptr() ? fget : none(),
fset.ptr() ? fset : none(),
/*deleter*/none(),
- pybind11::str(has_doc ? rec_fget->doc : ""));
+ pybind11::str(has_doc ? rec_func->doc : ""));
}
};
: std::true_type { };
/// Call class-specific delete if it exists or global otherwise. Can also be an overload set.
template <typename T, enable_if_t<has_operator_delete<T>::value, int> = 0>
-void call_operator_delete(T *p, size_t) { T::operator delete(p); }
+void call_operator_delete(T *p, size_t, size_t) { T::operator delete(p); }
template <typename T, enable_if_t<!has_operator_delete<T>::value && has_operator_delete_size<T>::value, int> = 0>
-void call_operator_delete(T *p, size_t s) { T::operator delete(p, s); }
+void call_operator_delete(T *p, size_t s, size_t) { T::operator delete(p, s); }
-inline void call_operator_delete(void *p, size_t) { ::operator delete(p); }
+inline void call_operator_delete(void *p, size_t s, size_t a) {
+ (void)s; (void)a;
+#if defined(PYBIND11_CPP17)
+ if (a > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
+ ::operator delete(p, s, std::align_val_t(a));
+ else
+ ::operator delete(p, s);
+#else
+ ::operator delete(p);
+#endif
+}
NAMESPACE_END(detail)
auto method_adaptor(F &&f) -> decltype(std::forward<F>(f)) { return std::forward<F>(f); }
template <typename Derived, typename Return, typename Class, typename... Args>
-auto method_adaptor(Return (Class::*pmf)(Args...)) -> Return (Derived::*)(Args...) { return pmf; }
+auto method_adaptor(Return (Class::*pmf)(Args...)) -> Return (Derived::*)(Args...) {
+ static_assert(detail::is_accessible_base_of<Class, Derived>::value,
+ "Cannot bind an inaccessible base class method; use a lambda definition instead");
+ return pmf;
+}
template <typename Derived, typename Return, typename Class, typename... Args>
-auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)(Args...) const { return pmf; }
+auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)(Args...) const {
+ static_assert(detail::is_accessible_base_of<Class, Derived>::value,
+ "Cannot bind an inaccessible base class method; use a lambda definition instead");
+ return pmf;
+}
template <typename type_, typename... options>
class class_ : public detail::generic_type {
record.name = name;
record.type = &typeid(type);
record.type_size = sizeof(conditional_t<has_alias, type_alias, type>);
+ record.type_align = alignof(conditional_t<has_alias, type_alias, type>&);
record.holder_size = sizeof(holder_type);
record.init_instance = init_instance;
record.dealloc = dealloc;
- record.default_holder = std::is_same<holder_type, std::unique_ptr<type>>::value;
+ record.default_holder = detail::is_instantiation<std::unique_ptr, holder_type>::value;
set_operator_new<type>(&record);
"def_static(...) called with a non-static member function pointer");
cpp_function cf(std::forward<Func>(f), name(name_), scope(*this),
sibling(getattr(*this, name_, none())), extra...);
- attr(cf.name()) = cf;
+ attr(cf.name()) = staticmethod(cf);
return *this;
}
template <typename C, typename D, typename... Extra>
class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) {
- static_assert(std::is_base_of<C, type>::value, "def_readwrite() requires a class member (or base class member)");
+ static_assert(std::is_same<C, type>::value || std::is_base_of<C, type>::value, "def_readwrite() requires a class member (or base class member)");
cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)),
fset([pm](type &c, const D &value) { c.*pm = value; }, is_method(*this));
def_property(name, fget, fset, return_value_policy::reference_internal, extra...);
template <typename C, typename D, typename... Extra>
class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) {
- static_assert(std::is_base_of<C, type>::value, "def_readonly() requires a class member (or base class member)");
+ static_assert(std::is_same<C, type>::value || std::is_base_of<C, type>::value, "def_readonly() requires a class member (or base class member)");
cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this));
def_property_readonly(name, fget, return_value_policy::reference_internal, extra...);
return *this;
/// Uses cpp_function's return_value_policy by default
template <typename... Extra>
class_ &def_property_readonly(const char *name, const cpp_function &fget, const Extra& ...extra) {
- return def_property(name, fget, cpp_function(), extra...);
+ return def_property(name, fget, nullptr, extra...);
}
/// Uses return_value_policy::reference by default
/// Uses cpp_function's return_value_policy by default
template <typename... Extra>
class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const Extra& ...extra) {
- return def_property_static(name, fget, cpp_function(), extra...);
+ return def_property_static(name, fget, nullptr, extra...);
}
/// Uses return_value_policy::reference_internal by default
/// Uses cpp_function's return_value_policy by default
template <typename... Extra>
class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) {
+ static_assert( 0 == detail::constexpr_sum(std::is_base_of<arg, Extra>::value...),
+ "Argument annotations are not allowed for properties");
auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset);
- char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */
- detail::process_attributes<Extra...>::init(extra..., rec_fget);
- if (rec_fget->doc && rec_fget->doc != doc_prev) {
- free(doc_prev);
- rec_fget->doc = strdup(rec_fget->doc);
+ auto *rec_active = rec_fget;
+ if (rec_fget) {
+ char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */
+ detail::process_attributes<Extra...>::init(extra..., rec_fget);
+ if (rec_fget->doc && rec_fget->doc != doc_prev) {
+ free(doc_prev);
+ rec_fget->doc = strdup(rec_fget->doc);
+ }
}
if (rec_fset) {
- doc_prev = rec_fset->doc;
+ char *doc_prev = rec_fset->doc;
detail::process_attributes<Extra...>::init(extra..., rec_fset);
if (rec_fset->doc && rec_fset->doc != doc_prev) {
free(doc_prev);
rec_fset->doc = strdup(rec_fset->doc);
}
+ if (! rec_active) rec_active = rec_fset;
}
- def_property_static_impl(name, fget, fset, rec_fget);
+ def_property_static_impl(name, fget, fset, rec_active);
return *this;
}
auto sh = std::dynamic_pointer_cast<typename holder_type::element_type>(
v_h.value_ptr<type>()->shared_from_this());
if (sh) {
- new (&v_h.holder<holder_type>()) holder_type(std::move(sh));
+ new (std::addressof(v_h.holder<holder_type>())) holder_type(std::move(sh));
v_h.set_holder_constructed();
}
} catch (const std::bad_weak_ptr &) {}
if (!v_h.holder_constructed() && inst->owned) {
- new (&v_h.holder<holder_type>()) holder_type(v_h.value_ptr<type>());
+ new (std::addressof(v_h.holder<holder_type>())) holder_type(v_h.value_ptr<type>());
v_h.set_holder_constructed();
}
}
static void init_holder_from_existing(const detail::value_and_holder &v_h,
const holder_type *holder_ptr, std::true_type /*is_copy_constructible*/) {
- new (&v_h.holder<holder_type>()) holder_type(*reinterpret_cast<const holder_type *>(holder_ptr));
+ new (std::addressof(v_h.holder<holder_type>())) holder_type(*reinterpret_cast<const holder_type *>(holder_ptr));
}
static void init_holder_from_existing(const detail::value_and_holder &v_h,
const holder_type *holder_ptr, std::false_type /*is_copy_constructible*/) {
- new (&v_h.holder<holder_type>()) holder_type(std::move(*const_cast<holder_type *>(holder_ptr)));
+ new (std::addressof(v_h.holder<holder_type>())) holder_type(std::move(*const_cast<holder_type *>(holder_ptr)));
}
/// Initialize holder object, variant 2: try to construct from existing holder object, if possible
init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible<holder_type>());
v_h.set_holder_constructed();
} else if (inst->owned || detail::always_construct_holder<holder_type>::value) {
- new (&v_h.holder<holder_type>()) holder_type(v_h.value_ptr<type>());
+ new (std::addressof(v_h.holder<holder_type>())) holder_type(v_h.value_ptr<type>());
v_h.set_holder_constructed();
}
}
v_h.set_holder_constructed(false);
}
else {
- detail::call_operator_delete(v_h.value_ptr<type>(), v_h.type->type_size);
+ detail::call_operator_delete(v_h.value_ptr<type>(),
+ v_h.type->type_size,
+ v_h.type->type_align
+ );
}
v_h.value_ptr() = nullptr;
}
return {std::forward<GetState>(g), std::forward<SetState>(s)};
}
+NAMESPACE_BEGIN(detail)
+struct enum_base {
+ enum_base(handle base, handle parent) : m_base(base), m_parent(parent) { }
+
+ PYBIND11_NOINLINE void init(bool is_arithmetic, bool is_convertible) {
+ m_base.attr("__entries") = dict();
+ auto property = handle((PyObject *) &PyProperty_Type);
+ auto static_property = handle((PyObject *) get_internals().static_property_type);
+
+ m_base.attr("__repr__") = cpp_function(
+ [](handle arg) -> str {
+ handle type = arg.get_type();
+ object type_name = type.attr("__name__");
+ dict entries = type.attr("__entries");
+ for (const auto &kv : entries) {
+ object other = kv.second[int_(0)];
+ if (other.equal(arg))
+ return pybind11::str("{}.{}").format(type_name, kv.first);
+ }
+ return pybind11::str("{}.???").format(type_name);
+ }, is_method(m_base)
+ );
+
+ m_base.attr("name") = property(cpp_function(
+ [](handle arg) -> str {
+ dict entries = arg.get_type().attr("__entries");
+ for (const auto &kv : entries) {
+ if (handle(kv.second[int_(0)]).equal(arg))
+ return pybind11::str(kv.first);
+ }
+ return "???";
+ }, is_method(m_base)
+ ));
+
+ m_base.attr("__doc__") = static_property(cpp_function(
+ [](handle arg) -> std::string {
+ std::string docstring;
+ dict entries = arg.attr("__entries");
+ if (((PyTypeObject *) arg.ptr())->tp_doc)
+ docstring += std::string(((PyTypeObject *) arg.ptr())->tp_doc) + "\n\n";
+ docstring += "Members:";
+ for (const auto &kv : entries) {
+ auto key = std::string(pybind11::str(kv.first));
+ auto comment = kv.second[int_(1)];
+ docstring += "\n\n " + key;
+ if (!comment.is_none())
+ docstring += " : " + (std::string) pybind11::str(comment);
+ }
+ return docstring;
+ }
+ ), none(), none(), "");
+
+ m_base.attr("__members__") = static_property(cpp_function(
+ [](handle arg) -> dict {
+ dict entries = arg.attr("__entries"), m;
+ for (const auto &kv : entries)
+ m[kv.first] = kv.second[int_(0)];
+ return m;
+ }), none(), none(), ""
+ );
+
+ #define PYBIND11_ENUM_OP_STRICT(op, expr, strict_behavior) \
+ m_base.attr(op) = cpp_function( \
+ [](object a, object b) { \
+ if (!a.get_type().is(b.get_type())) \
+ strict_behavior; \
+ return expr; \
+ }, \
+ is_method(m_base))
+
+ #define PYBIND11_ENUM_OP_CONV(op, expr) \
+ m_base.attr(op) = cpp_function( \
+ [](object a_, object b_) { \
+ int_ a(a_), b(b_); \
+ return expr; \
+ }, \
+ is_method(m_base))
+
+ #define PYBIND11_ENUM_OP_CONV_LHS(op, expr) \
+ m_base.attr(op) = cpp_function( \
+ [](object a_, object b) { \
+ int_ a(a_); \
+ return expr; \
+ }, \
+ is_method(m_base))
+
+ if (is_convertible) {
+ PYBIND11_ENUM_OP_CONV_LHS("__eq__", !b.is_none() && a.equal(b));
+ PYBIND11_ENUM_OP_CONV_LHS("__ne__", b.is_none() || !a.equal(b));
+
+ if (is_arithmetic) {
+ PYBIND11_ENUM_OP_CONV("__lt__", a < b);
+ PYBIND11_ENUM_OP_CONV("__gt__", a > b);
+ PYBIND11_ENUM_OP_CONV("__le__", a <= b);
+ PYBIND11_ENUM_OP_CONV("__ge__", a >= b);
+ PYBIND11_ENUM_OP_CONV("__and__", a & b);
+ PYBIND11_ENUM_OP_CONV("__rand__", a & b);
+ PYBIND11_ENUM_OP_CONV("__or__", a | b);
+ PYBIND11_ENUM_OP_CONV("__ror__", a | b);
+ PYBIND11_ENUM_OP_CONV("__xor__", a ^ b);
+ PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b);
+ m_base.attr("__invert__") = cpp_function(
+ [](object arg) { return ~(int_(arg)); }, is_method(m_base));
+ }
+ } else {
+ PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false);
+ PYBIND11_ENUM_OP_STRICT("__ne__", !int_(a).equal(int_(b)), return true);
+
+ if (is_arithmetic) {
+ #define PYBIND11_THROW throw type_error("Expected an enumeration of matching type!");
+ PYBIND11_ENUM_OP_STRICT("__lt__", int_(a) < int_(b), PYBIND11_THROW);
+ PYBIND11_ENUM_OP_STRICT("__gt__", int_(a) > int_(b), PYBIND11_THROW);
+ PYBIND11_ENUM_OP_STRICT("__le__", int_(a) <= int_(b), PYBIND11_THROW);
+ PYBIND11_ENUM_OP_STRICT("__ge__", int_(a) >= int_(b), PYBIND11_THROW);
+ #undef PYBIND11_THROW
+ }
+ }
+
+ #undef PYBIND11_ENUM_OP_CONV_LHS
+ #undef PYBIND11_ENUM_OP_CONV
+ #undef PYBIND11_ENUM_OP_STRICT
+
+ object getstate = cpp_function(
+ [](object arg) { return int_(arg); }, is_method(m_base));
+
+ m_base.attr("__getstate__") = getstate;
+ m_base.attr("__hash__") = getstate;
+ }
+
+ PYBIND11_NOINLINE void value(char const* name_, object value, const char *doc = nullptr) {
+ dict entries = m_base.attr("__entries");
+ str name(name_);
+ if (entries.contains(name)) {
+ std::string type_name = (std::string) str(m_base.attr("__name__"));
+ throw value_error(type_name + ": element \"" + std::string(name_) + "\" already exists!");
+ }
+
+ entries[name] = std::make_pair(value, doc);
+ m_base.attr(name) = value;
+ }
+
+ PYBIND11_NOINLINE void export_values() {
+ dict entries = m_base.attr("__entries");
+ for (const auto &kv : entries)
+ m_parent.attr(kv.first) = kv.second[int_(0)];
+ }
+
+ handle m_base;
+ handle m_parent;
+};
+
+NAMESPACE_END(detail)
+
/// Binds C++ enumerations and enumeration classes to Python
template <typename Type> class enum_ : public class_<Type> {
public:
- using class_<Type>::def;
- using class_<Type>::def_property_readonly_static;
+ using Base = class_<Type>;
+ using Base::def;
+ using Base::attr;
+ using Base::def_property_readonly;
+ using Base::def_property_readonly_static;
using Scalar = typename std::underlying_type<Type>::type;
template <typename... Extra>
enum_(const handle &scope, const char *name, const Extra&... extra)
- : class_<Type>(scope, name, extra...), m_entries(), m_parent(scope) {
-
+ : class_<Type>(scope, name, extra...), m_base(*this, scope) {
constexpr bool is_arithmetic = detail::any_of<std::is_same<arithmetic, Extra>...>::value;
+ constexpr bool is_convertible = std::is_convertible<Type, Scalar>::value;
+ m_base.init(is_arithmetic, is_convertible);
- auto m_entries_ptr = m_entries.inc_ref().ptr();
- def("__repr__", [name, m_entries_ptr](Type value) -> pybind11::str {
- for (const auto &kv : reinterpret_borrow<dict>(m_entries_ptr)) {
- if (pybind11::cast<Type>(kv.second) == value)
- return pybind11::str("{}.{}").format(name, kv.first);
- }
- return pybind11::str("{}.???").format(name);
- });
- def_property_readonly_static("__members__", [m_entries_ptr](object /* self */) {
- dict m;
- for (const auto &kv : reinterpret_borrow<dict>(m_entries_ptr))
- m[kv.first] = kv.second;
- return m;
- }, return_value_policy::copy);
def(init([](Scalar i) { return static_cast<Type>(i); }));
def("__int__", [](Type value) { return (Scalar) value; });
#if PY_MAJOR_VERSION < 3
def("__long__", [](Type value) { return (Scalar) value; });
#endif
- def("__eq__", [](const Type &value, Type *value2) { return value2 && value == *value2; });
- def("__ne__", [](const Type &value, Type *value2) { return !value2 || value != *value2; });
- if (is_arithmetic) {
- def("__lt__", [](const Type &value, Type *value2) { return value2 && value < *value2; });
- def("__gt__", [](const Type &value, Type *value2) { return value2 && value > *value2; });
- def("__le__", [](const Type &value, Type *value2) { return value2 && value <= *value2; });
- def("__ge__", [](const Type &value, Type *value2) { return value2 && value >= *value2; });
- }
- if (std::is_convertible<Type, Scalar>::value) {
- // Don't provide comparison with the underlying type if the enum isn't convertible,
- // i.e. if Type is a scoped enum, mirroring the C++ behaviour. (NB: we explicitly
- // convert Type to Scalar below anyway because this needs to compile).
- def("__eq__", [](const Type &value, Scalar value2) { return (Scalar) value == value2; });
- def("__ne__", [](const Type &value, Scalar value2) { return (Scalar) value != value2; });
- if (is_arithmetic) {
- def("__lt__", [](const Type &value, Scalar value2) { return (Scalar) value < value2; });
- def("__gt__", [](const Type &value, Scalar value2) { return (Scalar) value > value2; });
- def("__le__", [](const Type &value, Scalar value2) { return (Scalar) value <= value2; });
- def("__ge__", [](const Type &value, Scalar value2) { return (Scalar) value >= value2; });
- def("__invert__", [](const Type &value) { return ~((Scalar) value); });
- def("__and__", [](const Type &value, Scalar value2) { return (Scalar) value & value2; });
- def("__or__", [](const Type &value, Scalar value2) { return (Scalar) value | value2; });
- def("__xor__", [](const Type &value, Scalar value2) { return (Scalar) value ^ value2; });
- def("__rand__", [](const Type &value, Scalar value2) { return (Scalar) value & value2; });
- def("__ror__", [](const Type &value, Scalar value2) { return (Scalar) value | value2; });
- def("__rxor__", [](const Type &value, Scalar value2) { return (Scalar) value ^ value2; });
- def("__and__", [](const Type &value, const Type &value2) { return (Scalar) value & (Scalar) value2; });
- def("__or__", [](const Type &value, const Type &value2) { return (Scalar) value | (Scalar) value2; });
- def("__xor__", [](const Type &value, const Type &value2) { return (Scalar) value ^ (Scalar) value2; });
- }
- }
- def("__hash__", [](const Type &value) { return (Scalar) value; });
- // Pickling and unpickling -- needed for use with the 'multiprocessing' module
- def(pickle([](const Type &value) { return pybind11::make_tuple((Scalar) value); },
- [](tuple t) { return static_cast<Type>(t[0].cast<Scalar>()); }));
+ #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 8
+ def("__index__", [](Type value) { return (Scalar) value; });
+ #endif
+
+ cpp_function setstate(
+ [](Type &value, Scalar arg) { value = static_cast<Type>(arg); },
+ is_method(*this));
+ attr("__setstate__") = setstate;
}
/// Export enumeration entries into the parent scope
enum_& export_values() {
- for (const auto &kv : m_entries)
- m_parent.attr(kv.first) = kv.second;
+ m_base.export_values();
return *this;
}
/// Add an enumeration entry
- enum_& value(char const* name, Type value) {
- auto v = pybind11::cast(value, return_value_policy::copy);
- this->attr(name) = v;
- m_entries[pybind11::str(name)] = v;
+ enum_& value(char const* name, Type value, const char *doc = nullptr) {
+ m_base.value(name, pybind11::cast(value, return_value_policy::copy), doc);
return *this;
}
private:
- dict m_entries;
- handle m_parent;
+ detail::enum_base m_base;
};
NAMESPACE_BEGIN(detail)
template <typename type>
class exception : public object {
public:
+ exception() = default;
exception(handle scope, const char *name, PyObject *base = PyExc_Exception) {
std::string full_name = scope.attr("__name__").cast<std::string>() +
std::string(".") + name;
}
};
+NAMESPACE_BEGIN(detail)
+// Returns a reference to a function-local static exception object used in the simple
+// register_exception approach below. (It would be simpler to have the static local variable
+// directly in register_exception, but that makes clang <3.5 segfault - issue #1349).
+template <typename CppException>
+exception<CppException> &get_exception_object() { static exception<CppException> ex; return ex; }
+NAMESPACE_END(detail)
+
/**
* Registers a Python exception in `m` of the given `name` and installs an exception translator to
* translate the C++ exception to the created Python exception using the exceptions what() method.
exception<CppException> ®ister_exception(handle scope,
const char *name,
PyObject *base = PyExc_Exception) {
- static exception<CppException> ex(scope, name, base);
+ auto &ex = detail::get_exception_object<CppException>();
+ if (!ex) ex = exception<CppException>(scope, name, base);
+
register_exception_translator([](std::exception_ptr p) {
if (!p) return;
try {
std::rethrow_exception(p);
} catch (const CppException &e) {
- ex(e.what());
+ detail::get_exception_object<CppException>()(e.what());
}
});
return ex;
public:
PYBIND11_NOINLINE gil_scoped_acquire() {
auto const &internals = detail::get_internals();
- tstate = (PyThreadState *) PyThread_get_key_value(internals.tstate);
+ tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate);
+
+ if (!tstate) {
+ /* Check if the GIL was acquired using the PyGILState_* API instead (e.g. if
+ calling from a Python thread). Since we use a different key, this ensures
+ we don't create a new thread state and deadlock in PyEval_AcquireThread
+ below. Note we don't save this state with internals.tstate, since we don't
+ create it we would fail to clear it (its reference count should be > 0). */
+ tstate = PyGILState_GetThisThreadState();
+ }
if (!tstate) {
tstate = PyThreadState_New(internals.istate);
pybind11_fail("scoped_acquire: could not create thread state!");
#endif
tstate->gilstate_counter = 0;
- #if PY_MAJOR_VERSION < 3
- PyThread_delete_key_value(internals.tstate);
- #endif
- PyThread_set_key_value(internals.tstate, tstate);
+ PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate);
} else {
release = detail::get_thread_state_unchecked() != tstate;
}
#endif
PyThreadState_Clear(tstate);
PyThreadState_DeleteCurrent();
- PyThread_delete_key_value(detail::get_internals().tstate);
+ PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate);
release = false;
}
}
tstate = PyEval_SaveThread();
if (disassoc) {
auto key = internals.tstate;
- #if PY_MAJOR_VERSION < 3
- PyThread_delete_key_value(key);
- #else
- PyThread_set_key_value(key, nullptr);
- #endif
+ PYBIND11_TLS_DELETE_VALUE(key);
}
}
~gil_scoped_release() {
PyEval_RestoreThread(tstate);
if (disassoc) {
auto key = detail::get_internals().tstate;
- #if PY_MAJOR_VERSION < 3
- PyThread_delete_key_value(key);
- #endif
- PyThread_set_key_value(key, tstate);
+ PYBIND11_TLS_REPLACE_VALUE(key, tstate);
}
}
private:
#endif
error_already_set::~error_already_set() {
- if (type) {
+ if (m_type) {
gil_scoped_acquire gil;
- type.release().dec_ref();
- value.release().dec_ref();
- trace.release().dec_ref();
+ error_scope scope;
+ m_type.release().dec_ref();
+ m_value.release().dec_ref();
+ m_trace.release().dec_ref();
}
}
return overload;
}
+/** \rst
+ Try to retrieve a python method by the provided name from the instance pointed to by the this_ptr.
+
+ :this_ptr: The pointer to the object the overload should be retrieved for. This should be the first
+ non-trampoline class encountered in the inheritance chain.
+ :name: The name of the overloaded Python method to retrieve.
+ :return: The Python method by this name from the object or an empty function wrapper.
+ \endrst */
template <class T> function get_overload(const T *this_ptr, const char *name) {
auto tinfo = detail::get_type_info(typeid(T));
return tinfo ? get_type_overload(this_ptr, tinfo, name) : function();
} \
}
+/** \rst
+ Macro to populate the virtual method in the trampoline class. This macro tries to look up a method named 'fn'
+ from the Python side, deals with the :ref:`gil` and necessary argument conversions to call this method and return
+ the appropriate type. See :ref:`overriding_virtuals` for more information. This macro should be used when the method
+ name in C is not the same as the method name in Python. For example with `__str__`.
+
+ .. code-block:: cpp
+
+ std::string toString() override {
+ PYBIND11_OVERLOAD_NAME(
+ std::string, // Return type (ret_type)
+ Animal, // Parent class (cname)
+ toString, // Name of function in C++ (name)
+ "__str__", // Name of method in Python (fn)
+ );
+ }
+\endrst */
#define PYBIND11_OVERLOAD_NAME(ret_type, cname, name, fn, ...) \
- PYBIND11_OVERLOAD_INT(ret_type, cname, name, __VA_ARGS__) \
+ PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \
return cname::fn(__VA_ARGS__)
+/** \rst
+ Macro for pure virtual functions, this function is identical to :c:macro:`PYBIND11_OVERLOAD_NAME`, except that it
+ throws if no overload can be found.
+\endrst */
#define PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, name, fn, ...) \
- PYBIND11_OVERLOAD_INT(ret_type, cname, name, __VA_ARGS__) \
- pybind11::pybind11_fail("Tried to call pure virtual function \"" #cname "::" name "\"");
-
+ PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \
+ pybind11::pybind11_fail("Tried to call pure virtual function \"" PYBIND11_STRINGIFY(cname) "::" name "\"");
+
+/** \rst
+ Macro to populate the virtual method in the trampoline class. This macro tries to look up the method
+ from the Python side, deals with the :ref:`gil` and necessary argument conversions to call this method and return
+ the appropriate type. This macro should be used if the method name in C and in Python are identical.
+ See :ref:`overriding_virtuals` for more information.
+
+ .. code-block:: cpp
+
+ class PyAnimal : public Animal {
+ public:
+ // Inherit the constructors
+ using Animal::Animal;
+
+ // Trampoline (need one for each virtual function)
+ std::string go(int n_times) override {
+ PYBIND11_OVERLOAD_PURE(
+ std::string, // Return type (ret_type)
+ Animal, // Parent class (cname)
+ go, // Name of function in C++ (must match Python name) (fn)
+ n_times // Argument(s) (...)
+ );
+ }
+ };
+\endrst */
#define PYBIND11_OVERLOAD(ret_type, cname, fn, ...) \
- PYBIND11_OVERLOAD_NAME(ret_type, cname, #fn, fn, __VA_ARGS__)
+ PYBIND11_OVERLOAD_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__)
+/** \rst
+ Macro for pure virtual functions, this function is identical to :c:macro:`PYBIND11_OVERLOAD`, except that it throws
+ if no overload can be found.
+\endrst */
#define PYBIND11_OVERLOAD_PURE(ret_type, cname, fn, ...) \
- PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, #fn, fn, __VA_ARGS__)
+ PYBIND11_OVERLOAD_PURE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__)
NAMESPACE_END(PYBIND11_NAMESPACE)
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
# pragma warning(pop)
-#elif defined(__INTEL_COMPILER)
-/* Leave ignored warnings on */
#elif defined(__GNUG__) && !defined(__clang__)
# pragma GCC diagnostic pop
#endif
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;
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)
/// 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")
/// 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 _
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(); }
// 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;
private:
handle obj;
- PyObject *key, *value;
+ PyObject *key = nullptr, *value = nullptr;
ssize_t pos = -1;
};
NAMESPACE_END(iterator_policies)
}
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) { }
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)
(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 {
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)}; }
};
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
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)}; }
};
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) };
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 {
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();
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();
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)
#include <unordered_map>
#include <iostream>
#include <list>
+#include <deque>
#include <valarray>
#if defined(_MSC_VER)
# 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
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));
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 {
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;
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();
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) {
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> { };
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;
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>>
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);
}
return true;
}
- PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name() + _("]"));
+ PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name + _("]"));
};
#ifdef PYBIND11_HAS_OPTIONAL
}
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) {
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"),
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();
"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."
);
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"),
);
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;
}
);
);
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``"
);
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
);
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];
}
);
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);
auto it = m.find(k);
if (it == m.end())
throw key_error();
- return m.erase(it);
+ m.erase(it);
}
);
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)
-version_info = (2, 2, 1)
+version_info = (2, 4, 1)
__version__ = '.'.join(map(str, version_info))
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
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,
# Full set of test files (you can override these; see below)
set(PYBIND11_TEST_FILES
+ test_async.cpp
test_buffers.cpp
test_builtin_casters.cpp
test_call_policies.cpp
test_eval.cpp
test_exceptions.cpp
test_factory_constructors.cpp
+ test_gil_scoped.cpp
test_iostream.cpp
test_kwargs_and_defaults.cpp
test_local_bindings.cpp
test_smart_ptr.cpp
test_stl.cpp
test_stl_binders.cpp
+ test_tagbased_polymorphic.cpp
+ test_union.cpp
test_virtual_functions.cpp
)
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
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).
# 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)
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)
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()
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})
_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"""
self.capfd.readouterr()
return self
- def __exit__(self, *_):
+ def __exit__(self, *args):
self.out, self.err = self.capfd.readouterr()
def __eq__(self, other):
gc.collect()
-def pytest_namespace():
+def pytest_configure():
"""Add import suppression and test requirements to `pytest` namespace"""
try:
import numpy as np
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():
}
}
}
- 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
--- /dev/null
+/*
+ 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
+}
ignore::ImportWarning
# bogus numpy ABI warning (see numpy/#432)
ignore:.*numpy.dtype size changed.*:RuntimeWarning
+ ignore:.*numpy.ufunc size changed.*:RuntimeWarning
--- /dev/null
+/*
+ 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__")();
+ });
+}
--- /dev/null
+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()))
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!");
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) }
);
})
# 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
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
// 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; });
// 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;
+ });
}
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
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)
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()
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")
#include "pybind11_tests.h"
#include "constructor_stats.h"
#include <pybind11/functional.h>
+#include <thread>
int dummy_function(int i) { return i + 1; }
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));
+ });
}
import pytest
from pybind11_tests import callbacks as m
+from threading import Thread
def test_callbacks():
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()
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(); });
// 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;
+ });
}
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)
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)
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))
#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");
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 { };
.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
.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; };
"""
+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"
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():
# 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
+ '''
+
+
+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!")
+
-Invoked with: 0'''
+def test_aligned():
+ if hasattr(m, "Aligned"):
+ p = m.Aligned().ptr()
+ assert p % 1024 == 0
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; }
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
};
}
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) {
#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>;
// 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)
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)
def assert_sparse_equal_ref(sparse_mat):
- assert_equal_ref(sparse_mat.todense())
+ assert_equal_ref(sparse_mat.toarray())
def test_fixed():
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)
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():
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"""
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()
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)
#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);
--- /dev/null
+#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());
+ });
+}
#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>
};
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") {
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();
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
// 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
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();
+ });
}
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__ = {}
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}
+
+ for docstring_line in '''An unscoped enumeration
+
+Members:
+
+ EOne : Docstring for EOne
+
+ ETwo : Docstring for ETwo
- # no TypeError exception for unscoped enum ==/!= int comparisons
+ 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"
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):
+ z > 3
with pytest.raises(TypeError):
- assert z == 2
+ z < 3
with pytest.raises(TypeError):
- assert z != 3
+ z >= 3
+ with pytest.raises(TypeError):
+ z <= 3
# order
assert m.ScopedEnum.Two < m.ScopedEnum.Three
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
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!'
#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} {}
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) {
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):
#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); }
// 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()"); }
--- /dev/null
+/*
+ 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();
+ });
+}
--- /dev/null
+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
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)
*/
#include "pybind11_tests.h"
+#include "constructor_stats.h"
#include <pybind11/stl.h>
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);
});
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) {});
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) == \
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
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)
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)
#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); }
.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))
.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); })
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_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_static = 3
- assert m.TestProperties.def_property_static == 3
+ m.TestProperties.def_property_writeonly_static = 5
+ assert m.TestProperties.def_property_static == 5
# Static property read and write via instance
instance = m.TestProperties()
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
// 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:
// 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; };
#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, "");
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()); });
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(
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
}
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')
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
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
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'
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
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_;
return list;
}
+struct A {};
+struct B {};
+
TEST_SUBMODULE(numpy_dtypes, m) {
try { py::module::import("numpy"); }
catch (...) { return; }
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_);
// 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>);
// 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)); });
}
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')]",
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}," +
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())
#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
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]"
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); }
};
}
+// 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
.def(float() - py::self)
.def(float() * py::self)
.def(float() / py::self)
+ .def(-py::self)
.def("__str__", &Vector2::toString)
.def(hash(py::self))
;
.def_readwrite("b", &NestC::b);
m.def("get_NestC", [](const NestC &c) { return c.value; });
}
+
+#ifndef _MSC_VER
+ #pragma GCC diagnostic pop
+#endif
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]"
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
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
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)
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) {
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"); });
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")); });
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)];
+ });
}
+from __future__ import division
import pytest
import sys
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"
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"
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"
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)
}
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 {
next(it)
+def test_sliceable():
+ sliceable = m.Sliceable(100)
+ assert sliceable[::] == (0, 100, 1)
+ assert sliceable[10::] == (10, 100, 1)
+ assert sliceable[:10:] == (0, 10, 1)
+ assert sliceable[::10] == (0, 100, 10)
+ assert sliceable[-10::] == (90, 100, 1)
+ assert sliceable[:-10:] == (0, 90, 1)
+ assert sliceable[::-10] == (99, -1, -10)
+ assert sliceable[50:60:1] == (50, 60, 1)
+ assert sliceable[50:60:-1] == (50, 60, -1)
+
+
def test_sequence():
cstats = ConstructorStats.get(m.Sequence)
m.iterator_to_list(iter(bad_next_call, None))
assert str(excinfo.value) == "py::iterator::advance() should propagate errors"
- l = [1, None, 0, None]
- assert m.count_none(l) == 2
- assert m.find_none(l) is True
+ lst = [1, None, 0, None]
+ assert m.count_none(lst) == 2
+ assert m.find_none(lst) is True
assert m.count_nonzeros({"a": 0, "b": 1, "c": 2}) == 2
r = range(5)
// ref<T> is a wrapper for 'Object' which uses intrusive reference counting
// It is always possible to construct a ref<T> from an Object* pointer without
-// possible incosistencies, hence the 'true' argument at the end.
+// possible inconsistencies, hence the 'true' argument at the end.
PYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>, true);
// Make pybind11 aware of the non-standard getter member function
namespace pybind11 { namespace detail {
};
PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr<T>);
+// Simple custom holder that works like shared_ptr and has operator& overload
+// To obtain address of an instance of this holder pybind should use std::addressof
+// Attempt to get address via operator& may leads to segmentation fault
+template <typename T>
+class shared_ptr_with_addressof_operator {
+ std::shared_ptr<T> impl;
+public:
+ shared_ptr_with_addressof_operator( ) = default;
+ shared_ptr_with_addressof_operator(T* p) : impl(p) { }
+ T* get() const { return impl.get(); }
+ T** operator&() { throw std::logic_error("Call of overloaded operator& is not expected"); }
+};
+PYBIND11_DECLARE_HOLDER_TYPE(T, shared_ptr_with_addressof_operator<T>);
+
+// Simple custom holder that works like unique_ptr and has operator& overload
+// To obtain address of an instance of this holder pybind should use std::addressof
+// Attempt to get address via operator& may leads to segmentation fault
+template <typename T>
+class unique_ptr_with_addressof_operator {
+ std::unique_ptr<T> impl;
+public:
+ unique_ptr_with_addressof_operator() = default;
+ unique_ptr_with_addressof_operator(T* p) : impl(p) { }
+ T* get() const { return impl.get(); }
+ T* release_ptr() { return impl.release(); }
+ T** operator&() { throw std::logic_error("Call of overloaded operator& is not expected"); }
+};
+PYBIND11_DECLARE_HOLDER_TYPE(T, unique_ptr_with_addressof_operator<T>);
+
TEST_SUBMODULE(smart_ptr, m) {
// Object managed by a std::shared_ptr<>
class MyObject2 {
public:
+ MyObject2(const MyObject2 &) = default;
MyObject2(int value) : value(value) { print_created(this, toString()); }
std::string toString() const { return "MyObject2[" + std::to_string(value) + "]"; }
virtual ~MyObject2() { print_destroyed(this); }
// Object managed by a std::shared_ptr<>, additionally derives from std::enable_shared_from_this<>
class MyObject3 : public std::enable_shared_from_this<MyObject3> {
public:
+ MyObject3(const MyObject3 &) = default;
MyObject3(int value) : value(value) { print_created(this, toString()); }
std::string toString() const { return "MyObject3[" + std::to_string(value) + "]"; }
virtual ~MyObject3() { print_destroyed(this); }
.def(py::init<int>())
.def_readwrite("value", &MyObject4::value);
+ // test_unique_deleter
+ // Object with std::unique_ptr<T, D> where D is not matching the base class
+ // Object with a protected destructor
+ class MyObject4a {
+ public:
+ MyObject4a(int i) {
+ value = i;
+ print_created(this);
+ };
+ int value;
+ protected:
+ virtual ~MyObject4a() { print_destroyed(this); }
+ };
+ py::class_<MyObject4a, std::unique_ptr<MyObject4a, py::nodelete>>(m, "MyObject4a")
+ .def(py::init<int>())
+ .def_readwrite("value", &MyObject4a::value);
+
+ // Object derived but with public destructor and no Deleter in default holder
+ class MyObject4b : public MyObject4a {
+ public:
+ MyObject4b(int i) : MyObject4a(i) { print_created(this); }
+ ~MyObject4b() { print_destroyed(this); }
+ };
+ py::class_<MyObject4b, MyObject4a>(m, "MyObject4b")
+ .def(py::init<int>());
+
// test_large_holder
class MyObject5 { // managed by huge_unique_ptr
public:
// Issue #865: shared_from_this doesn't work with virtual inheritance
struct SharedFromThisVBase : std::enable_shared_from_this<SharedFromThisVBase> {
+ SharedFromThisVBase() = default;
+ SharedFromThisVBase(const SharedFromThisVBase &) = default;
virtual ~SharedFromThisVBase() = default;
};
struct SharedFromThisVirt : virtual SharedFromThisVBase {};
py::class_<C, custom_unique_ptr<C>>(m, "TypeWithMoveOnlyHolder")
.def_static("make", []() { return custom_unique_ptr<C>(new C); });
+ // test_holder_with_addressof_operator
+ struct TypeForHolderWithAddressOf {
+ TypeForHolderWithAddressOf() { print_created(this); }
+ TypeForHolderWithAddressOf(const TypeForHolderWithAddressOf &) { print_copy_created(this); }
+ TypeForHolderWithAddressOf(TypeForHolderWithAddressOf &&) { print_move_created(this); }
+ ~TypeForHolderWithAddressOf() { print_destroyed(this); }
+ std::string toString() const {
+ return "TypeForHolderWithAddressOf[" + std::to_string(value) + "]";
+ }
+ int value = 42;
+ };
+ using HolderWithAddressOf = shared_ptr_with_addressof_operator<TypeForHolderWithAddressOf>;
+ py::class_<TypeForHolderWithAddressOf, HolderWithAddressOf>(m, "TypeForHolderWithAddressOf")
+ .def_static("make", []() { return HolderWithAddressOf(new TypeForHolderWithAddressOf); })
+ .def("get", [](const HolderWithAddressOf &self) { return self.get(); })
+ .def("print_object_1", [](const TypeForHolderWithAddressOf *obj) { py::print(obj->toString()); })
+ .def("print_object_2", [](HolderWithAddressOf obj) { py::print(obj.get()->toString()); })
+ .def("print_object_3", [](const HolderWithAddressOf &obj) { py::print(obj.get()->toString()); })
+ .def("print_object_4", [](const HolderWithAddressOf *obj) { py::print((*obj).get()->toString()); });
+
+ // test_move_only_holder_with_addressof_operator
+ struct TypeForMoveOnlyHolderWithAddressOf {
+ TypeForMoveOnlyHolderWithAddressOf(int value) : value{value} { print_created(this); }
+ ~TypeForMoveOnlyHolderWithAddressOf() { print_destroyed(this); }
+ std::string toString() const {
+ return "MoveOnlyHolderWithAddressOf[" + std::to_string(value) + "]";
+ }
+ int value;
+ };
+ using MoveOnlyHolderWithAddressOf = unique_ptr_with_addressof_operator<TypeForMoveOnlyHolderWithAddressOf>;
+ py::class_<TypeForMoveOnlyHolderWithAddressOf, MoveOnlyHolderWithAddressOf>(m, "TypeForMoveOnlyHolderWithAddressOf")
+ .def_static("make", []() { return MoveOnlyHolderWithAddressOf(new TypeForMoveOnlyHolderWithAddressOf(0)); })
+ .def_readwrite("value", &TypeForMoveOnlyHolderWithAddressOf::value)
+ .def("print_object", [](const TypeForMoveOnlyHolderWithAddressOf *obj) { py::print(obj->toString()); });
+
// test_smart_ptr_from_default
struct HeldByDefaultHolder { };
py::class_<HeldByDefaultHolder>(m, "HeldByDefaultHolder")
// test_shared_ptr_gc
// #187: issue involving std::shared_ptr<> return value policy & garbage collection
- struct ElementBase { virtual void foo() { } /* Force creation of virtual table */ };
+ struct ElementBase {
+ virtual ~ElementBase() { } /* Force creation of virtual table */
+ };
py::class_<ElementBase, std::shared_ptr<ElementBase>>(m, "ElementBase");
struct ElementA : ElementBase {
assert cstats.alive() == 1 # Leak, but that's intentional
+def test_unique_nodelete4a():
+ o = m.MyObject4a(23)
+ assert o.value == 23
+ cstats = ConstructorStats.get(m.MyObject4a)
+ assert cstats.alive() == 1
+ del o
+ assert cstats.alive() == 1 # Leak, but that's intentional
+
+
+def test_unique_deleter():
+ o = m.MyObject4b(23)
+ assert o.value == 23
+ cstats4a = ConstructorStats.get(m.MyObject4a)
+ assert cstats4a.alive() == 2 # Two because of previous test
+ cstats4b = ConstructorStats.get(m.MyObject4b)
+ assert cstats4b.alive() == 1
+ del o
+ assert cstats4a.alive() == 1 # Should now only be one leftover from previous test
+ assert cstats4b.alive() == 0 # Should be deleted
+
+
def test_large_holder():
o = m.MyObject5(5)
assert o.value == 5
assert stats.alive() == 0
+def test_holder_with_addressof_operator():
+ # this test must not throw exception from c++
+ a = m.TypeForHolderWithAddressOf.make()
+ a.print_object_1()
+ a.print_object_2()
+ a.print_object_3()
+ a.print_object_4()
+
+ stats = ConstructorStats.get(m.TypeForHolderWithAddressOf)
+ assert stats.alive() == 1
+
+ np = m.TypeForHolderWithAddressOf.make()
+ assert stats.alive() == 2
+ del a
+ assert stats.alive() == 1
+ del np
+ assert stats.alive() == 0
+
+ b = m.TypeForHolderWithAddressOf.make()
+ c = b
+ assert b.get() is c.get()
+ assert stats.alive() == 1
+
+ del b
+ assert stats.alive() == 1
+
+ del c
+ assert stats.alive() == 0
+
+
+def test_move_only_holder_with_addressof_operator():
+ a = m.TypeForMoveOnlyHolderWithAddressOf.make()
+ a.print_object()
+
+ stats = ConstructorStats.get(m.TypeForMoveOnlyHolderWithAddressOf)
+ assert stats.alive() == 1
+
+ a.value = 42
+ assert a.value == 42
+
+ del a
+ assert stats.alive() == 0
+
+
def test_smart_ptr_from_default():
instance = m.HeldByDefaultHolder()
with pytest.raises(RuntimeError) as excinfo:
m.HeldByDefaultHolder.load_shared_ptr(instance)
- assert "Unable to load a custom holder type from a default-holder instance" in str(excinfo)
+ assert "Unable to load a custom holder type from a " \
+ "default-holder instance" in str(excinfo.value)
def test_shared_ptr_gc():
*/
#include "pybind11_tests.h"
+#include "constructor_stats.h"
#include <pybind11/stl.h>
+#include <vector>
+#include <string>
+
// Test with `std::variant` in C++17 mode, or with `boost::variant` in C++11/14
#if PYBIND11_HAS_VARIANT
using std::variant;
}} // namespace pybind11::detail
#endif
+PYBIND11_MAKE_OPAQUE(std::vector<std::string, std::allocator<std::string>>);
+
/// Issue #528: templated constructor
struct TplCtorClass {
template <typename T> TplCtorClass(const T &) { }
static std::vector<RValueCaster> lvv{2};
m.def("cast_ptr_vector", []() { return &lvv; });
+ // test_deque
+ m.def("cast_deque", []() { return std::deque<int>{1}; });
+ m.def("load_deque", [](const std::deque<int> &v) { return v.at(0) == 1 && v.at(1) == 2; });
+
// test_array
m.def("cast_array", []() { return std::array<int, 2> {{1 , 2}}; });
m.def("load_array", [](const std::array<int, 2> &a) { return a[0] == 1 && a[1] == 2; });
// test_stl_pass_by_pointer
m.def("stl_pass_by_pointer", [](std::vector<int>* v) { return *v; }, "v"_a=nullptr);
+
+ // #1258: pybind11/stl.h converts string to vector<string>
+ m.def("func_with_string_or_vector_string_arg_overload", [](std::vector<std::string>) { return 1; });
+ m.def("func_with_string_or_vector_string_arg_overload", [](std::list<std::string>) { return 2; });
+ m.def("func_with_string_or_vector_string_arg_overload", [](std::string) { return 3; });
+
+ class Placeholder {
+ public:
+ Placeholder() { print_created(this); }
+ Placeholder(const Placeholder &) = delete;
+ ~Placeholder() { print_destroyed(this); }
+ };
+ py::class_<Placeholder>(m, "Placeholder");
+
+ /// test_stl_vector_ownership
+ m.def("test_stl_ownership",
+ []() {
+ std::vector<Placeholder *> result;
+ result.push_back(new Placeholder());
+ return result;
+ },
+ py::return_value_policy::take_ownership);
+
+ m.def("array_cast_sequence", [](std::array<int, 3> x) { return x; });
+
+ /// test_issue_1561
+ struct Issue1561Inner { std::string data; };
+ struct Issue1561Outer { std::vector<Issue1561Inner> list; };
+
+ py::class_<Issue1561Inner>(m, "Issue1561Inner")
+ .def(py::init<std::string>())
+ .def_readwrite("data", &Issue1561Inner::data);
+
+ py::class_<Issue1561Outer>(m, "Issue1561Outer")
+ .def(py::init<>())
+ .def_readwrite("list", &Issue1561Outer::list);
}
from pybind11_tests import stl as m
from pybind11_tests import UserType
+from pybind11_tests import ConstructorStats
def test_vector(doc):
"""std::vector <-> list"""
- l = m.cast_vector()
- assert l == [1]
- l.append(2)
- assert m.load_vector(l)
- assert m.load_vector(tuple(l))
+ lst = m.cast_vector()
+ assert lst == [1]
+ lst.append(2)
+ assert m.load_vector(lst)
+ assert m.load_vector(tuple(lst))
assert m.cast_bool_vector() == [True, False]
assert m.load_bool_vector([True, False])
assert m.cast_ptr_vector() == ["lvalue", "lvalue"]
+def test_deque(doc):
+ """std::deque <-> list"""
+ lst = m.cast_deque()
+ assert lst == [1]
+ lst.append(2)
+ assert m.load_deque(lst)
+ assert m.load_deque(tuple(lst))
+
+
def test_array(doc):
"""std::array <-> list"""
- l = m.cast_array()
- assert l == [1, 2]
- assert m.load_array(l)
+ lst = m.cast_array()
+ assert lst == [1, 2]
+ assert m.load_array(lst)
assert doc(m.cast_array) == "cast_array() -> List[int[2]]"
assert doc(m.load_array) == "load_array(arg0: List[int[2]]) -> bool"
def test_valarray(doc):
"""std::valarray <-> list"""
- l = m.cast_valarray()
- assert l == [1, 4, 9]
- assert m.load_valarray(l)
+ lst = m.cast_valarray()
+ assert lst == [1, 4, 9]
+ assert m.load_valarray(lst)
assert doc(m.cast_valarray) == "cast_valarray() -> List[int]"
assert doc(m.load_valarray) == "load_valarray(arg0: List[int]) -> bool"
"""std::map <-> dict"""
d = m.cast_map()
assert d == {"key": "value"}
+ assert "key" in d
d["key2"] = "value2"
+ assert "key2" in d
assert m.load_map(d)
assert doc(m.cast_map) == "cast_map() -> Dict[str, str]"
m.stl_pass_by_pointer() # default value is `nullptr`
assert msg(excinfo.value) == """
stl_pass_by_pointer(): incompatible function arguments. The following argument types are supported:
- 1. (v: List[int]=None) -> List[int]
+ 1. (v: List[int] = None) -> List[int]
Invoked with:
""" # noqa: E501 line too long
m.stl_pass_by_pointer(None)
assert msg(excinfo.value) == """
stl_pass_by_pointer(): incompatible function arguments. The following argument types are supported:
- 1. (v: List[int]=None) -> List[int]
+ 1. (v: List[int] = None) -> List[int]
Invoked with: None
""" # noqa: E501 line too long
with pytest.raises(TypeError) as excinfo:
cm.missing_header_return()
assert expected_message in str(excinfo.value)
+
+
+def test_function_with_string_and_vector_string_arg():
+ """Check if a string is NOT implicitly converted to a list, which was the
+ behavior before fix of issue #1258"""
+ assert m.func_with_string_or_vector_string_arg_overload(('A', 'B', )) == 2
+ assert m.func_with_string_or_vector_string_arg_overload(['A', 'B']) == 2
+ assert m.func_with_string_or_vector_string_arg_overload('A') == 3
+
+
+def test_stl_ownership():
+ cstats = ConstructorStats.get(m.Placeholder)
+ assert cstats.alive() == 0
+ r = m.test_stl_ownership()
+ assert len(r) == 1
+ del r
+ assert cstats.alive() == 0
+
+
+def test_array_cast_sequence():
+ assert m.array_cast_sequence((1, 2, 3)) == [1, 2, 3]
+
+
+def test_issue_1561():
+ """ check fix for issue #1561 """
+ bar = m.Issue1561Outer()
+ bar.list = [m.Issue1561Inner('bar')]
+ bar.list
+ assert bar.list[0].data == 'bar'
assert len(v_int) == 2
assert bool(v_int) is True
+ # test construction from a generator
+ v_int1 = m.VectorInt(x for x in range(5))
+ assert v_int1 == m.VectorInt([0, 1, 2, 3, 4])
+
v_int2 = m.VectorInt([0, 0])
assert v_int == v_int2
v_int2[1] = 1
del v_int2[0]
assert v_int2 == m.VectorInt([0, 99, 2, 3])
+ v_int2.extend(m.VectorInt([4, 5]))
+ assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5])
+
+ v_int2.extend([6, 7])
+ assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5, 6, 7])
+
+ # test error handling, and that the vector is unchanged
+ with pytest.raises(RuntimeError):
+ v_int2.extend([8, 'a'])
+
+ assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5, 6, 7])
+
+ # test extending from a generator
+ v_int2.extend(x for x in range(5))
+ assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4])
+
+ # test negative indexing
+ assert v_int2[-1] == 4
+
+ # insert with negative index
+ v_int2.insert(-1, 88)
+ assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 88, 4])
+
+ # delete negative index
+ del v_int2[-1]
+ assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 88])
# related to the PyPy's buffer protocol.
@pytest.unsupported_on_pypy
vsum += v.value
assert vsum == 150
+
+
+def test_map_delitem():
+ mm = m.MapStringDouble()
+ mm['a'] = 1
+ mm['b'] = 2.5
+
+ assert list(mm) == ['a', 'b']
+ assert list(mm.items()) == [('a', 1), ('b', 2.5)]
+ del mm['a']
+ assert list(mm) == ['b']
+ assert list(mm.items()) == [('b', 2.5)]
+
+ um = m.UnorderedMapStringDouble()
+ um['ua'] = 1.1
+ um['ub'] = 2.6
+
+ assert sorted(list(um)) == ['ua', 'ub']
+ assert sorted(list(um.items())) == [('ua', 1.1), ('ub', 2.6)]
+ del um['ua']
+ assert sorted(list(um)) == ['ub']
+ assert sorted(list(um.items())) == [('ub', 2.6)]
--- /dev/null
+/*
+ tests/test_tagbased_polymorphic.cpp -- test of polymorphic_type_hook
+
+ Copyright (c) 2018 Hudson River Trading LLC <opensource@hudson-trading.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/stl.h>
+
+struct Animal
+{
+ enum class Kind {
+ Unknown = 0,
+ Dog = 100, Labrador, Chihuahua, LastDog = 199,
+ Cat = 200, Panther, LastCat = 299
+ };
+ static const std::type_info* type_of_kind(Kind kind);
+ static std::string name_of_kind(Kind kind);
+
+ const Kind kind;
+ const std::string name;
+
+ protected:
+ Animal(const std::string& _name, Kind _kind)
+ : kind(_kind), name(_name)
+ {}
+};
+
+struct Dog : Animal
+{
+ Dog(const std::string& _name, Kind _kind = Kind::Dog) : Animal(_name, _kind) {}
+ std::string bark() const { return name_of_kind(kind) + " " + name + " goes " + sound; }
+ std::string sound = "WOOF!";
+};
+
+struct Labrador : Dog
+{
+ Labrador(const std::string& _name, int _excitement = 9001)
+ : Dog(_name, Kind::Labrador), excitement(_excitement) {}
+ int excitement;
+};
+
+struct Chihuahua : Dog
+{
+ Chihuahua(const std::string& _name) : Dog(_name, Kind::Chihuahua) { sound = "iyiyiyiyiyi"; }
+ std::string bark() const { return Dog::bark() + " and runs in circles"; }
+};
+
+struct Cat : Animal
+{
+ Cat(const std::string& _name, Kind _kind = Kind::Cat) : Animal(_name, _kind) {}
+ std::string purr() const { return "mrowr"; }
+};
+
+struct Panther : Cat
+{
+ Panther(const std::string& _name) : Cat(_name, Kind::Panther) {}
+ std::string purr() const { return "mrrrRRRRRR"; }
+};
+
+std::vector<std::unique_ptr<Animal>> create_zoo()
+{
+ std::vector<std::unique_ptr<Animal>> ret;
+ ret.emplace_back(new Labrador("Fido", 15000));
+
+ // simulate some new type of Dog that the Python bindings
+ // haven't been updated for; it should still be considered
+ // a Dog, not just an Animal.
+ ret.emplace_back(new Dog("Ginger", Dog::Kind(150)));
+
+ ret.emplace_back(new Chihuahua("Hertzl"));
+ ret.emplace_back(new Cat("Tiger", Cat::Kind::Cat));
+ ret.emplace_back(new Panther("Leo"));
+ return ret;
+}
+
+const std::type_info* Animal::type_of_kind(Kind kind)
+{
+ switch (kind) {
+ case Kind::Unknown: break;
+
+ case Kind::Dog: break;
+ case Kind::Labrador: return &typeid(Labrador);
+ case Kind::Chihuahua: return &typeid(Chihuahua);
+ case Kind::LastDog: break;
+
+ case Kind::Cat: break;
+ case Kind::Panther: return &typeid(Panther);
+ case Kind::LastCat: break;
+ }
+
+ if (kind >= Kind::Dog && kind <= Kind::LastDog) return &typeid(Dog);
+ if (kind >= Kind::Cat && kind <= Kind::LastCat) return &typeid(Cat);
+ return nullptr;
+}
+
+std::string Animal::name_of_kind(Kind kind)
+{
+ std::string raw_name = type_of_kind(kind)->name();
+ py::detail::clean_type_id(raw_name);
+ return raw_name;
+}
+
+namespace pybind11 {
+ template <typename itype>
+ struct polymorphic_type_hook<itype, detail::enable_if_t<std::is_base_of<Animal, itype>::value>>
+ {
+ static const void *get(const itype *src, const std::type_info*& type)
+ { type = src ? Animal::type_of_kind(src->kind) : nullptr; return src; }
+ };
+}
+
+TEST_SUBMODULE(tagbased_polymorphic, m) {
+ py::class_<Animal>(m, "Animal")
+ .def_readonly("name", &Animal::name);
+ py::class_<Dog, Animal>(m, "Dog")
+ .def(py::init<std::string>())
+ .def_readwrite("sound", &Dog::sound)
+ .def("bark", &Dog::bark);
+ py::class_<Labrador, Dog>(m, "Labrador")
+ .def(py::init<std::string, int>(), "name"_a, "excitement"_a = 9001)
+ .def_readwrite("excitement", &Labrador::excitement);
+ py::class_<Chihuahua, Dog>(m, "Chihuahua")
+ .def(py::init<std::string>())
+ .def("bark", &Chihuahua::bark);
+ py::class_<Cat, Animal>(m, "Cat")
+ .def(py::init<std::string>())
+ .def("purr", &Cat::purr);
+ py::class_<Panther, Cat>(m, "Panther")
+ .def(py::init<std::string>())
+ .def("purr", &Panther::purr);
+ m.def("create_zoo", &create_zoo);
+};
--- /dev/null
+from pybind11_tests import tagbased_polymorphic as m
+
+
+def test_downcast():
+ zoo = m.create_zoo()
+ assert [type(animal) for animal in zoo] == [
+ m.Labrador, m.Dog, m.Chihuahua, m.Cat, m.Panther
+ ]
+ assert [animal.name for animal in zoo] == [
+ "Fido", "Ginger", "Hertzl", "Tiger", "Leo"
+ ]
+ zoo[1].sound = "woooooo"
+ assert [dog.bark() for dog in zoo[:3]] == [
+ "Labrador Fido goes WOOF!",
+ "Dog Ginger goes woooooo",
+ "Chihuahua Hertzl goes iyiyiyiyiyi and runs in circles"
+ ]
+ assert [cat.purr() for cat in zoo[3:]] == ["mrowr", "mrrrRRRRRR"]
+ zoo[0].excitement -= 1000
+ assert zoo[0].excitement == 14000
--- /dev/null
+/*
+ tests/test_class.cpp -- test py::class_ definitions and basic functionality
+
+ Copyright (c) 2019 Roland Dreier <roland.dreier@gmail.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"
+
+TEST_SUBMODULE(union_, m) {
+ union TestUnion {
+ int value_int;
+ unsigned value_uint;
+ };
+
+ py::class_<TestUnion>(m, "TestUnion")
+ .def(py::init<>())
+ .def_readonly("as_int", &TestUnion::value_int)
+ .def_readwrite("as_uint", &TestUnion::value_uint);
+}
--- /dev/null
+from pybind11_tests import union_ as m
+
+
+def test_union():
+ instance = m.TestUnion()
+
+ instance.as_uint = 10
+ assert instance.as_int == 10
#include "pybind11_tests.h"
#include "constructor_stats.h"
#include <pybind11/functional.h>
+#include <thread>
/* This is an example class that we'll want to be able to extend from Python */
class ExampleVirt {
ExampleVirt(int state) : state(state) { print_created(this, state); }
ExampleVirt(const ExampleVirt &e) : state(e.state) { print_copy_created(this); }
ExampleVirt(ExampleVirt &&e) : state(e.state) { print_move_created(this); e.state = 0; }
- ~ExampleVirt() { print_destroyed(this); }
+ virtual ~ExampleVirt() { print_destroyed(this); }
virtual int run(int value) {
py::print("Original implementation of "
class NCVirt {
public:
+ virtual ~NCVirt() { }
virtual NonCopyable get_noncopyable(int a, int b) { return NonCopyable(a, b); }
virtual Movable get_movable(int a, int b) = 0;
}
};
+static void test_gil() {
+ {
+ py::gil_scoped_acquire lock;
+ py::print("1st lock acquired");
+
+ }
+
+ {
+ py::gil_scoped_acquire lock;
+ py::print("2nd lock acquired");
+ }
+
+}
+
+static void test_gil_from_thread() {
+ py::gil_scoped_release release;
+
+ std::thread t(test_gil);
+ t.join();
+}
+
+
// Forward declaration (so that we can put the main tests here; the inherited virtual approaches are
// rather long).
void initialize_inherited_virtuals(py::module &m);
void f() override {
py::print("PyA.f()");
- PYBIND11_OVERLOAD(void, A, f);
+ // This convolution just gives a `void`, but tests that PYBIND11_TYPE() works to protect
+ // a type containing a ,
+ PYBIND11_OVERLOAD(PYBIND11_TYPE(typename std::enable_if<true, void>::type), A, f);
}
};
m.def("dispatch_issue_go", [](const Base * b) { return b->dispatch(); });
// test_override_ref
- // #392/397: overridding reference-returning functions
+ // #392/397: overriding reference-returning functions
class OverrideTest {
public:
struct A { std::string value = "hi"; };
};
*/
-
void initialize_inherited_virtuals(py::module &m) {
// test_inherited_virtuals
py::class_<D_Tpl, C_Tpl, PyB_Tpl<D_Tpl>>(m, "D_Tpl")
.def(py::init<>());
+
+ // Fix issue #1454 (crash when acquiring/releasing GIL on another thread in Python 2.7)
+ m.def("test_gil", &test_gil);
+ m.def("test_gil_from_thread", &test_gil_from_thread);
};
def test_override_ref():
- """#392/397: overridding reference-returning functions"""
+ """#392/397: overriding reference-returning functions"""
o = m.OverrideTest("asdf")
# Not allowed (see associated .cpp comment)
assert obj.unlucky_number() == -7
assert obj.lucky_number() == -1.375
assert obj.say_everything() == "BT -7"
+
+
+def test_issue_1454():
+ # Fix issue #1454 (crash when acquiring/releasing GIL on another thread in Python 2.7)
+ m.test_gil()
+ m.test_gil_from_thread()
# - Find python libraries
-# This module finds the libraries corresponding to the Python interpeter
+# This module finds the libraries corresponding to the Python interpreter
# FindPythonInterp provides.
# This code sets the following variables:
#
if(NOT PYTHONINTERP_FOUND)
set(PYTHONLIBS_FOUND FALSE)
+ set(PythonLibsNew_FOUND FALSE)
return()
endif()
"Python config failure:\n${_PYTHON_ERROR_VALUE}")
endif()
set(PYTHONLIBS_FOUND FALSE)
+ set(PythonLibsNew_FOUND FALSE)
return()
endif()
# Convert the process output into a list
+if(WIN32)
+ string(REGEX REPLACE "\\\\" "/" _PYTHON_VALUES ${_PYTHON_VALUES})
+endif()
string(REGEX REPLACE ";" "\\\\;" _PYTHON_VALUES ${_PYTHON_VALUES})
string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES})
list(GET _PYTHON_VALUES 0 _PYTHON_VERSION_LIST)
"chosen compiler is ${_CMAKE_BITS}-bit")
endif()
set(PYTHONLIBS_FOUND FALSE)
+ set(PythonLibsNew_FOUND FALSE)
return()
endif()
string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR ${PYTHON_INCLUDE_DIR})
string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES ${PYTHON_SITE_PACKAGES})
-if(CMAKE_HOST_WIN32)
+if(CMAKE_HOST_WIN32 AND NOT (MSYS OR MINGW))
set(PYTHON_LIBRARY
"${PYTHON_PREFIX}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib")
"${PYTHON_EXECUTABLE}${PYTHON_VERSION}")
set(PYTHONLIBS_FOUND TRUE)
+set(PythonLibsNew_FOUND TRUE)
# 4. missing space between keyword and parenthesis, e.g.: for(, if(, while(
# 5. Missing space between right parenthesis and brace, e.g. 'for (...){'
# 6. opening brace on its own line. It should always be on the same line as the
-# if/while/for/do statment.
+# if/while/for/do statement.
#
# Invoke as: tools/check-style.sh
#
from clang import cindex
from clang.cindex import CursorKind
from collections import OrderedDict
+from glob import glob
from threading import Thread, Semaphore
from multiprocessing import cpu_count
CursorKind.FIELD_DECL
]
+PREFIX_BLACKLIST = [
+ CursorKind.TRANSLATION_UNIT
+]
+
CPP_OPERATORS = {
'<=': 'le', '>=': 'ge', '==': 'eq', '!=': 'ne', '[]': 'array',
'+=': 'iadd', '-=': 'isub', '*=': 'imul', '/=': 'idiv', '%=':
job_count = cpu_count()
job_semaphore = Semaphore(job_count)
-output = []
+
+class NoFilenamesError(ValueError):
+ pass
+
def d(s):
- return s.decode('utf8')
+ return s if isinstance(s, str) else s.decode('utf8')
def sanitize_name(name):
return result.rstrip().lstrip('\n')
-def extract(filename, node, prefix):
+def extract(filename, node, prefix, output):
if not (node.location.file is None or
os.path.samefile(d(node.location.file.name), filename)):
return 0
if node.kind in RECURSE_LIST:
sub_prefix = prefix
- if node.kind != CursorKind.TRANSLATION_UNIT:
+ if node.kind not in PREFIX_BLACKLIST:
if len(sub_prefix) > 0:
sub_prefix += '_'
sub_prefix += d(node.spelling)
for i in node.get_children():
- extract(filename, i, sub_prefix)
+ extract(filename, i, sub_prefix, output)
if node.kind in PRINT_LIST:
comment = d(node.raw_comment) if node.raw_comment is not None else ''
comment = process_comment(comment)
sub_prefix += '_'
if len(node.spelling) > 0:
name = sanitize_name(sub_prefix + d(node.spelling))
- global output
output.append((name, filename, comment))
class ExtractionThread(Thread):
- def __init__(self, filename, parameters):
+ def __init__(self, filename, parameters, output):
Thread.__init__(self)
self.filename = filename
self.parameters = parameters
+ self.output = output
job_semaphore.acquire()
def run(self):
index = cindex.Index(
cindex.conf.lib.clang_createIndex(False, True))
tu = index.parse(self.filename, self.parameters)
- extract(self.filename, tu.cursor, '')
+ extract(self.filename, tu.cursor, '', self.output)
finally:
job_semaphore.release()
-if __name__ == '__main__':
- parameters = ['-x', 'c++', '-std=c++11']
+
+def read_args(args):
+ parameters = []
filenames = []
+ if "-x" not in args:
+ parameters.extend(['-x', 'c++'])
+ if not any(it.startswith("-std=") for it in args):
+ parameters.append('-std=c++11')
if platform.system() == 'Darwin':
dev_path = '/Applications/Xcode.app/Contents/Developer/'
sysroot_dir = os.path.join(sdk_dir, next(os.walk(sdk_dir))[1][0])
parameters.append('-isysroot')
parameters.append(sysroot_dir)
-
- for item in sys.argv[1:]:
+ elif platform.system() == 'Linux':
+ # clang doesn't find its own base includes by default on Linux,
+ # but different distros install them in different paths.
+ # Try to autodetect, preferring the highest numbered version.
+ def clang_folder_version(d):
+ return [int(ver) for ver in re.findall(r'(?<!lib)(?<!\d)\d+', d)]
+ clang_include_dir = max((
+ path
+ for libdir in ['lib64', 'lib', 'lib32']
+ for path in glob('/usr/%s/clang/*/include' % libdir)
+ if os.path.isdir(path)
+ ), default=None, key=clang_folder_version)
+ if clang_include_dir:
+ parameters.extend(['-isystem', clang_include_dir])
+
+ for item in args:
if item.startswith('-'):
parameters.append(item)
else:
filenames.append(item)
if len(filenames) == 0:
- print('Syntax: %s [.. a list of header files ..]' % sys.argv[0])
- exit(-1)
+ raise NoFilenamesError("args parameter did not contain any filenames")
+
+ return parameters, filenames
+
+
+def extract_all(args):
+ parameters, filenames = read_args(args)
+ output = []
+ for filename in filenames:
+ thr = ExtractionThread(filename, parameters, output)
+ thr.start()
+
+ print('Waiting for jobs to finish ..', file=sys.stderr)
+ for i in range(job_count):
+ job_semaphore.acquire()
+ return output
+
+
+def write_header(comments, out_file=sys.stdout):
print('''/*
This file contains docstrings for the Python bindings.
Do not edit! These were automatically extracted by mkdoc.py
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
#endif
-''')
+''', file=out_file)
- output.clear()
- for filename in filenames:
- thr = ExtractionThread(filename, parameters)
- thr.start()
-
- print('Waiting for jobs to finish ..', file=sys.stderr)
- for i in range(job_count):
- job_semaphore.acquire()
name_ctr = 1
name_prev = None
- for name, _, comment in list(sorted(output, key=lambda x: (x[0], x[1]))):
+ for name, _, comment in list(sorted(comments, key=lambda x: (x[0], x[1]))):
if name == name_prev:
name_ctr += 1
name = name + "_%i" % name_ctr
name_prev = name
name_ctr = 1
print('\nstatic const char *%s =%sR"doc(%s)doc";' %
- (name, '\n' if '\n' in comment else ' ', comment))
+ (name, '\n' if '\n' in comment else ' ', comment), file=out_file)
print('''
#if defined(__GNUG__)
#pragma GCC diagnostic pop
#endif
-''')
+''', file=out_file)
+
+
+def mkdoc(args):
+ args = list(args)
+ out_path = None
+ for idx, arg in enumerate(args):
+ if arg.startswith("-o"):
+ args.remove(arg)
+ try:
+ out_path = arg[2:] or args.pop(idx)
+ except IndexError:
+ print("-o flag requires an argument")
+ exit(-1)
+ break
+
+ comments = extract_all(args)
+
+ if out_path:
+ try:
+ with open(out_path, 'w') as out_file:
+ write_header(comments, out_file)
+ except:
+ # In the event of an error, don't leave a partially-written
+ # output file.
+ try:
+ os.unlink(out_path)
+ except:
+ pass
+ raise
+ else:
+ write_header(comments)
+
+
+if __name__ == '__main__':
+ try:
+ mkdoc(sys.argv[1:])
+ except NoFilenamesError:
+ print('Syntax: %s [.. a list of header files ..]' % sys.argv[0])
+ exit(-1)
set_property(TARGET ${PN}::module APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${PYTHON_LIBRARIES})
endif()
- set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "${PYBIND11_CPP_STANDARD}")
+ if(CMAKE_VERSION VERSION_LESS 3.3)
+ set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "${PYBIND11_CPP_STANDARD}")
+ else()
+ set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_COMPILE_OPTIONS $<$<COMPILE_LANGUAGE:CXX>:${PYBIND11_CPP_STANDARD}>)
+ endif()
get_property(_iid TARGET ${PN}::pybind11 PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
get_property(_ill TARGET ${PN}::module PROPERTY INTERFACE_LINK_LIBRARIES)
# Build a Python extension module:
# pybind11_add_module(<name> [MODULE | SHARED] [EXCLUDE_FROM_ALL]
-# [NO_EXTRAS] [THIN_LTO] source1 [source2 ...])
+# [NO_EXTRAS] [SYSTEM] [THIN_LTO] source1 [source2 ...])
#
function(pybind11_add_module target_name)
- set(options MODULE SHARED EXCLUDE_FROM_ALL NO_EXTRAS THIN_LTO)
+ set(options MODULE SHARED EXCLUDE_FROM_ALL NO_EXTRAS SYSTEM THIN_LTO)
cmake_parse_arguments(ARG "${options}" "" "" ${ARGN})
if(ARG_MODULE AND ARG_SHARED)
add_library(${target_name} ${lib_type} ${exclude_from_all} ${ARG_UNPARSED_ARGUMENTS})
- target_include_directories(${target_name}
+ if(ARG_SYSTEM)
+ set(inc_isystem SYSTEM)
+ endif()
+
+ target_include_directories(${target_name} ${inc_isystem}
PRIVATE ${PYBIND11_INCLUDE_DIR} # from project CMakeLists.txt
PRIVATE ${pybind11_INCLUDE_DIR} # from pybind11Config
PRIVATE ${PYTHON_INCLUDE_DIRS})
+ # Python debug libraries expose slightly different objects
+ # https://docs.python.org/3.6/c-api/intro.html#debugging-builds
+ # https://stackoverflow.com/questions/39161202/how-to-work-around-missing-pymodule-create2-in-amd64-win-python35-d-lib
+ if(PYTHON_IS_DEBUG)
+ target_compile_definitions(${target_name} PRIVATE Py_DEBUG)
+ endif()
+
# The prefix and extension are provided by FindPythonLibsNew.cmake
set_target_properties(${target_name} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}")
set_target_properties(${target_name} PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}")
# namespace; also turning it on for a pybind module compilation here avoids
# potential warnings or issues from having mixed hidden/non-hidden types.
set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden")
+ set_target_properties(${target_name} PROPERTIES CUDA_VISIBILITY_PRESET "hidden")
if(WIN32 OR CYGWIN)
# Link against the Python shared library on Windows
endif()
# Make sure C++11/14 are enabled
- target_compile_options(${target_name} PUBLIC ${PYBIND11_CPP_STANDARD})
+ if(CMAKE_VERSION VERSION_LESS 3.3)
+ target_compile_options(${target_name} PUBLIC ${PYBIND11_CPP_STANDARD})
+ else()
+ target_compile_options(${target_name} PUBLIC $<$<COMPILE_LANGUAGE:CXX>:${PYBIND11_CPP_STANDARD}>)
+ endif()
if(ARG_NO_EXTRAS)
return()
_pybind11_add_lto_flags(${target_name} ${ARG_THIN_LTO})
- if (NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug)
+ if (NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo)
# Strip unnecessary sections of the binary on Linux/Mac OS
if(CMAKE_STRIP)
if(APPLE)
if(MSVC)
# /MP enables multithreaded builds (relevant when there are many files), /bigobj is
# needed for bigger binding projects due to the limit to 64k addressable sections
- target_compile_options(${target_name} PRIVATE /MP /bigobj)
+ target_compile_options(${target_name} PRIVATE /bigobj)
+ if(CMAKE_VERSION VERSION_LESS 3.11)
+ target_compile_options(${target_name} PRIVATE $<$<NOT:$<CONFIG:Debug>>:/MP>)
+ else()
+ # Only set these options for C++ files. This is important so that, for
+ # instance, projects that include other types of source files like CUDA
+ # .cu files don't get these options propagated to nvcc since that would
+ # cause the build to fail.
+ target_compile_options(${target_name} PRIVATE $<$<NOT:$<CONFIG:Debug>>:$<$<COMPILE_LANGUAGE:CXX>:/MP>>)
+ endif()
endif()
endfunction()