Update to pybind11 v2.7.1

This commit is contained in:
Seth Hillbrand 2021-08-30 15:58:05 -07:00
parent aeb61d5984
commit 84c7cc6c21
104 changed files with 2597 additions and 1194 deletions

View File

@ -125,7 +125,8 @@ set(PYBIND11_HEADERS
include/pybind11/pybind11.h include/pybind11/pybind11.h
include/pybind11/pytypes.h include/pybind11/pytypes.h
include/pybind11/stl.h include/pybind11/stl.h
include/pybind11/stl_bind.h) include/pybind11/stl_bind.h
include/pybind11/stl/filesystem.h)
# Compare with grep and warn if mismatched # Compare with grep and warn if mismatched
if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12) if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12)
@ -202,6 +203,12 @@ if(PYBIND11_INSTALL)
"${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME}" "${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME}"
CACHE STRING "install path for pybind11Config.cmake") CACHE STRING "install path for pybind11Config.cmake")
if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
set(pybind11_INCLUDEDIR "${CMAKE_INSTALL_FULL_INCLUDEDIR}")
else()
set(pybind11_INCLUDEDIR "\$\{PACKAGE_PREFIX_DIR\}/${CMAKE_INSTALL_INCLUDEDIR}")
endif()
configure_package_config_file( configure_package_config_file(
tools/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" tools/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR})

View File

@ -151,6 +151,8 @@ as arguments and return values, refer to the section on binding :ref:`classes`.
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-------------------------------+
| ``std::variant<...>`` | Type-safe union (C++17) | :file:`pybind11/stl.h` | | ``std::variant<...>`` | Type-safe union (C++17) | :file:`pybind11/stl.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-------------------------------+
| ``std::filesystem::path<T>`` | STL path (C++17) [#]_ | :file:`pybind11/stl.h` |
+------------------------------------+---------------------------+-------------------------------+
| ``std::function<...>`` | STL polymorphic function | :file:`pybind11/functional.h` | | ``std::function<...>`` | STL polymorphic function | :file:`pybind11/functional.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-------------------------------+
| ``std::chrono::duration<...>`` | STL time duration | :file:`pybind11/chrono.h` | | ``std::chrono::duration<...>`` | STL time duration | :file:`pybind11/chrono.h` |
@ -163,3 +165,7 @@ as arguments and return values, refer to the section on binding :ref:`classes`.
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-------------------------------+
| ``Eigen::SparseMatrix<...>`` | Eigen: sparse matrix | :file:`pybind11/eigen.h` | | ``Eigen::SparseMatrix<...>`` | Eigen: sparse matrix | :file:`pybind11/eigen.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-------------------------------+
.. [#] ``std::filesystem::path`` is converted to ``pathlib.Path`` and
``os.PathLike`` is converted to ``std::filesystem::path``, but this requires
Python 3.6 (for ``__fspath__`` support).

View File

@ -259,7 +259,7 @@ override the ``name()`` method):
.. note:: .. note::
Note the trailing commas in the ``PYBIND11_OVERIDE`` calls to ``name()`` Note the trailing commas in the ``PYBIND11_OVERRIDE`` calls to ``name()``
and ``bark()``. These are needed to portably implement a trampoline for a and ``bark()``. These are needed to portably implement a trampoline for a
function that does not take any arguments. For functions that take function that does not take any arguments. For functions that take
a nonzero number of arguments, the trailing comma must be omitted. a nonzero number of arguments, the trailing comma must be omitted.
@ -804,7 +804,7 @@ to bind these two functions:
} }
)); ));
The ``__setstate__`` part of the ``py::picke()`` definition follows the same The ``__setstate__`` part of the ``py::pickle()`` definition follows the same
rules as the single-argument version of ``py::init()``. The return type can be rules as the single-argument version of ``py::init()``. The return type can be
a value, pointer or holder type. See :ref:`custom_constructors` for details. a value, pointer or holder type. See :ref:`custom_constructors` for details.

View File

@ -164,6 +164,10 @@ section.
may be explicitly (re-)thrown to delegate it to the other, may be explicitly (re-)thrown to delegate it to the other,
previously-declared existing exception translators. previously-declared existing exception translators.
Note that ``libc++`` and ``libstdc++`` `behave differently <https://stackoverflow.com/questions/19496643/using-clang-fvisibility-hidden-and-typeinfo-and-type-erasure/28827430>`_
with ``-fvisibility=hidden``. Therefore exceptions that are used across ABI boundaries need to be explicitly exported, as exercised in ``tests/test_exceptions.h``.
See also: "Problems with C++ exceptions" under `GCC Wiki <https://gcc.gnu.org/wiki/Visibility>`_.
.. _handling_python_exceptions_cpp: .. _handling_python_exceptions_cpp:
Handling exceptions from Python in C++ Handling exceptions from Python in C++

View File

@ -90,17 +90,18 @@ The following table provides an overview of available policies:
| | return value is referenced by Python. This is the default policy for | | | return value is referenced by Python. This is the default policy for |
| | property getters created via ``def_property``, ``def_readwrite``, etc. | | | property getters created via ``def_property``, ``def_readwrite``, etc. |
+--------------------------------------------------+----------------------------------------------------------------------------+ +--------------------------------------------------+----------------------------------------------------------------------------+
| :enum:`return_value_policy::automatic` | **Default policy.** This policy falls back to the policy | | :enum:`return_value_policy::automatic` | This policy falls back to the policy |
| | :enum:`return_value_policy::take_ownership` when the return value is a | | | :enum:`return_value_policy::take_ownership` when the return value is a |
| | pointer. Otherwise, it uses :enum:`return_value_policy::move` or | | | pointer. Otherwise, it uses :enum:`return_value_policy::move` or |
| | :enum:`return_value_policy::copy` for rvalue and lvalue references, | | | :enum:`return_value_policy::copy` for rvalue and lvalue references, |
| | respectively. See above for a description of what all of these different | | | respectively. See above for a description of what all of these different |
| | policies do. | | | policies do. This is the default policy for ``py::class_``-wrapped types. |
+--------------------------------------------------+----------------------------------------------------------------------------+ +--------------------------------------------------+----------------------------------------------------------------------------+
| :enum:`return_value_policy::automatic_reference` | As above, but use policy :enum:`return_value_policy::reference` when the | | :enum:`return_value_policy::automatic_reference` | As above, but use policy :enum:`return_value_policy::reference` when the |
| | return value is a pointer. This is the default conversion policy for | | | return value is a pointer. This is the default conversion policy for |
| | function arguments when calling Python functions manually from C++ code | | | function arguments when calling Python functions manually from C++ code |
| | (i.e. via handle::operator()). You probably won't need to use this. | | | (i.e. via ``handle::operator()``) and the casters in ``pybind11/stl.h``. |
| | You probably won't need to use this explicitly. |
+--------------------------------------------------+----------------------------------------------------------------------------+ +--------------------------------------------------+----------------------------------------------------------------------------+
Return value policies can also be applied to properties: Return value policies can also be applied to properties:
@ -182,6 +183,9 @@ relies on the ability to create a *weak reference* to the nurse object. When
the nurse object is not a pybind11-registered type and does not support weak the nurse object is not a pybind11-registered type and does not support weak
references, an exception will be thrown. references, an exception will be thrown.
If you use an incorrect argument index, you will get a ``RuntimeError`` saying
``Could not activate keep_alive!``. You should review the indices you're using.
Consider the following example: here, the binding code for a list append Consider the following example: here, the binding code for a list append
operation ties the lifetime of the newly added element to the underlying operation ties the lifetime of the newly added element to the underlying
container: container:
@ -251,7 +255,7 @@ For instance, the following statement iterates over a Python ``dict``:
.. code-block:: cpp .. code-block:: cpp
void print_dict(py::dict dict) { void print_dict(const py::dict& dict) {
/* Easily interact with Python types */ /* Easily interact with Python types */
for (auto item : dict) for (auto item : dict)
std::cout << "key=" << std::string(py::str(item.first)) << ", " std::cout << "key=" << std::string(py::str(item.first)) << ", "
@ -289,7 +293,7 @@ Such functions can also be created using pybind11:
.. code-block:: cpp .. code-block:: cpp
void generic(py::args args, py::kwargs kwargs) { void generic(py::args args, const py::kwargs& kwargs) {
/// .. do something with args /// .. do something with args
if (kwargs) if (kwargs)
/// .. do something with kwargs /// .. do something with kwargs

View File

@ -390,7 +390,7 @@ operation on the C++ side:
py::array b = a[py::make_tuple(0, py::ellipsis(), 0)]; py::array b = a[py::make_tuple(0, py::ellipsis(), 0)];
.. versionchanged:: 2.6 .. versionchanged:: 2.6
``py::ellipsis()`` is now also avaliable in Python 2. ``py::ellipsis()`` is now also available in Python 2.
Memory view Memory view
=========== ===========

View File

@ -47,6 +47,17 @@ redirects output to the corresponding Python streams:
call_noisy_func(); call_noisy_func();
}); });
.. warning::
The implementation in ``pybind11/iostream.h`` is NOT thread safe. Multiple
threads writing to a redirected ostream concurrently cause data races
and potentially buffer overflows. Therefore it is currently a requirement
that all (possibly) concurrent redirected ostream writes are protected by
a mutex. #HelpAppreciated: Work on iostream.h thread safety. For more
background see the discussions under
`PR #2982 <https://github.com/pybind/pybind11/pull/2982>`_ and
`PR #2995 <https://github.com/pybind/pybind11/pull/2995>`_.
This method respects flushes on the output streams and will flush if needed This method respects flushes on the output streams and will flush if needed
when the scoped guard is destroyed. This allows the output to be redirected in when the scoped guard is destroyed. This allows the output to be redirected in
real time, such as to a Jupyter notebook. The two arguments, the C++ stream and real time, such as to a Jupyter notebook. The two arguments, the C++ stream and

View File

@ -6,8 +6,86 @@ Changelog
Starting with version 1.8.0, pybind11 releases use a `semantic versioning Starting with version 1.8.0, pybind11 releases use a `semantic versioning
<http://semver.org>`_ policy. <http://semver.org>`_ policy.
v2.7.0 (TBA, not yet released) v2.8.0 (WIP)
------------------------------ ------------
* Allow exception translators to be optionally registered local to a module
instead of applying globally across all pybind11 modules. Use
``register_local_exception_translator(ExceptionTranslator&& translator)``
instead of ``register_exception_translator(ExceptionTranslator&&
translator)`` to keep your exception remapping code local to the module.
`#2650 <https://github.com/pybind/pybind11/pull/2650>`_
v2.7.1 (Aug 3, 2021)
---------------------
Minor missing functionality added:
* Allow Python builtins to be used as callbacks in CPython.
`#1413 <https://github.com/pybind/pybind11/pull/1413>`_
Bug fixes:
* Fix regression in CMake Python package config: improper use of absolute path.
`#3144 <https://github.com/pybind/pybind11/pull/3144>`_
* Fix Mingw64 and add to the CI testing matrix.
`#3132 <https://github.com/pybind/pybind11/pull/3132>`_
* Specified UTF8-encoding in setup.py calls of open().
`#3137 <https://github.com/pybind/pybind11/pull/3137>`_
* Add clang-tidy-readability rules to make boolean casts explicit improving
code readability. Also enabled other misc and readability clang-tidy checks.
`#3148 <https://github.com/pybind/pybind11/pull/3148>`_
* Move object in ``.pop()`` for list.
`#3116 <https://github.com/pybind/pybind11/pull/3116>`_
Backend and tidying up:
* Removed and fixed warning suppressions.
`#3127 <https://github.com/pybind/pybind11/pull/3127>`_
`#3129 <https://github.com/pybind/pybind11/pull/3129>`_
`#3135 <https://github.com/pybind/pybind11/pull/3135>`_
`#3141 <https://github.com/pybind/pybind11/pull/3141>`_
`#3142 <https://github.com/pybind/pybind11/pull/3142>`_
`#3150 <https://github.com/pybind/pybind11/pull/3150>`_
`#3152 <https://github.com/pybind/pybind11/pull/3152>`_
`#3160 <https://github.com/pybind/pybind11/pull/3160>`_
`#3161 <https://github.com/pybind/pybind11/pull/3161>`_
v2.7.0 (Jul 16, 2021)
---------------------
New features:
* Enable ``py::implicitly_convertible<py::none, ...>`` for
``py::class_``-wrapped types.
`#3059 <https://github.com/pybind/pybind11/pull/3059>`_
* Allow function pointer extraction from overloaded functions.
`#2944 <https://github.com/pybind/pybind11/pull/2944>`_
* NumPy: added ``.char_()`` to type which gives the NumPy public ``char``
result, which also distinguishes types by bit length (unlike ``.kind()``).
`#2864 <https://github.com/pybind/pybind11/pull/2864>`_
* Add ``pybind11::bytearray`` to manipulate ``bytearray`` similar to ``bytes``.
`#2799 <https://github.com/pybind/pybind11/pull/2799>`_
* ``pybind11/stl/filesystem.h`` registers a type caster that, on C++17/Python
3.6+, converts ``std::filesystem::path`` to ``pathlib.Path`` and any
``os.PathLike`` to ``std::filesystem::path``.
`#2730 <https://github.com/pybind/pybind11/pull/2730>`_
* A ``PYBIND11_VERSION_HEX`` define was added, similar to ``PY_VERSION_HEX``.
`#3120 <https://github.com/pybind/pybind11/pull/3120>`_
Changes:
* ``py::str`` changed to exclusively hold `PyUnicodeObject`. Previously * ``py::str`` changed to exclusively hold `PyUnicodeObject`. Previously
``py::str`` could also hold `bytes`, which is probably surprising, was ``py::str`` could also hold `bytes`, which is probably surprising, was
@ -15,6 +93,130 @@ v2.7.0 (TBA, not yet released)
instead of ``py::bytes``). instead of ``py::bytes``).
`#2409 <https://github.com/pybind/pybind11/pull/2409>`_ `#2409 <https://github.com/pybind/pybind11/pull/2409>`_
* Add a safety guard to ensure that the Python GIL is held when C++ calls back
into Python via ``object_api<>::operator()`` (e.g. ``py::function``
``__call__``). (This feature is available for Python 3.6+ only.)
`#2919 <https://github.com/pybind/pybind11/pull/2919>`_
* Catch a missing ``self`` argument in calls to ``__init__()``.
`#2914 <https://github.com/pybind/pybind11/pull/2914>`_
* Use ``std::string_view`` if available to avoid a copy when passing an object
to a ``std::ostream``.
`#3042 <https://github.com/pybind/pybind11/pull/3042>`_
* An important warning about thread safety was added to the ``iostream.h``
documentation; attempts to make ``py::scoped_ostream_redirect`` thread safe
have been removed, as it was only partially effective.
`#2995 <https://github.com/pybind/pybind11/pull/2995>`_
Fixes:
* Performance: avoid unnecessary strlen calls.
`#3058 <https://github.com/pybind/pybind11/pull/3058>`_
* Fix auto-generated documentation string when using ``const T`` in
``pyarray_t``.
`#3020 <https://github.com/pybind/pybind11/pull/3020>`_
* Unify error messages thrown by ``simple_collector``/``unpacking_collector``.
`#3013 <https://github.com/pybind/pybind11/pull/3013>`_
* ``pybind11::builtin_exception`` is now explicitly exported, which means the
types included/defined in different modules are identical, and exceptions
raised in different modules can be caught correctly. The documentation was
updated to explain that custom exceptions that are used across module
boundaries need to be explicitly exported as well.
`#2999 <https://github.com/pybind/pybind11/pull/2999>`_
* Fixed exception when printing UTF-8 to a ``scoped_ostream_redirect``.
`#2982 <https://github.com/pybind/pybind11/pull/2982>`_
* Pickle support enhancement: ``setstate`` implementation will attempt to
``setattr`` ``__dict__`` only if the unpickled ``dict`` object is not empty,
to not force use of ``py::dynamic_attr()`` unnecessarily.
`#2972 <https://github.com/pybind/pybind11/pull/2972>`_
* Allow negative timedelta values to roundtrip.
`#2870 <https://github.com/pybind/pybind11/pull/2870>`_
* Fix unchecked errors could potentially swallow signals/other exceptions.
`#2863 <https://github.com/pybind/pybind11/pull/2863>`_
* Add null pointer check with ``std::localtime``.
`#2846 <https://github.com/pybind/pybind11/pull/2846>`_
* Fix the ``weakref`` constructor from ``py::object`` to create a new
``weakref`` on conversion.
`#2832 <https://github.com/pybind/pybind11/pull/2832>`_
* Avoid relying on exceptions in C++17 when getting a ``shared_ptr`` holder
from a ``shared_from_this`` class.
`#2819 <https://github.com/pybind/pybind11/pull/2819>`_
* Allow the codec's exception to be raised instead of :code:`RuntimeError` when
casting from :code:`py::str` to :code:`std::string`.
`#2903 <https://github.com/pybind/pybind11/pull/2903>`_
Build system improvements:
* In ``setup_helpers.py``, test for platforms that have some multiprocessing
features but lack semaphores, which ``ParallelCompile`` requires.
`#3043 <https://github.com/pybind/pybind11/pull/3043>`_
* Fix ``pybind11_INCLUDE_DIR`` in case ``CMAKE_INSTALL_INCLUDEDIR`` is
absolute.
`#3005 <https://github.com/pybind/pybind11/pull/3005>`_
* Fix bug not respecting ``WITH_SOABI`` or ``WITHOUT_SOABI`` to CMake.
`#2938 <https://github.com/pybind/pybind11/pull/2938>`_
* Fix the default ``Pybind11Extension`` compilation flags with a Mingw64 python.
`#2921 <https://github.com/pybind/pybind11/pull/2921>`_
* Clang on Windows: do not pass ``/MP`` (ignored flag).
`#2824 <https://github.com/pybind/pybind11/pull/2824>`_
* ``pybind11.setup_helpers.intree_extensions`` can be used to generate
``Pybind11Extension`` instances from cpp files placed in the Python package
source tree.
`#2831 <https://github.com/pybind/pybind11/pull/2831>`_
Backend and tidying up:
* Enable clang-tidy performance, readability, and modernization checks
throughout the codebase to enforce best coding practices.
`#3046 <https://github.com/pybind/pybind11/pull/3046>`_,
`#3049 <https://github.com/pybind/pybind11/pull/3049>`_,
`#3051 <https://github.com/pybind/pybind11/pull/3051>`_,
`#3052 <https://github.com/pybind/pybind11/pull/3052>`_,
`#3080 <https://github.com/pybind/pybind11/pull/3080>`_, and
`#3094 <https://github.com/pybind/pybind11/pull/3094>`_
* Checks for common misspellings were added to the pre-commit hooks.
`#3076 <https://github.com/pybind/pybind11/pull/3076>`_
* Changed ``Werror`` to stricter ``Werror-all`` for Intel compiler and fixed
minor issues.
`#2948 <https://github.com/pybind/pybind11/pull/2948>`_
* Fixed compilation with GCC < 5 when the user defines ``_GLIBCXX_USE_CXX11_ABI``.
`#2956 <https://github.com/pybind/pybind11/pull/2956>`_
* Added nox support for easier local testing and linting of contributions.
`#3101 <https://github.com/pybind/pybind11/pull/3101>`_ and
`#3121 <https://github.com/pybind/pybind11/pull/3121>`_
* Avoid RTD style issue with docutils 0.17+.
`#3119 <https://github.com/pybind/pybind11/pull/3119>`_
* Support pipx run, such as ``pipx run pybind11 --include`` for a quick compile.
`#3117 <https://github.com/pybind/pybind11/pull/3117>`_
v2.6.2 (Jan 26, 2021) v2.6.2 (Jan 26, 2021)
--------------------- ---------------------
@ -95,7 +297,7 @@ Bug fixes:
* Fix ``py::gil_scoped_acquire`` assert with CPython 3.9 debug build. * Fix ``py::gil_scoped_acquire`` assert with CPython 3.9 debug build.
`#2683 <https://github.com/pybind/pybind11/pull/2683>`_ `#2683 <https://github.com/pybind/pybind11/pull/2683>`_
* Fix issue with a test failing on PyTest 6.2. * Fix issue with a test failing on pytest 6.2.
`#2741 <https://github.com/pybind/pybind11/pull/2741>`_ `#2741 <https://github.com/pybind/pybind11/pull/2741>`_
Warning fixes: Warning fixes:
@ -504,7 +706,7 @@ v2.4.0 (Sep 19, 2019)
`#1888 <https://github.com/pybind/pybind11/pull/1888>`_. `#1888 <https://github.com/pybind/pybind11/pull/1888>`_.
* ``py::details::overload_cast_impl`` is available in C++11 mode, can be used * ``py::details::overload_cast_impl`` is available in C++11 mode, can be used
like ``overload_cast`` with an additional set of parantheses. like ``overload_cast`` with an additional set of parentheses.
`#1581 <https://github.com/pybind/pybind11/pull/1581>`_. `#1581 <https://github.com/pybind/pybind11/pull/1581>`_.
* Fixed ``get_include()`` on Conda. * Fixed ``get_include()`` on Conda.

View File

@ -70,6 +70,19 @@ that is supported via a ``build_ext`` command override; it will only affect
ext_modules=ext_modules ext_modules=ext_modules
) )
If you have single-file extension modules that are directly stored in the
Python source tree (``foo.cpp`` in the same directory as where a ``foo.py``
would be located), you can also generate ``Pybind11Extensions`` using
``setup_helpers.intree_extensions``: ``intree_extensions(["path/to/foo.cpp",
...])`` returns a list of ``Pybind11Extensions`` which can be passed to
``ext_modules``, possibly after further customizing their attributes
(``libraries``, ``include_dirs``, etc.). By doing so, a ``foo.*.so`` extension
module will be generated and made available upon installation.
``intree_extension`` will automatically detect if you are using a ``src``-style
layout (as long as no namespace packages are involved), but you can also
explicitly pass ``package_dir`` to it (as in ``setuptools.setup``).
Since pybind11 does not require NumPy when building, a light-weight replacement Since pybind11 does not require NumPy when building, a light-weight replacement
for NumPy's parallel compilation distutils tool is included. Use it like this: for NumPy's parallel compilation distutils tool is included. Use it like this:
@ -93,7 +106,7 @@ to a memory dependent number.
If you are developing rapidly and have a lot of C++ files, you may want to If you are developing rapidly and have a lot of C++ files, you may want to
avoid rebuilding files that have not changed. For simple cases were you are avoid rebuilding files that have not changed. For simple cases were you are
using ``pip install -e .`` and do not have local headers, you can skip the using ``pip install -e .`` and do not have local headers, you can skip the
rebuild if a object file is newer than it's source (headers are not checked!) rebuild if an object file is newer than its source (headers are not checked!)
with the following: with the following:
.. code-block:: python .. code-block:: python
@ -149,7 +162,7 @@ Your ``pyproject.toml`` file will likely look something like this:
and ``pyproject.toml`` are not even contained in the wheel, so this high and ``pyproject.toml`` are not even contained in the wheel, so this high
Pip requirement is only for source builds, and will not affect users of Pip requirement is only for source builds, and will not affect users of
your binary wheels. If you are building SDists and wheels, then your binary wheels. If you are building SDists and wheels, then
`pypa-build`_ is the recommended offical tool. `pypa-build`_ is the recommended official tool.
.. _PEP 517: https://www.python.org/dev/peps/pep-0517/ .. _PEP 517: https://www.python.org/dev/peps/pep-0517/
.. _cibuildwheel: https://cibuildwheel.readthedocs.io .. _cibuildwheel: https://cibuildwheel.readthedocs.io
@ -411,7 +424,7 @@ existing targets instead:
.. code-block:: cmake .. code-block:: cmake
cmake_minumum_required(VERSION 3.15...3.19) cmake_minimum_required(VERSION 3.15...3.19)
project(example LANGUAGES CXX) project(example LANGUAGES CXX)
find_package(Python COMPONENTS Interpreter Development REQUIRED) find_package(Python COMPONENTS Interpreter Development REQUIRED)
@ -516,7 +529,7 @@ Instead of setting properties, you can set ``CMAKE_*`` variables to initialize t
compiler flags are provided to ensure high quality code generation. In compiler flags are provided to ensure high quality code generation. In
contrast to the ``pybind11_add_module()`` command, the CMake interface contrast to the ``pybind11_add_module()`` command, the CMake interface
provides a *composable* set of targets to ensure that you retain flexibility. provides a *composable* set of targets to ensure that you retain flexibility.
It can be expecially important to provide or set these properties; the It can be especially important to provide or set these properties; the
:ref:`FAQ <faq:symhidden>` contains an explanation on why these are needed. :ref:`FAQ <faq:symhidden>` contains an explanation on why these are needed.
.. versionadded:: 2.6 .. versionadded:: 2.6

View File

@ -5,7 +5,7 @@ Frequently asked questions
=========================================================== ===========================================================
1. Make sure that the name specified in PYBIND11_MODULE is identical to the 1. Make sure that the name specified in PYBIND11_MODULE is identical to the
filename of the extension library (without suffixes such as .so) filename of the extension library (without suffixes such as ``.so``).
2. If the above did not fix the issue, you are likely using an incompatible 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 version of Python (for instance, the extension library was compiled against
@ -170,7 +170,7 @@ complete independence of the symbols involved when not using
``-fvisibility=hidden``. ``-fvisibility=hidden``.
Additionally, ``-fvisibility=hidden`` can deliver considerably binary size Additionally, ``-fvisibility=hidden`` can deliver considerably binary size
savings. (See the following section for more details). savings. (See the following section for more details.)
.. _`faq:symhidden`: .. _`faq:symhidden`:
@ -180,7 +180,7 @@ How can I create smaller binaries?
To do its job, pybind11 extensively relies on a programming technique known as To do its job, pybind11 extensively relies on a programming technique known as
*template metaprogramming*, which is a way of performing computation at compile *template metaprogramming*, which is a way of performing computation at compile
time using type information. Template metaprogamming usually instantiates code time using type information. Template metaprogramming usually instantiates code
involving significant numbers of deeply nested types that are either completely involving significant numbers of deeply nested types that are either completely
removed or reduced to just a few instructions during the compiler's optimization removed or reduced to just a few instructions during the compiler's optimization
phase. However, due to the nested nature of these types, the resulting symbol phase. However, due to the nested nature of these types, the resulting symbol

View File

@ -57,16 +57,16 @@ clean, well written patch would likely be accepted to solve them.
Python 3.9.0 warning Python 3.9.0 warning
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
Combining older versions of pybind11 (< 2.6.0) with Python on 3.9.0 will Combining older versions of pybind11 (< 2.6.0) with Python on exactly 3.9.0
trigger undefined behavior that typically manifests as crashes during will trigger undefined behavior that typically manifests as crashes during
interpreter shutdown (but could also destroy your data. **You have been interpreter shutdown (but could also destroy your data. **You have been
warned**). warned**).
This issue has been This issue was `fixed in Python <https://github.com/python/cpython/pull/22670>`_.
`fixed in Python <https://github.com/python/cpython/pull/22670>`_. As a As a mitigation for this bug, pybind11 2.6.0 or newer includes a workaround
mitigation until 3.9.1 is released and commonly used, pybind11 (2.6.0 or newer) specifically when Python 3.9.0 is detected at runtime, leaking about 50 bytes
includes a temporary workaround specifically when Python 3.9.0 is detected at of memory when a callback function is garbage collected. For reference, the
runtime, leaking about 50 bytes of memory when a callback function is garbage pybind11 test suite has about 2,000 such callbacks, but only 49 are garbage
collected. For reference; the pybind11 test suite has about 2,000 such collected before the end-of-process. Wheels (even if built with Python 3.9.0)
callbacks, but only 49 are garbage collected before the end-of-process. Wheels will correctly avoid the leak when run in Python 3.9.1, and this does not
built with Python 3.9.0 will correctly avoid the leak when run in Python 3.9.1. affect other 3.X versions.

View File

@ -15,7 +15,8 @@ For example:
For beta, ``PYBIND11_VERSION_PATCH`` should be ``Z.b1``. RC's can be ``Z.rc1``. For beta, ``PYBIND11_VERSION_PATCH`` should be ``Z.b1``. RC's can be ``Z.rc1``.
Always include the dot (even though PEP 440 allows it to be dropped). For a Always include the dot (even though PEP 440 allows it to be dropped). For a
final release, this must be a simple integer. final release, this must be a simple integer. There is also a HEX version of
the version just below.
To release a new version of pybind11: To release a new version of pybind11:
@ -24,6 +25,7 @@ To release a new version of pybind11:
- Update the version number - Update the version number
- Update ``PYBIND11_VERSION_MAJOR`` etc. in - Update ``PYBIND11_VERSION_MAJOR`` etc. in
``include/pybind11/detail/common.h``. PATCH should be a simple integer. ``include/pybind11/detail/common.h``. PATCH should be a simple integer.
- Update the version HEX just below, as well.
- Update ``pybind11/_version.py`` (match above) - Update ``pybind11/_version.py`` (match above)
- Ensure that all the information in ``setup.cfg`` is up-to-date, like - Ensure that all the information in ``setup.cfg`` is up-to-date, like
supported Python versions. supported Python versions.
@ -53,7 +55,7 @@ To release a new version of pybind11:
name like "Version X.Y.Z", and optionally copy-and-paste the changelog into name like "Version X.Y.Z", and optionally copy-and-paste the changelog into
the description (processed as markdown by Pandoc). Check "pre-release" if the description (processed as markdown by Pandoc). Check "pre-release" if
this is a beta/RC. You can get partway there with this is a beta/RC. You can get partway there with
``cat docs/changelog.rst | pandsoc -f rst -t markdown``. ``cat docs/changelog.rst | pandoc -f rst -t gfm``.
- CLI method: with ``gh`` installed, run ``gh release create vX.Y.Z -t "Version X.Y.Z"`` - CLI method: with ``gh`` installed, run ``gh release create vX.Y.Z -t "Version X.Y.Z"``
If this is a pre-release, add ``-p``. If this is a pre-release, add ``-p``.

View File

@ -1,6 +1,7 @@
breathe==4.26.1 breathe==4.26.1
commonmark==0.9.1 # docutils 0.17 breaks HTML tags & RTD theme
recommonmark==0.7.1 # https://github.com/sphinx-doc/sphinx/issues/9001
docutils==0.16
sphinx==3.3.1 sphinx==3.3.1
sphinx_rtd_theme==0.5.0 sphinx_rtd_theme==0.5.0
sphinxcontrib-moderncmakedomain==3.17 sphinxcontrib-moderncmakedomain==3.17

View File

@ -281,7 +281,7 @@ Within pybind11's CMake build system, ``pybind11_add_module`` has always been
setting the ``-fvisibility=hidden`` flag in release mode. From now on, it's setting the ``-fvisibility=hidden`` flag in release mode. From now on, it's
being applied unconditionally, even in debug mode and it can no longer be opted being applied unconditionally, even in debug mode and it can no longer be opted
out of with the ``NO_EXTRAS`` option. The ``pybind11::module`` target now also out of with the ``NO_EXTRAS`` option. The ``pybind11::module`` target now also
adds this flag to it's interface. The ``pybind11::embed`` target is unchanged. adds this flag to its interface. The ``pybind11::embed`` target is unchanged.
The most significant change here is for the ``pybind11::module`` target. If you The most significant change here is for the ``pybind11::module`` target. If you
were previously relying on default visibility, i.e. if your Python module was were previously relying on default visibility, i.e. if your Python module was

View File

@ -62,7 +62,8 @@ struct metaclass {
handle value; handle value;
PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.")
metaclass() { } // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute // NOLINTNEXTLINE(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute
metaclass() {}
/// Override pybind11's default metaclass /// Override pybind11's default metaclass
explicit metaclass(handle value) : value(value) { } explicit metaclass(handle value) : value(value) { }
@ -377,7 +378,7 @@ template <> struct process_attribute<is_new_style_constructor> : process_attribu
}; };
inline void process_kw_only_arg(const arg &a, function_record *r) { inline void process_kw_only_arg(const arg &a, function_record *r) {
if (!a.name || strlen(a.name) == 0) if (!a.name || a.name[0] == '\0')
pybind11_fail("arg(): cannot specify an unnamed argument after an kw_only() annotation"); pybind11_fail("arg(): cannot specify an unnamed argument after an kw_only() annotation");
++r->nargs_kw_only; ++r->nargs_kw_only;
} }

View File

@ -83,7 +83,7 @@ struct buffer_info {
view->strides view->strides
? std::vector<ssize_t>(view->strides, view->strides + view->ndim) ? std::vector<ssize_t>(view->strides, view->strides + view->ndim)
: detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize), : detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize),
view->readonly) { (view->readonly != 0)) {
this->m_view = view; this->m_view = view;
this->ownview = ownview; this->ownview = ownview;
} }
@ -91,11 +91,9 @@ struct buffer_info {
buffer_info(const buffer_info &) = delete; buffer_info(const buffer_info &) = delete;
buffer_info& operator=(const buffer_info &) = delete; buffer_info& operator=(const buffer_info &) = delete;
buffer_info(buffer_info &&other) { buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); }
(*this) = std::move(other);
}
buffer_info& operator=(buffer_info &&rhs) { buffer_info &operator=(buffer_info &&rhs) noexcept {
ptr = rhs.ptr; ptr = rhs.ptr;
itemsize = rhs.itemsize; itemsize = rhs.itemsize;
size = rhs.size; size = rhs.size;

View File

@ -86,24 +86,27 @@ public:
}; };
#define PYBIND11_TYPE_CASTER(type, py_name) \ #define PYBIND11_TYPE_CASTER(type, py_name) \
protected: \ protected: \
type value; \ type value; \
public: \ \
public: \
static constexpr auto name = 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> \ 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) { \ static handle cast(T_ *src, return_value_policy policy, handle parent) { \
if (!src) return none().release(); \ if (!src) \
return none().release(); \
if (policy == return_value_policy::take_ownership) { \ if (policy == return_value_policy::take_ownership) { \
auto h = cast(std::move(*src), policy, parent); delete src; return h; \ auto h = cast(std::move(*src), policy, parent); \
} else { \ delete src; \
return h; \
} \
return cast(*src, policy, parent); \ return cast(*src, policy, parent); \
} \ } \
} \ operator type *() { return &value; } \
operator type*() { return &value; } \ operator type &() { return value; } \
operator type&() { return value; } \ operator type &&() && { return std::move(value); } \
operator type&&() && { return std::move(value); } \ template <typename T_> \
template <typename T_> using cast_op_type = pybind11::detail::movable_cast_op_type<T_> using cast_op_type = pybind11::detail::movable_cast_op_type<T_>
template <typename CharT> using is_std_char_type = any_of< template <typename CharT> using is_std_char_type = any_of<
std::is_same<CharT, char>, /* std::string */ std::is_same<CharT, char>, /* std::string */
@ -178,7 +181,7 @@ public:
// Signed/unsigned checks happen elsewhere // Signed/unsigned checks happen elsewhere
if (py_err || (std::is_integral<T>::value && sizeof(py_type) != sizeof(T) && py_value != (py_type) (T) py_value)) { if (py_err || (std::is_integral<T>::value && sizeof(py_type) != sizeof(T) && py_value != (py_type) (T) py_value)) {
PyErr_Clear(); PyErr_Clear();
if (py_err && convert && PyNumber_Check(src.ptr())) { if (py_err && convert && (PyNumber_Check(src.ptr()) != 0)) {
auto tmp = reinterpret_steal<object>(std::is_floating_point<T>::value auto tmp = reinterpret_steal<object>(std::is_floating_point<T>::value
? PyNumber_Float(src.ptr()) ? PyNumber_Float(src.ptr())
: PyNumber_Long(src.ptr())); : PyNumber_Long(src.ptr()));
@ -222,7 +225,7 @@ public:
return PyLong_FromUnsignedLongLong((unsigned long long) src); return PyLong_FromUnsignedLongLong((unsigned long long) src);
} }
PYBIND11_TYPE_CASTER(T, _x<std::is_integral<T>::value>("int", "float")); PYBIND11_TYPE_CASTER(T, _<std::is_integral<T>::value>("int", "float"));
}; };
template<typename T> struct void_caster { template<typename T> struct void_caster {
@ -235,7 +238,7 @@ public:
static handle cast(T, return_value_policy /* policy */, handle /* parent */) { static handle cast(T, return_value_policy /* policy */, handle /* parent */) {
return none().inc_ref(); return none().inc_ref();
} }
PYBIND11_TYPE_CASTER(T, _x("None")); PYBIND11_TYPE_CASTER(T, _("None"));
}; };
template <> class type_caster<void_type> : public void_caster<void_type> {}; template <> class type_caster<void_type> : public void_caster<void_type> {};
@ -247,7 +250,8 @@ public:
bool load(handle h, bool) { bool load(handle h, bool) {
if (!h) { if (!h) {
return false; return false;
} else if (h.is_none()) { }
if (h.is_none()) {
value = nullptr; value = nullptr;
return true; return true;
} }
@ -272,13 +276,12 @@ public:
static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) {
if (ptr) if (ptr)
return capsule(ptr).release(); return capsule(ptr).release();
else
return none().inc_ref(); return none().inc_ref();
} }
template <typename T> using cast_op_type = void*&; template <typename T> using cast_op_type = void*&;
operator void *&() { return value; } operator void *&() { return value; }
static constexpr auto name = _x("capsule"); static constexpr auto name = _("capsule");
private: private:
void *value = nullptr; void *value = nullptr;
}; };
@ -289,9 +292,15 @@ template <> class type_caster<bool> {
public: public:
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
if (!src) return false; if (!src) return false;
else if (src.ptr() == Py_True) { value = true; return true; } if (src.ptr() == Py_True) {
else if (src.ptr() == Py_False) { value = false; return true; } value = true;
else if (convert || !std::strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name)) { return true;
}
if (src.ptr() == Py_False) {
value = false;
return true;
}
if (convert || (std::strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name) == 0)) {
// (allow non-implicit conversion for numpy booleans) // (allow non-implicit conversion for numpy booleans)
Py_ssize_t res = -1; Py_ssize_t res = -1;
@ -313,18 +322,17 @@ public:
} }
#endif #endif
if (res == 0 || res == 1) { if (res == 0 || res == 1) {
value = (bool) res; value = (res != 0);
return true; return true;
} else {
PyErr_Clear();
} }
PyErr_Clear();
} }
return false; return false;
} }
static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) {
return handle(src ? Py_True : Py_False).inc_ref(); return handle(src ? Py_True : Py_False).inc_ref();
} }
PYBIND11_TYPE_CASTER(bool, _x("bool")); PYBIND11_TYPE_CASTER(bool, _("bool"));
}; };
// Helper class for UTF-{8,16,32} C++ stl strings: // Helper class for UTF-{8,16,32} C++ stl strings:
@ -351,7 +359,8 @@ template <typename StringType, bool IsView = false> struct string_caster {
handle load_src = src; handle load_src = src;
if (!src) { if (!src) {
return false; return false;
} else if (!PyUnicode_Check(load_src.ptr())) { }
if (!PyUnicode_Check(load_src.ptr())) {
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
return load_bytes(load_src); return load_bytes(load_src);
#else #else
@ -393,7 +402,7 @@ template <typename StringType, bool IsView = false> struct string_caster {
return s; return s;
} }
PYBIND11_TYPE_CASTER(StringType, _x(PYBIND11_STRING_NAME)); PYBIND11_TYPE_CASTER(StringType, _(PYBIND11_STRING_NAME));
private: private:
static handle decode_utfN(const char *buffer, ssize_t nbytes) { static handle decode_utfN(const char *buffer, ssize_t nbytes) {
@ -492,10 +501,14 @@ public:
// can fit into a single char value. // can fit into a single char value.
if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) { if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) {
auto v0 = static_cast<unsigned char>(value[0]); auto v0 = static_cast<unsigned char>(value[0]);
size_t char0_bytes = !(v0 & 0x80) ? 1 : // low bits only: 0-127 // low bits only: 0-127
(v0 & 0xE0) == 0xC0 ? 2 : // 0b110xxxxx - start of 2-byte sequence // 0b110xxxxx - start of 2-byte sequence
(v0 & 0xF0) == 0xE0 ? 3 : // 0b1110xxxx - start of 3-byte sequence // 0b1110xxxx - start of 3-byte sequence
4; // 0b11110xxx - start of 4-byte sequence // 0b11110xxx - start of 4-byte sequence
size_t char0_bytes = (v0 & 0x80) == 0 ? 1
: (v0 & 0xE0) == 0xC0 ? 2
: (v0 & 0xF0) == 0xE0 ? 3
: 4;
if (char0_bytes == str_len) { if (char0_bytes == str_len) {
// If we have a 128-255 value, we can decode it into a single char: // If we have a 128-255 value, we can decode it into a single char:
@ -524,7 +537,7 @@ public:
return one_char; return one_char;
} }
static constexpr auto name = _x(PYBIND11_STRING_NAME); static constexpr auto name = _(PYBIND11_STRING_NAME);
template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>; template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>;
}; };
@ -554,13 +567,14 @@ public:
static handle cast(T *src, return_value_policy policy, handle parent) { static handle cast(T *src, return_value_policy policy, handle parent) {
if (!src) return none().release(); if (!src) return none().release();
if (policy == return_value_policy::take_ownership) { if (policy == return_value_policy::take_ownership) {
auto h = cast(std::move(*src), policy, parent); delete src; return h; auto h = cast(std::move(*src), policy, parent);
} else { delete src;
return h;
}
return cast(*src, policy, parent); return cast(*src, policy, parent);
} }
}
static constexpr auto name = _x("Tuple[") + concat(make_caster<Ts>::name...) + _x("]"); static constexpr auto name = _("Tuple[") + concat(make_caster<Ts>::name...) + _("]");
template <typename T> using cast_op_type = type; template <typename T> using cast_op_type = type;
@ -664,15 +678,15 @@ protected:
value = v_h.value_ptr(); value = v_h.value_ptr();
holder = v_h.template holder<holder_type>(); holder = v_h.template holder<holder_type>();
return true; return true;
} else { }
throw cast_error("Unable to cast from non-held to held instance (T& to Holder<T>) " throw cast_error("Unable to cast from non-held to held instance (T& to Holder<T>) "
#if defined(NDEBUG) #if defined(NDEBUG)
"(compile in debug mode for type information)"); "(compile in debug mode for type information)");
#else #else
"of type '" + type_id<holder_type>() + "''"); "of type '"
+ type_id<holder_type>() + "''");
#endif #endif
} }
}
template <typename T = holder_type, detail::enable_if_t<!std::is_constructible<T, const T &, type*>::value, int> = 0> template <typename T = holder_type, detail::enable_if_t<!std::is_constructible<T, const T &, type*>::value, int> = 0>
bool try_implicit_casts(handle, bool) { return false; } bool try_implicit_casts(handle, bool) { return false; }
@ -743,14 +757,14 @@ template <typename base, typename holder> struct is_holder_type :
template <typename base, typename deleter> struct is_holder_type<base, std::unique_ptr<base, deleter>> : template <typename base, typename deleter> struct is_holder_type<base, std::unique_ptr<base, deleter>> :
std::true_type {}; std::true_type {};
template <typename T> struct handle_type_name { static constexpr auto name = _x<T>(); }; template <typename T> struct handle_type_name { static constexpr auto name = _<T>(); };
template <> struct handle_type_name<bytes> { static constexpr auto name = _x(PYBIND11_BYTES_NAME); }; template <> struct handle_type_name<bytes> { static constexpr auto name = _(PYBIND11_BYTES_NAME); };
template <> struct handle_type_name<int_> { static constexpr auto name = _x("int"); }; template <> struct handle_type_name<int_> { static constexpr auto name = _("int"); };
template <> struct handle_type_name<iterable> { static constexpr auto name = _x("Iterable"); }; template <> struct handle_type_name<iterable> { static constexpr auto name = _("Iterable"); };
template <> struct handle_type_name<iterator> { static constexpr auto name = _x("Iterator"); }; template <> struct handle_type_name<iterator> { static constexpr auto name = _("Iterator"); };
template <> struct handle_type_name<none> { static constexpr auto name = _x("None"); }; template <> struct handle_type_name<none> { static constexpr auto name = _("None"); };
template <> struct handle_type_name<args> { static constexpr auto name = _x("*args"); }; template <> struct handle_type_name<args> { static constexpr auto name = _("*args"); };
template <> struct handle_type_name<kwargs> { static constexpr auto name = _x("**kwargs"); }; template <> struct handle_type_name<kwargs> { static constexpr auto name = _("**kwargs"); };
template <typename type> template <typename type>
struct pyobject_caster { struct pyobject_caster {
@ -917,7 +931,6 @@ template <typename T> detail::enable_if_t<detail::move_always<T>::value, T> cast
template <typename T> detail::enable_if_t<detail::move_if_unreferenced<T>::value, T> cast(object &&object) { template <typename T> detail::enable_if_t<detail::move_if_unreferenced<T>::value, T> cast(object &&object) {
if (object.ref_count() > 1) if (object.ref_count() > 1)
return cast<T>(object); return cast<T>(object);
else
return move<T>(std::move(object)); return move<T>(std::move(object));
} }
template <typename T> detail::enable_if_t<detail::move_never<T>::value, T> cast(object &&object) { template <typename T> detail::enable_if_t<detail::move_never<T>::value, T> cast(object &&object) {
@ -958,6 +971,21 @@ template <> inline void cast_safe<void>(object &&) {}
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
// The overloads could coexist, i.e. the #if is not strictly speaking needed,
// but it is an easy minor optimization.
#if defined(NDEBUG)
inline cast_error cast_error_unable_to_convert_call_arg() {
return cast_error(
"Unable to convert call argument to Python object (compile in debug mode for details)");
}
#else
inline cast_error cast_error_unable_to_convert_call_arg(const std::string &name,
const std::string &type) {
return cast_error("Unable to convert call argument '" + name + "' of type '" + type
+ "' to Python object");
}
#endif
template <return_value_policy policy = return_value_policy::automatic_reference> template <return_value_policy policy = return_value_policy::automatic_reference>
tuple make_tuple() { return tuple(0); } tuple make_tuple() { return tuple(0); }
@ -971,11 +999,10 @@ template <return_value_policy policy = return_value_policy::automatic_reference,
for (size_t i = 0; i < args.size(); i++) { for (size_t i = 0; i < args.size(); i++) {
if (!args[i]) { if (!args[i]) {
#if defined(NDEBUG) #if defined(NDEBUG)
throw cast_error("make_tuple(): unable to convert arguments to Python object (compile in debug mode for details)"); throw cast_error_unable_to_convert_call_arg();
#else #else
std::array<std::string, size> argtypes { {type_id<Args>()...} }; std::array<std::string, size> argtypes { {type_id<Args>()...} };
throw cast_error("make_tuple(): unable to convert argument of type '" + throw cast_error_unable_to_convert_call_arg(std::to_string(i), argtypes[i]);
argtypes[i] + "' to Python object");
#endif #endif
} }
} }
@ -1064,7 +1091,9 @@ struct kw_only {};
struct pos_only {}; struct pos_only {};
template <typename T> template <typename T>
arg_v arg::operator=(T &&value) const { return {std::move(*this), std::forward<T>(value)}; } arg_v arg::operator=(T &&value) const {
return {*this, std::forward<T>(value)};
}
/// Alias for backward compatibility -- to be removed in version 2.0 /// Alias for backward compatibility -- to be removed in version 2.0
template <typename /*unused*/> using arg_t = arg_v; template <typename /*unused*/> using arg_t = arg_v;
@ -1228,9 +1257,10 @@ private:
auto o = reinterpret_steal<object>(detail::make_caster<T>::cast(std::forward<T>(x), policy, {})); auto o = reinterpret_steal<object>(detail::make_caster<T>::cast(std::forward<T>(x), policy, {}));
if (!o) { if (!o) {
#if defined(NDEBUG) #if defined(NDEBUG)
argument_cast_error(); throw cast_error_unable_to_convert_call_arg();
#else #else
argument_cast_error(std::to_string(args_list.size()), type_id<T>()); throw cast_error_unable_to_convert_call_arg(
std::to_string(args_list.size()), type_id<T>());
#endif #endif
} }
args_list.append(o); args_list.append(o);
@ -1258,9 +1288,9 @@ private:
} }
if (!a.value) { if (!a.value) {
#if defined(NDEBUG) #if defined(NDEBUG)
argument_cast_error(); throw cast_error_unable_to_convert_call_arg();
#else #else
argument_cast_error(a.name, a.type); throw cast_error_unable_to_convert_call_arg(a.name, a.type);
#endif #endif
} }
m_kwargs[a.name] = a.value; m_kwargs[a.name] = a.value;
@ -1286,7 +1316,7 @@ private:
"may be passed via py::arg() to a python function call. " "may be passed via py::arg() to a python function call. "
"(compile in debug mode for details)"); "(compile in debug mode for details)");
} }
[[noreturn]] static void nameless_argument_error(std::string type) { [[noreturn]] static void nameless_argument_error(const std::string &type) {
throw type_error("Got kwargs without a name of type '" + type + "'; only named " throw type_error("Got kwargs without a name of type '" + type + "'; only named "
"arguments may be passed via py::arg() to a python function call. "); "arguments may be passed via py::arg() to a python function call. ");
} }
@ -1295,20 +1325,10 @@ private:
"(compile in debug mode for details)"); "(compile in debug mode for details)");
} }
[[noreturn]] static void multiple_values_error(std::string name) { [[noreturn]] static void multiple_values_error(const std::string &name) {
throw type_error("Got multiple values for keyword argument '" + name + "'"); throw type_error("Got multiple values for keyword argument '" + name + "'");
} }
[[noreturn]] static void argument_cast_error() {
throw cast_error("Unable to convert call argument to Python object "
"(compile in debug mode for details)");
}
[[noreturn]] static void argument_cast_error(std::string name, std::string type) {
throw cast_error("Unable to convert call argument '" + name
+ "' of type '" + type + "' to Python object");
}
private: private:
tuple m_args; tuple m_args;
dict m_kwargs; dict m_kwargs;
@ -1348,6 +1368,11 @@ unpacking_collector<policy> collect_arguments(Args &&...args) {
template <typename Derived> template <typename Derived>
template <return_value_policy policy, typename... Args> template <return_value_policy policy, typename... Args>
object object_api<Derived>::operator()(Args &&...args) const { object object_api<Derived>::operator()(Args &&...args) const {
#if !defined(NDEBUG) && PY_VERSION_HEX >= 0x03060000
if (!PyGILState_Check()) {
pybind11_fail("pybind11::object_api<>::operator() PyGILState_Check() failure.");
}
#endif
return detail::collect_arguments<policy>(std::forward<Args>(args)...).call(derived().ptr()); return detail::collect_arguments<policy>(std::forward<Args>(args)...).call(derived().ptr());
} }

View File

@ -11,9 +11,14 @@
#pragma once #pragma once
#include "pybind11.h" #include "pybind11.h"
#include <chrono>
#include <cmath> #include <cmath>
#include <ctime> #include <ctime>
#include <chrono> #include <mutex>
#include <time.h>
#include <datetime.h> #include <datetime.h>
// Backport the PyDateTime_DELTA functions from Python3.3 if required // Backport the PyDateTime_DELTA functions from Python3.3 if required
@ -35,7 +40,7 @@ public:
using rep = typename type::rep; using rep = typename type::rep;
using period = typename type::period; using period = typename type::period;
using days = std::chrono::duration<uint_fast32_t, std::ratio<86400>>; using days = std::chrono::duration<int_least32_t, std::ratio<86400>>; // signed 25 bits required by the standard.
bool load(handle src, bool) { bool load(handle src, bool) {
using namespace std::chrono; using namespace std::chrono;
@ -53,11 +58,11 @@ public:
return true; return true;
} }
// If invoked with a float we assume it is seconds and convert // If invoked with a float we assume it is seconds and convert
else if (PyFloat_Check(src.ptr())) { if (PyFloat_Check(src.ptr())) {
value = type(duration_cast<duration<rep, period>>(duration<double>(PyFloat_AsDouble(src.ptr())))); value = type(duration_cast<duration<rep, period>>(duration<double>(PyFloat_AsDouble(src.ptr()))));
return true; return true;
} }
else return false; return false;
} }
// If this is a duration just return it back // If this is a duration just return it back
@ -95,6 +100,22 @@ public:
PYBIND11_TYPE_CASTER(type, _("datetime.timedelta")); PYBIND11_TYPE_CASTER(type, _("datetime.timedelta"));
}; };
inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) {
#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || defined(_MSC_VER)
if (localtime_s(buf, time))
return nullptr;
return buf;
#else
static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
std::tm *tm_ptr = localtime(time);
if (tm_ptr != nullptr) {
*buf = *tm_ptr;
}
return tm_ptr;
#endif
}
// This is for casting times on the system clock into datetime.datetime instances // This is for casting times on the system clock into datetime.datetime instances
template <typename Duration> class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> { template <typename Duration> class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> {
public: public:
@ -162,16 +183,10 @@ public:
// (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t) // (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t)
std::time_t tt = system_clock::to_time_t(time_point_cast<system_clock::duration>(src - us)); std::time_t tt = system_clock::to_time_t(time_point_cast<system_clock::duration>(src - us));
// std::localtime returns a pointer to a static internal std::tm object on success, std::tm localtime;
// or null pointer otherwise std::tm *localtime_ptr = localtime_thread_safe(&tt, &localtime);
std::tm *localtime_ptr = std::localtime(&tt);
if (!localtime_ptr) if (!localtime_ptr)
throw cast_error("Unable to represent system_clock in local time"); throw cast_error("Unable to represent system_clock in local time");
// 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 = *localtime_ptr;
return PyDateTime_FromDateAndTime(localtime.tm_year + 1900, return PyDateTime_FromDateAndTime(localtime.tm_year + 1900,
localtime.tm_mon + 1, localtime.tm_mon + 1,
localtime.tm_mday, localtime.tm_mday,

View File

@ -129,8 +129,9 @@ extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyOb
// 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop`
// 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment
const auto static_prop = (PyObject *) get_internals().static_property_type; const auto static_prop = (PyObject *) get_internals().static_property_type;
const auto call_descr_set = descr && value && PyObject_IsInstance(descr, static_prop) const auto call_descr_set = (descr != nullptr) && (value != nullptr)
&& !PyObject_IsInstance(value, static_prop); && (PyObject_IsInstance(descr, static_prop) != 0)
&& (PyObject_IsInstance(value, static_prop) == 0);
if (call_descr_set) { if (call_descr_set) {
// Call `static_property.__set__()` instead of replacing the `static_property`. // Call `static_property.__set__()` instead of replacing the `static_property`.
#if !defined(PYPY_VERSION) #if !defined(PYPY_VERSION)
@ -162,9 +163,7 @@ extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name
Py_INCREF(descr); Py_INCREF(descr);
return descr; return descr;
} }
else {
return PyType_Type.tp_getattro(obj, name); return PyType_Type.tp_getattro(obj, name);
}
} }
#endif #endif
@ -329,7 +328,7 @@ inline bool deregister_instance(instance *self, void *valptr, const type_info *t
inline PyObject *make_new_instance(PyTypeObject *type) { inline PyObject *make_new_instance(PyTypeObject *type) {
#if defined(PYPY_VERSION) #if defined(PYPY_VERSION)
// PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited
// object is a a plain Python type (i.e. not derived from an extension type). Fix it. // object is a plain Python type (i.e. not derived from an extension type). Fix it.
ssize_t instance_size = static_cast<ssize_t>(sizeof(instance)); ssize_t instance_size = static_cast<ssize_t>(sizeof(instance));
if (type->tp_basicsize < instance_size) { if (type->tp_basicsize < instance_size) {
type->tp_basicsize = instance_size; type->tp_basicsize = instance_size;
@ -564,7 +563,7 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
view->len = view->itemsize; view->len = view->itemsize;
for (auto s : info->shape) for (auto s : info->shape)
view->len *= s; view->len *= s;
view->readonly = info->readonly; view->readonly = static_cast<int>(info->readonly);
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
view->format = const_cast<char *>(info->format.c_str()); view->format = const_cast<char *>(info->format.c_str());
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {

View File

@ -10,8 +10,12 @@
#pragma once #pragma once
#define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MAJOR 2
#define PYBIND11_VERSION_MINOR 6 #define PYBIND11_VERSION_MINOR 7
#define PYBIND11_VERSION_PATCH 3.dev1 #define PYBIND11_VERSION_PATCH 1
// Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html
// Additional convention: 0xD = dev
#define PYBIND11_VERSION_HEX 0x02070100
#define PYBIND11_NAMESPACE_BEGIN(name) namespace name { #define PYBIND11_NAMESPACE_BEGIN(name) namespace name {
#define PYBIND11_NAMESPACE_END(name) } #define PYBIND11_NAMESPACE_END(name) }
@ -52,6 +56,9 @@
# elif __INTEL_COMPILER < 1900 && defined(PYBIND11_CPP14) # elif __INTEL_COMPILER < 1900 && defined(PYBIND11_CPP14)
# error pybind11 supports only C++11 with Intel C++ compiler v18. Use v19 or newer for C++14. # error pybind11 supports only C++11 with Intel C++ compiler v18. Use v19 or newer for C++14.
# endif # endif
/* The following pragma cannot be pop'ed:
https://community.intel.com/t5/Intel-C-Compiler/Inline-and-no-inline-warning/td-p/1216764 */
# pragma warning disable 2196 // warning #2196: routine is both "inline" and "noinline"
#elif defined(__clang__) && !defined(__apple_build_version__) #elif defined(__clang__) && !defined(__apple_build_version__)
# if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3) # if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3)
# error pybind11 requires clang 3.3 or newer # error pybind11 requires clang 3.3 or newer
@ -82,13 +89,27 @@
# endif # endif
#endif #endif
#if !defined(PYBIND11_EXPORT_EXCEPTION)
# ifdef __MINGW32__
// workaround for:
// error: 'dllexport' implies default visibility, but xxx has already been declared with a different visibility
# define PYBIND11_EXPORT_EXCEPTION
# else
# define PYBIND11_EXPORT_EXCEPTION PYBIND11_EXPORT
# endif
#endif
#if defined(_MSC_VER) #if defined(_MSC_VER)
# define PYBIND11_NOINLINE __declspec(noinline) # define PYBIND11_NOINLINE __declspec(noinline)
#else #else
# define PYBIND11_NOINLINE __attribute__ ((noinline)) # define PYBIND11_NOINLINE __attribute__ ((noinline))
#endif #endif
#if defined(PYBIND11_CPP14) #if defined(__MINGW32__)
// For unknown reasons all PYBIND11_DEPRECATED member trigger a warning when declared
// whether it is used or not
# define PYBIND11_DEPRECATED(reason)
#elif defined(PYBIND11_CPP14)
# define PYBIND11_DEPRECATED(reason) [[deprecated(reason)]] # define PYBIND11_DEPRECATED(reason) [[deprecated(reason)]]
#else #else
# define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason))) # define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason)))
@ -108,12 +129,23 @@
# define HAVE_SNPRINTF 1 # define HAVE_SNPRINTF 1
#endif #endif
/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode
#if defined(_MSC_VER) #if defined(_MSC_VER)
# if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 4) # if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 4)
# define HAVE_ROUND 1 # define HAVE_ROUND 1
# endif # endif
# pragma warning(push) # pragma warning(push)
# pragma warning(disable: 4510 4610 4512 4005) // C4505: 'PySlice_GetIndicesEx': unreferenced local function has been removed (PyPy only)
# pragma warning(disable: 4505)
# if defined(_DEBUG) && !defined(Py_DEBUG)
# define PYBIND11_DEBUG_MARKER
# undef _DEBUG
# endif
#endif
// https://en.cppreference.com/w/c/chrono/localtime
#if defined(__STDC_LIB_EXT1__) && !defined(__STDC_WANT_LIB_EXT1__)
# define __STDC_WANT_LIB_EXT1__
#endif #endif
#include <Python.h> #include <Python.h>
@ -475,7 +507,7 @@ struct instance {
void allocate_layout(); void allocate_layout();
/// Destroys/deallocates all of the above /// Destroys/deallocates all of the above
void deallocate_layout(); void deallocate_layout() const;
/// Returns the value_and_holder wrapper for the given type (or the first, if `find_type` /// Returns the value_and_holder wrapper for the given type (or the first, if `find_type`
/// omitted). Returns a default-constructed (with `.inst = nullptr`) object on failure if /// omitted). Returns a default-constructed (with `.inst = nullptr`) object on failure if
@ -718,16 +750,23 @@ using expand_side_effects = bool[];
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4275) // warning C4275: An exported class was derived from a class that wasn't exported. Can be ignored when derived from a STL class.
#endif
/// C++ bindings of builtin Python exceptions /// C++ bindings of builtin Python exceptions
class builtin_exception : public std::runtime_error { class PYBIND11_EXPORT_EXCEPTION builtin_exception : public std::runtime_error {
public: public:
using std::runtime_error::runtime_error; using std::runtime_error::runtime_error;
/// Set the error using the Python C API /// Set the error using the Python C API
virtual void set_error() const = 0; virtual void set_error() const = 0;
}; };
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
#define PYBIND11_RUNTIME_EXCEPTION(name, type) \ #define PYBIND11_RUNTIME_EXCEPTION(name, type) \
class name : public builtin_exception { public: \ class PYBIND11_EXPORT_EXCEPTION name : public builtin_exception { public: \
using builtin_exception::builtin_exception; \ using builtin_exception::builtin_exception; \
name() : name("") { } \ name() : name("") { } \
void set_error() const override { PyErr_SetString(type, what()); } \ void set_error() const override { PyErr_SetString(type, what()); } \
@ -789,7 +828,8 @@ struct nodelete { template <typename T> void operator()(T*) { } };
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template <typename... Args> template <typename... Args>
struct overload_cast_impl { struct overload_cast_impl {
constexpr overload_cast_impl() {}; // NOLINT(modernize-use-equals-default): MSVC 2015 needs this // NOLINTNEXTLINE(modernize-use-equals-default): MSVC 2015 needs this
constexpr overload_cast_impl() {}
template <typename Return> template <typename Return>
constexpr auto operator()(Return (*pf)(Args...)) const noexcept constexpr auto operator()(Return (*pf)(Args...)) const noexcept

View File

@ -23,9 +23,9 @@ PYBIND11_NAMESPACE_BEGIN(detail)
/* Concatenate type signatures at compile time */ /* Concatenate type signatures at compile time */
template <size_t N, typename... Ts> template <size_t N, typename... Ts>
struct descr { struct descr {
char text[N + 1]; char text[N + 1]{'\0'};
constexpr descr() : text{'\0'} { } constexpr descr() = default;
constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence<N>()) { } constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence<N>()) { }
template <size_t... Is> template <size_t... Is>
@ -51,8 +51,8 @@ constexpr descr<N1 + N2, Ts1..., Ts2...> operator+(const descr<N1, Ts1...> &a, c
} }
template <size_t N> template <size_t N>
constexpr descr<N - 1> _x(char const(&text)[N]) { return descr<N - 1>(text); } constexpr descr<N - 1> _(char const(&text)[N]) { return descr<N - 1>(text); }
constexpr descr<0> _x(char const(&)[1]) { return {}; } 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 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...> { template <size_t...Digits> struct int_to_str<0, Digits...> {
@ -61,24 +61,24 @@ template <size_t...Digits> struct int_to_str<0, Digits...> {
// Ternary description (like std::conditional) // Ternary description (like std::conditional)
template <bool B, size_t N1, size_t N2> template <bool B, size_t N1, size_t N2>
constexpr enable_if_t<B, descr<N1 - 1>> _x(char const(&text1)[N1], char const(&)[N2]) { constexpr enable_if_t<B, descr<N1 - 1>> _(char const(&text1)[N1], char const(&)[N2]) {
return _x(text1); return _(text1);
} }
template <bool B, size_t N1, size_t N2> template <bool B, size_t N1, size_t N2>
constexpr enable_if_t<!B, descr<N2 - 1>> _x(char const(&)[N1], char const(&text2)[N2]) { constexpr enable_if_t<!B, descr<N2 - 1>> _(char const(&)[N1], char const(&text2)[N2]) {
return _x(text2); return _(text2);
} }
template <bool B, typename T1, typename T2> template <bool B, typename T1, typename T2>
constexpr enable_if_t<B, T1> _x(const T1 &d, const T2 &) { return d; } constexpr enable_if_t<B, T1> _(const T1 &d, const T2 &) { return d; }
template <bool B, typename T1, typename T2> template <bool B, typename T1, typename T2>
constexpr enable_if_t<!B, T2> _x(const T1 &, const T2 &d) { return d; } constexpr enable_if_t<!B, T2> _(const T1 &, const T2 &d) { return d; }
template <size_t Size> auto constexpr _x() -> decltype(int_to_str<Size / 10, Size % 10>::digits) { template <size_t Size> auto constexpr _() -> decltype(int_to_str<Size / 10, Size % 10>::digits) {
return int_to_str<Size / 10, Size % 10>::digits; return int_to_str<Size / 10, Size % 10>::digits;
} }
template <typename Type> constexpr descr<1, Type> _x() { return {'%'}; } template <typename Type> constexpr descr<1, Type> _() { return {'%'}; }
constexpr descr<0> concat() { return {}; } constexpr descr<0> concat() { return {}; }
@ -88,12 +88,12 @@ constexpr descr<N, Ts...> concat(const descr<N, Ts...> &descr) { return descr; }
template <size_t N, typename... Ts, typename... Args> template <size_t N, typename... Ts, typename... Args>
constexpr auto concat(const descr<N, Ts...> &d, const Args &...args) constexpr auto concat(const descr<N, Ts...> &d, const Args &...args)
-> decltype(std::declval<descr<N + 2, Ts...>>() + concat(args...)) { -> decltype(std::declval<descr<N + 2, Ts...>>() + concat(args...)) {
return d + _x(", ") + concat(args...); return d + _(", ") + concat(args...);
} }
template <size_t N, typename... Ts> template <size_t N, typename... Ts>
constexpr descr<N + 2, Ts...> type_descr(const descr<N, Ts...> &descr) { constexpr descr<N + 2, Ts...> type_descr(const descr<N, Ts...> &descr) {
return _x("{") + descr + _x("}"); return _("{") + descr + _("}");
} }
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)

View File

@ -24,7 +24,7 @@ public:
template <typename> using cast_op_type = value_and_holder &; template <typename> using cast_op_type = value_and_holder &;
operator value_and_holder &() { return *value; } operator value_and_holder &() { return *value; }
static constexpr auto name = _x<value_and_holder>(); static constexpr auto name = _<value_and_holder>();
private: private:
value_and_holder *value = nullptr; value_and_holder *value = nullptr;
@ -293,7 +293,13 @@ template <typename Class, typename T, typename O,
enable_if_t<std::is_convertible<O, handle>::value, int> = 0> enable_if_t<std::is_convertible<O, handle>::value, int> = 0>
void setstate(value_and_holder &v_h, std::pair<T, O> &&result, bool need_alias) { void setstate(value_and_holder &v_h, std::pair<T, O> &&result, bool need_alias) {
construct<Class>(v_h, std::move(result.first), need_alias); construct<Class>(v_h, std::move(result.first), need_alias);
setattr((PyObject *) v_h.inst, "__dict__", result.second); auto d = handle(result.second);
if (PyDict_Check(d.ptr()) && PyDict_Size(d.ptr()) == 0) {
// Skipping setattr below, to not force use of py::dynamic_attr() for Class unnecessarily.
// See PR #2972 for details.
return;
}
setattr((PyObject *) v_h.inst, "__dict__", d);
} }
/// Implementation for py::pickle(GetState, SetState) /// Implementation for py::pickle(GetState, SetState)

View File

@ -276,6 +276,8 @@ PYBIND11_NOINLINE inline internals &get_internals() {
// initial exception translator, below, so add another for our local exception classes. // initial exception translator, below, so add another for our local exception classes.
// //
// libstdc++ doesn't require this (types there are identified only by name) // libstdc++ doesn't require this (types there are identified only by name)
// libc++ with CPython doesn't require this (types are explicitly exported)
// libc++ with PyPy still need it, awaiting further investigation
#if !defined(__GLIBCXX__) #if !defined(__GLIBCXX__)
(*internals_pp)->registered_exception_translators.push_front(&translate_local_exception); (*internals_pp)->registered_exception_translators.push_front(&translate_local_exception);
#endif #endif
@ -291,7 +293,7 @@ PYBIND11_NOINLINE inline internals &get_internals() {
PyThreadState *tstate = PyThreadState_Get(); PyThreadState *tstate = PyThreadState_Get();
#if PY_VERSION_HEX >= 0x03070000 #if PY_VERSION_HEX >= 0x03070000
internals_ptr->tstate = PyThread_tss_alloc(); internals_ptr->tstate = PyThread_tss_alloc();
if (!internals_ptr->tstate || PyThread_tss_create(internals_ptr->tstate)) if (!internals_ptr->tstate || (PyThread_tss_create(internals_ptr->tstate) != 0))
pybind11_fail("get_internals: could not successfully initialize the TSS key!"); pybind11_fail("get_internals: could not successfully initialize the TSS key!");
PyThread_tss_set(internals_ptr->tstate, tstate); PyThread_tss_set(internals_ptr->tstate, tstate);
#else #else

View File

@ -231,7 +231,7 @@ struct value_and_holder {
return reinterpret_cast<V *&>(vh[0]); return reinterpret_cast<V *&>(vh[0]);
} }
// True if this `value_and_holder` has a non-null value pointer // True if this `value_and_holder` has a non-null value pointer
explicit operator bool() const { return value_ptr(); } explicit operator bool() const { return value_ptr() != nullptr; }
template <typename H> H &holder() const { template <typename H> H &holder() const {
return reinterpret_cast<H &>(vh[1]); return reinterpret_cast<H &>(vh[1]);
@ -239,9 +239,9 @@ struct value_and_holder {
bool holder_constructed() const { bool holder_constructed() const {
return inst->simple_layout return inst->simple_layout
? inst->simple_holder_constructed ? inst->simple_holder_constructed
: inst->nonsimple.status[index] & instance::status_holder_constructed; : (inst->nonsimple.status[index] & instance::status_holder_constructed) != 0u;
} }
void set_holder_constructed(bool v = true) { void set_holder_constructed(bool v = true) const {
if (inst->simple_layout) if (inst->simple_layout)
inst->simple_holder_constructed = v; inst->simple_holder_constructed = v;
else if (v) else if (v)
@ -252,9 +252,9 @@ struct value_and_holder {
bool instance_registered() const { bool instance_registered() const {
return inst->simple_layout return inst->simple_layout
? inst->simple_instance_registered ? inst->simple_instance_registered
: inst->nonsimple.status[index] & instance::status_instance_registered; : ((inst->nonsimple.status[index] & instance::status_instance_registered) != 0);
} }
void set_instance_registered(bool v = true) { void set_instance_registered(bool v = true) const {
if (inst->simple_layout) if (inst->simple_layout)
inst->simple_instance_registered = v; inst->simple_instance_registered = v;
else if (v) else if (v)
@ -397,7 +397,7 @@ PYBIND11_NOINLINE inline void instance::allocate_layout() {
owned = true; owned = true;
} }
PYBIND11_NOINLINE inline void instance::deallocate_layout() { PYBIND11_NOINLINE inline void instance::deallocate_layout() const {
if (!simple_layout) if (!simple_layout)
PyMem_Free(nonsimple.values_and_holders); PyMem_Free(nonsimple.values_and_holders);
} }
@ -657,12 +657,6 @@ public:
PYBIND11_NOINLINE bool load_impl(handle src, bool convert) { PYBIND11_NOINLINE bool load_impl(handle src, bool convert) {
if (!src) return false; if (!src) return false;
if (!typeinfo) return try_load_foreign_module_local(src); if (!typeinfo) return try_load_foreign_module_local(src);
if (src.is_none()) {
// Defer accepting None to other overloads (if we aren't in convert mode):
if (!convert) return false;
value = nullptr;
return true;
}
auto &this_ = static_cast<ThisT &>(*this); auto &this_ = static_cast<ThisT &>(*this);
this_.check_holder_compat(); this_.check_holder_compat();
@ -676,7 +670,7 @@ public:
return true; return true;
} }
// Case 2: We have a derived class // Case 2: We have a derived class
else if (PyType_IsSubtype(srctype, typeinfo->type)) { if (PyType_IsSubtype(srctype, typeinfo->type)) {
auto &bases = all_type_info(srctype); auto &bases = all_type_info(srctype);
bool no_cpp_mi = typeinfo->simple_type; bool no_cpp_mi = typeinfo->simple_type;
@ -693,7 +687,7 @@ public:
// Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if // Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if
// we can find an exact match (or, for a simple C++ type, an inherited match); if so, we // we can find an exact match (or, for a simple C++ type, an inherited match); if so, we
// can safely reinterpret_cast to the relevant pointer. // can safely reinterpret_cast to the relevant pointer.
else if (bases.size() > 1) { if (bases.size() > 1) {
for (auto base : bases) { for (auto base : bases) {
if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) { if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) {
this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder(base)); this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder(base));
@ -731,7 +725,19 @@ public:
} }
// Global typeinfo has precedence over foreign module_local // Global typeinfo has precedence over foreign module_local
return try_load_foreign_module_local(src); if (try_load_foreign_module_local(src)) {
return true;
}
// Custom converters didn't take None, now we convert None to nullptr.
if (src.is_none()) {
// Defer accepting None to other overloads (if we aren't in convert mode):
if (!convert) return false;
value = nullptr;
return true;
}
return false;
} }
@ -859,7 +865,7 @@ template <typename type> class type_caster_base : public type_caster_generic {
using itype = intrinsic_t<type>; using itype = intrinsic_t<type>;
public: public:
static constexpr auto name = _x<type>(); static constexpr auto name = _<type>();
type_caster_base() : type_caster_base(typeid(type)) { } type_caster_base() : type_caster_base(typeid(type)) { }
explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { } explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { }

View File

@ -169,22 +169,19 @@ template <typename Type_> struct EigenProps {
return false; // Vector size mismatch return false; // Vector size mismatch
return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride}; return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride};
} }
else if (fixed) { if (fixed) {
// The type has a fixed size, but is not a vector: abort // The type has a fixed size, but is not a vector: abort
return false; return false;
} }
else if (fixed_cols) { if (fixed_cols) {
// Since this isn't a vector, cols must be != 1. We allow this only if it exactly // Since this isn't a vector, cols must be != 1. We allow this only if it exactly
// equals the number of elements (rows is Dynamic, and so 1 row is allowed). // equals the number of elements (rows is Dynamic, and so 1 row is allowed).
if (cols != n) return false; if (cols != n) return false;
return {1, n, stride}; return {1, n, stride};
} } // Otherwise it's either fully dynamic, or column dynamic; both become a column vector
else {
// Otherwise it's either fully dynamic, or column dynamic; both become a column vector
if (fixed_rows && rows != n) return false; if (fixed_rows && rows != n) return false;
return {n, 1, stride}; return {n, 1, stride};
} }
}
static constexpr bool show_writeable = is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value; 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_order = is_eigen_dense_map<Type>::value;

View File

@ -76,7 +76,7 @@ struct embedded_module {
using init_t = void (*)(); using init_t = void (*)();
#endif #endif
embedded_module(const char *name, init_t init) { embedded_module(const char *name, init_t init) {
if (Py_IsInitialized()) if (Py_IsInitialized() != 0)
pybind11_fail("Can't add new modules after the interpreter has been initialized"); pybind11_fail("Can't add new modules after the interpreter has been initialized");
auto result = PyImport_AppendInittab(name, init); auto result = PyImport_AppendInittab(name, init);
@ -101,7 +101,7 @@ PYBIND11_NAMESPACE_END(detail)
.. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx .. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx
\endrst */ \endrst */
inline void initialize_interpreter(bool init_signal_handlers = true) { inline void initialize_interpreter(bool init_signal_handlers = true) {
if (Py_IsInitialized()) if (Py_IsInitialized() != 0)
pybind11_fail("The interpreter is already running"); pybind11_fail("The interpreter is already running");
Py_InitializeEx(init_signal_handlers ? 1 : 0); Py_InitializeEx(init_signal_handlers ? 1 : 0);

View File

@ -11,6 +11,8 @@
#pragma once #pragma once
#include <utility>
#include "pybind11.h" #include "pybind11.h"
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
@ -43,7 +45,7 @@ enum eval_mode {
}; };
template <eval_mode mode = eval_expr> template <eval_mode mode = eval_expr>
object eval(str expr, object global = globals(), object local = object()) { object eval(const str &expr, object global = globals(), object local = object()) {
if (!local) if (!local)
local = global; local = global;
@ -53,7 +55,7 @@ object eval(str expr, object global = globals(), object local = object()) {
this seems to be the only alternative */ this seems to be the only alternative */
std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr; std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr;
int start; int start = 0;
switch (mode) { switch (mode) {
case eval_expr: start = Py_eval_input; break; case eval_expr: start = Py_eval_input; break;
case eval_single_statement: start = Py_single_input; break; case eval_single_statement: start = Py_single_input; break;
@ -75,8 +77,8 @@ object eval(const char (&s)[N], object global = globals(), object local = object
return eval<mode>(expr, global, local); return eval<mode>(expr, global, local);
} }
inline void exec(str expr, object global = globals(), object local = object()) { inline void exec(const str &expr, object global = globals(), object local = object()) {
eval<eval_statements>(expr, global, local); eval<eval_statements>(expr, std::move(global), std::move(local));
} }
template <size_t N> template <size_t N>
@ -105,7 +107,7 @@ object eval_file(str fname, object global = globals(), object local = object())
detail::ensure_builtins_in_globals(global); detail::ensure_builtins_in_globals(global);
int start; int start = 0;
switch (mode) { switch (mode) {
case eval_expr: start = Py_eval_input; break; case eval_expr: start = Py_eval_input; break;
case eval_single_statement: start = Py_single_input; break; case eval_single_statement: start = Py_single_input; break;

View File

@ -43,24 +43,38 @@ public:
captured variables), in which case the roundtrip can be avoided. captured variables), in which case the roundtrip can be avoided.
*/ */
if (auto cfunc = func.cpp_function()) { if (auto cfunc = func.cpp_function()) {
auto c = reinterpret_borrow<capsule>(PyCFunction_GET_SELF(cfunc.ptr())); auto cfunc_self = PyCFunction_GET_SELF(cfunc.ptr());
if (isinstance<capsule>(cfunc_self)) {
auto c = reinterpret_borrow<capsule>(cfunc_self);
auto rec = (function_record *) c; auto rec = (function_record *) c;
if (rec && rec->is_stateless && while (rec != nullptr) {
same_type(typeid(function_type), *reinterpret_cast<const std::type_info *>(rec->data[1]))) { if (rec->is_stateless
struct capture { function_type f; }; && same_type(typeid(function_type),
*reinterpret_cast<const std::type_info *>(rec->data[1]))) {
struct capture {
function_type f;
};
value = ((capture *) &rec->data)->f; value = ((capture *) &rec->data)->f;
return true; return true;
} }
rec = rec->next;
}
}
// PYPY segfaults here when passing builtin function like sum.
// Raising an fail exception here works to prevent the segfault, but only on gcc.
// See PR #1413 for full details
} }
// ensure GIL is held during functor destruction // ensure GIL is held during functor destruction
struct func_handle { struct func_handle {
function f; function f;
func_handle(function&& f_) : f(std::move(f_)) {} func_handle(function &&f_) noexcept : f(std::move(f_)) {}
func_handle(const func_handle& f_) { func_handle(const func_handle &f_) { operator=(f_); }
func_handle &operator=(const func_handle &f_) {
gil_scoped_acquire acq; gil_scoped_acquire acq;
f = f_.f; f = f_.f;
return *this;
} }
~func_handle() { ~func_handle() {
gil_scoped_acquire acq; gil_scoped_acquire acq;
@ -71,7 +85,7 @@ public:
// to emulate 'move initialization capture' in C++11 // to emulate 'move initialization capture' in C++11
struct func_wrapper { struct func_wrapper {
func_handle hfunc; func_handle hfunc;
func_wrapper(func_handle&& hf): hfunc(std::move(hf)) {} func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {}
Return operator()(Args... args) const { Return operator()(Args... args) const {
gil_scoped_acquire acq; gil_scoped_acquire acq;
object retval(hfunc.f(std::forward<Args>(args)...)); object retval(hfunc.f(std::forward<Args>(args)...));
@ -92,7 +106,6 @@ public:
auto result = f_.template target<function_type>(); auto result = f_.template target<function_type>();
if (result) if (result)
return cpp_function(*result, policy).release(); return cpp_function(*result, policy).release();
else
return cpp_function(std::forward<Func>(f_), policy).release(); return cpp_function(std::forward<Func>(f_), policy).release();
} }

View File

@ -5,17 +5,31 @@
All rights reserved. Use of this source code is governed by a All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
WARNING: The implementation in this file is NOT thread safe. Multiple
threads writing to a redirected ostream concurrently cause data races
and potentially buffer overflows. Therefore it is currently a requirement
that all (possibly) concurrent redirected ostream writes are protected by
a mutex.
#HelpAppreciated: Work on iostream.h thread safety.
For more background see the discussions under
https://github.com/pybind/pybind11/pull/2982 and
https://github.com/pybind/pybind11/pull/2995.
*/ */
#pragma once #pragma once
#include "pybind11.h" #include "pybind11.h"
#include <streambuf> #include <algorithm>
#include <ostream> #include <cstring>
#include <string>
#include <memory>
#include <iostream> #include <iostream>
#include <iterator>
#include <memory>
#include <ostream>
#include <streambuf>
#include <string>
#include <utility>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
@ -38,25 +52,68 @@ private:
return sync() == 0 ? traits_type::not_eof(c) : traits_type::eof(); return sync() == 0 ? traits_type::not_eof(c) : traits_type::eof();
} }
// This function must be non-virtual to be called in a destructor. If the // Computes how many bytes at the end of the buffer are part of an
// rare MSVC test failure shows up with this version, then this should be // incomplete sequence of UTF-8 bytes.
// simplified to a fully qualified call. // Precondition: pbase() < pptr()
int _sync() { size_t utf8_remainder() const {
if (pbase() != pptr()) { const auto rbase = std::reverse_iterator<char *>(pbase());
const auto rpptr = std::reverse_iterator<char *>(pptr());
auto is_ascii = [](char c) {
return (static_cast<unsigned char>(c) & 0x80) == 0x00;
};
auto is_leading = [](char c) {
return (static_cast<unsigned char>(c) & 0xC0) == 0xC0;
};
auto is_leading_2b = [](char c) {
return static_cast<unsigned char>(c) <= 0xDF;
};
auto is_leading_3b = [](char c) {
return static_cast<unsigned char>(c) <= 0xEF;
};
// If the last character is ASCII, there are no incomplete code points
if (is_ascii(*rpptr))
return 0;
// Otherwise, work back from the end of the buffer and find the first
// UTF-8 leading byte
const auto rpend = rbase - rpptr >= 3 ? rpptr + 3 : rbase;
const auto leading = std::find_if(rpptr, rpend, is_leading);
if (leading == rbase)
return 0;
const auto dist = static_cast<size_t>(leading - rpptr);
size_t remainder = 0;
{ if (dist == 0)
gil_scoped_acquire tmp; remainder = 1; // 1-byte code point is impossible
else if (dist == 1)
// This subtraction cannot be negative, so dropping the sign. remainder = is_leading_2b(*leading) ? 0 : dist + 1;
str line(pbase(), static_cast<size_t>(pptr() - pbase())); else if (dist == 2)
remainder = is_leading_3b(*leading) ? 0 : dist + 1;
pywrite(line); // else if (dist >= 3), at least 4 bytes before encountering an UTF-8
pyflush(); // leading byte, either no remainder or invalid UTF-8.
// Invalid UTF-8 will cause an exception later when converting
// Placed inside gil_scoped_aquire as a mutex to avoid a race // to a Python string, so that's not handled here.
setp(pbase(), epptr()); return remainder;
} }
// This function must be non-virtual to be called in a destructor.
int _sync() {
if (pbase() != pptr()) { // If buffer is not empty
gil_scoped_acquire tmp;
// This subtraction cannot be negative, so dropping the sign.
auto size = static_cast<size_t>(pptr() - pbase());
size_t remainder = utf8_remainder();
if (size > remainder) {
str line(pbase(), size - remainder);
pywrite(line);
pyflush();
}
// Copy the remainder at the end of the buffer to the beginning:
if (remainder > 0)
std::memmove(pbase(), pptr() - remainder, remainder);
setp(pbase(), epptr());
pbump(static_cast<int>(remainder));
} }
return 0; return 0;
} }
@ -66,11 +123,8 @@ private:
} }
public: public:
pythonbuf(const object &pyostream, size_t buffer_size = 1024)
pythonbuf(object pyostream, size_t buffer_size = 1024) : buf_size(buffer_size), d_buffer(new char[buf_size]), pywrite(pyostream.attr("write")),
: buf_size(buffer_size),
d_buffer(new char[buf_size]),
pywrite(pyostream.attr("write")),
pyflush(pyostream.attr("flush")) { pyflush(pyostream.attr("flush")) {
setp(d_buffer.get(), d_buffer.get() + buf_size - 1); setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
} }
@ -117,9 +171,8 @@ protected:
detail::pythonbuf buffer; detail::pythonbuf buffer;
public: public:
scoped_ostream_redirect( scoped_ostream_redirect(std::ostream &costream = std::cout,
std::ostream &costream = std::cout, const object &pyostream = module_::import("sys").attr("stdout"))
object pyostream = module_::import("sys").attr("stdout"))
: costream(costream), buffer(pyostream) { : costream(costream), buffer(pyostream) {
old = costream.rdbuf(&buffer); old = costream.rdbuf(&buffer);
} }
@ -148,10 +201,9 @@ public:
\endrst */ \endrst */
class scoped_estream_redirect : public scoped_ostream_redirect { class scoped_estream_redirect : public scoped_ostream_redirect {
public: public:
scoped_estream_redirect( scoped_estream_redirect(std::ostream &costream = std::cerr,
std::ostream &costream = std::cerr, const object &pyostream = module_::import("sys").attr("stderr"))
object pyostream = module_::import("sys").attr("stderr")) : scoped_ostream_redirect(costream, pyostream) {}
: scoped_ostream_redirect(costream,pyostream) {}
}; };
@ -210,11 +262,12 @@ PYBIND11_NAMESPACE_END(detail)
m.noisy_function_with_error_printing() m.noisy_function_with_error_printing()
\endrst */ \endrst */
inline class_<detail::OstreamRedirect> add_ostream_redirect(module_ m, std::string name = "ostream_redirect") { inline class_<detail::OstreamRedirect>
return class_<detail::OstreamRedirect>(m, name.c_str(), module_local()) add_ostream_redirect(module_ m, const std::string &name = "ostream_redirect") {
.def(init<bool,bool>(), arg("stdout")=true, arg("stderr")=true) return class_<detail::OstreamRedirect>(std::move(m), name.c_str(), module_local())
.def(init<bool, bool>(), arg("stdout") = true, arg("stderr") = true)
.def("__enter__", &detail::OstreamRedirect::enter) .def("__enter__", &detail::OstreamRedirect::enter)
.def("__exit__", [](detail::OstreamRedirect &self_, args) { self_.exit(); }); .def("__exit__", [](detail::OstreamRedirect &self_, const args &) { self_.exit(); });
} }
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@ -164,10 +164,10 @@ struct npy_api {
NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_), NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_),
}; };
typedef struct { struct PyArray_Dims {
Py_intptr_t *ptr; Py_intptr_t *ptr;
int len; int len;
} PyArray_Dims; };
static npy_api& get() { static npy_api& get() {
static npy_api api = lookup(); static npy_api api = lookup();
@ -319,14 +319,13 @@ template <typename T> using remove_all_extents_t = typename array_info<T>::type;
template <typename T> using is_pod_struct = all_of< template <typename T> using is_pod_struct = all_of<
std::is_standard_layout<T>, // since we're accessing directly in memory we need a standard layout type std::is_standard_layout<T>, // since we're accessing directly in memory we need a standard layout type
#if !defined(__GNUG__) || defined(_LIBCPP_VERSION) || defined(_GLIBCXX_USE_CXX11_ABI) #if defined(__GLIBCXX__) && (__GLIBCXX__ < 20150422 || __GLIBCXX__ == 20150623 || __GLIBCXX__ == 20150626 || __GLIBCXX__ == 20160803)
// _GLIBCXX_USE_CXX11_ABI indicates that we're using libstdc++ from GCC 5 or newer, independent // libstdc++ < 5 (including versions 4.8.5, 4.9.3 and 4.9.4 which were released after 5)
// of the actual compiler (Clang can also use libstdc++, but it always defines __GNUC__ == 4). // don't implement is_trivially_copyable, so approximate it
std::is_trivially_copyable<T>,
#else
// GCC 4 doesn't implement is_trivially_copyable, so approximate it
std::is_trivially_destructible<T>, std::is_trivially_destructible<T>,
satisfies_any_of<T, std::has_trivial_copy_constructor, std::has_trivial_copy_assign>, satisfies_any_of<T, std::has_trivial_copy_constructor, std::has_trivial_copy_assign>,
#else
std::is_trivially_copyable<T>,
#endif #endif
satisfies_none_of<T, std::is_reference, std::is_array, is_std_array, std::is_arithmetic, is_complex, std::is_enum> satisfies_none_of<T, std::is_reference, std::is_array, is_std_array, std::is_arithmetic, is_complex, std::is_enum>
>; >;
@ -466,7 +465,9 @@ public:
explicit dtype(const buffer_info &info) { explicit dtype(const buffer_info &info) {
dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format))); dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format)));
// If info.itemsize == 0, use the value calculated from the format string // If info.itemsize == 0, use the value calculated from the format string
m_ptr = descr.strip_padding(info.itemsize ? info.itemsize : descr.itemsize()).release().ptr(); m_ptr = descr.strip_padding(info.itemsize != 0 ? info.itemsize : descr.itemsize())
.release()
.ptr();
} }
explicit dtype(const std::string &format) { explicit dtype(const std::string &format) {
@ -487,7 +488,7 @@ public:
/// This is essentially the same as calling numpy.dtype(args) in Python. /// This is essentially the same as calling numpy.dtype(args) in Python.
static dtype from_args(object args) { static dtype from_args(object args) {
PyObject *ptr = nullptr; PyObject *ptr = nullptr;
if (!detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) || !ptr) if ((detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) == 0) || !ptr)
throw error_already_set(); throw error_already_set();
return reinterpret_steal<dtype>(ptr); return reinterpret_steal<dtype>(ptr);
} }
@ -543,7 +544,7 @@ private:
auto name = spec[0].cast<pybind11::str>(); auto name = spec[0].cast<pybind11::str>();
auto format = spec[1].cast<tuple>()[0].cast<dtype>(); auto format = spec[1].cast<tuple>()[0].cast<dtype>();
auto offset = spec[1].cast<tuple>()[1].cast<pybind11::int_>(); auto offset = spec[1].cast<tuple>()[1].cast<pybind11::int_>();
if (!len(name) && format.kind() == 'V') if ((len(name) == 0u) && format.kind() == 'V')
continue; continue;
field_descriptors.push_back({(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset}); field_descriptors.push_back({(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset});
} }
@ -873,11 +874,12 @@ public:
: array(std::move(shape), std::move(strides), ptr, base) { } : array(std::move(shape), std::move(strides), ptr, base) { }
explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle()) explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle())
: array_t(private_ctor{}, std::move(shape), : array_t(private_ctor{},
ExtraFlags & f_style std::move(shape),
? detail::f_strides(*shape, itemsize()) (ExtraFlags & f_style) != 0 ? detail::f_strides(*shape, itemsize())
: detail::c_strides(*shape, itemsize()), : detail::c_strides(*shape, itemsize()),
ptr, base) { } ptr,
base) {}
explicit array_t(ssize_t count, const T *ptr = nullptr, handle base = handle()) explicit array_t(ssize_t count, const T *ptr = nullptr, handle base = handle())
: array({count}, {}, ptr, base) { } : array({count}, {}, ptr, base) { }
@ -1030,7 +1032,10 @@ struct npy_format_descriptor_name<T, enable_if_t<std::is_integral<T>::value>> {
template <typename T> template <typename T>
struct npy_format_descriptor_name<T, enable_if_t<std::is_floating_point<T>::value>> { 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>( static constexpr auto name = _<std::is_same<T, float>::value
|| std::is_same<T, const float>::value
|| std::is_same<T, double>::value
|| std::is_same<T, const double>::value>(
_("numpy.float") + _<sizeof(T)*8>(), _("numpy.longdouble") _("numpy.float") + _<sizeof(T)*8>(), _("numpy.longdouble")
); );
}; };
@ -1038,7 +1043,9 @@ struct npy_format_descriptor_name<T, enable_if_t<std::is_floating_point<T>::valu
template <typename T> template <typename T>
struct npy_format_descriptor_name<T, enable_if_t<is_complex<T>::value>> { 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 static constexpr auto name = _<std::is_same<typename T::value_type, float>::value
|| std::is_same<typename T::value_type, double>::value>( || std::is_same<typename T::value_type, const float>::value
|| std::is_same<typename T::value_type, double>::value
|| std::is_same<typename T::value_type, const double>::value>(
_("numpy.complex") + _<sizeof(typename T::value_type)*16>(), _("numpy.longcomplex") _("numpy.complex") + _<sizeof(typename T::value_type)*16>(), _("numpy.longcomplex")
); );
}; };
@ -1287,7 +1294,7 @@ public:
using value_type = container_type::value_type; using value_type = container_type::value_type;
using size_type = container_type::size_type; using size_type = container_type::size_type;
common_iterator() : p_ptr(0), m_strides() {} common_iterator() : m_strides() {}
common_iterator(void* ptr, const container_type& strides, const container_type& shape) common_iterator(void* ptr, const container_type& strides, const container_type& shape)
: p_ptr(reinterpret_cast<char*>(ptr)), m_strides(strides.size()) { : p_ptr(reinterpret_cast<char*>(ptr)), m_strides(strides.size()) {
@ -1308,7 +1315,7 @@ public:
} }
private: private:
char* p_ptr; char *p_ptr{0};
container_type m_strides; container_type m_strides;
}; };
@ -1336,9 +1343,8 @@ public:
if (++m_index[i] != m_shape[i]) { if (++m_index[i] != m_shape[i]) {
increment_common_iterator(i); increment_common_iterator(i);
break; break;
} else {
m_index[i] = 0;
} }
m_index[i] = 0;
} }
return *this; return *this;
} }
@ -1489,7 +1495,6 @@ struct vectorize_returned_array {
static Type create(broadcast_trivial trivial, const std::vector<ssize_t> &shape) { static Type create(broadcast_trivial trivial, const std::vector<ssize_t> &shape) {
if (trivial == broadcast_trivial::f_trivial) if (trivial == broadcast_trivial::f_trivial)
return array_t<Return, array::f_style>(shape); return array_t<Return, array::f_style>(shape);
else
return array_t<Return>(shape); return array_t<Return>(shape);
} }

View File

@ -10,36 +10,14 @@
#pragma once #pragma once
#if defined(__INTEL_COMPILER) #if defined(_MSC_VER) && !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(push)
# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter # pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant # pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
# pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted #elif defined(__GNUG__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
# pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning)
# 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
# pragma warning(disable: 4505) // warning C4505: 'PySlice_GetIndicesEx': unreferenced local function has been removed (PyPy only)
#elif defined(__GNUG__) && !defined(__clang__)
# pragma GCC diagnostic push # pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-but-set-parameter" # pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
# pragma GCC diagnostic ignored "-Wunused-but-set-variable"
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
# pragma GCC diagnostic ignored "-Wstrict-aliasing"
# pragma GCC diagnostic ignored "-Wattributes" # pragma GCC diagnostic ignored "-Wattributes"
# if __GNUC__ >= 7
# pragma GCC diagnostic ignored "-Wnoexcept-type"
# endif
#endif #endif
#include "attr.h" #include "attr.h"
@ -49,16 +27,44 @@
#include "detail/init.h" #include "detail/init.h"
#include <memory> #include <memory>
#include <new>
#include <vector> #include <vector>
#include <string> #include <string>
#include <utility> #include <utility>
#include <string.h>
#if defined(__cpp_lib_launder) && !(defined(_MSC_VER) && (_MSC_VER < 1914))
# define PYBIND11_STD_LAUNDER std::launder
# define PYBIND11_HAS_STD_LAUNDER 1
#else
# define PYBIND11_STD_LAUNDER
# define PYBIND11_HAS_STD_LAUNDER 0
#endif
#if defined(__GNUG__) && !defined(__clang__) #if defined(__GNUG__) && !defined(__clang__)
# include <cxxabi.h> # include <cxxabi.h>
#endif #endif
/* https://stackoverflow.com/questions/46798456/handling-gccs-noexcept-type-warning
This warning is about ABI compatibility, not code health.
It is only actually needed in a couple places, but apparently GCC 7 "generates this warning if
and only if the first template instantiation ... involves noexcept" [stackoverflow], therefore
it could get triggered from seemingly random places, depending on user code.
No other GCC version generates this warning.
*/
#if defined(__GNUC__) && __GNUC__ == 7
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wnoexcept-type"
#endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
#if defined(_MSC_VER)
# define PYBIND11_COMPAT_STRDUP _strdup
#else
# define PYBIND11_COMPAT_STRDUP strdup
#endif
/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object /// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object
class cpp_function : public function { class cpp_function : public function {
public: public:
@ -143,16 +149,29 @@ protected:
/* Without these pragmas, GCC warns that there might not be /* Without these pragmas, GCC warns that there might not be
enough space to use the placement new operator. However, the enough space to use the placement new operator. However, the
'if' statement above ensures that this is the case. */ 'if' statement above ensures that this is the case. */
#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 #if defined(__GNUG__) && __GNUC__ >= 6 && !defined(__clang__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic push # pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wplacement-new" # pragma GCC diagnostic ignored "-Wplacement-new"
#endif #endif
new ((capture *) &rec->data) capture { std::forward<Func>(f) }; new ((capture *) &rec->data) capture { std::forward<Func>(f) };
#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 #if defined(__GNUG__) && __GNUC__ >= 6 && !defined(__clang__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic pop # pragma GCC diagnostic pop
#endif #endif
#if defined(__GNUG__) && !PYBIND11_HAS_STD_LAUNDER && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif
// UB without std::launder, but without breaking ABI and/or
// a significant refactoring it's "impossible" to solve.
if (!std::is_trivially_destructible<Func>::value) if (!std::is_trivially_destructible<Func>::value)
rec->free_data = [](function_record *r) { ((capture *) &r->data)->~capture(); }; rec->free_data = [](function_record *r) {
auto data = PYBIND11_STD_LAUNDER((capture *) &r->data);
(void) data;
data->~capture();
};
#if defined(__GNUG__) && !PYBIND11_HAS_STD_LAUNDER && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic pop
#endif
} else { } else {
rec->data[0] = new capture { std::forward<Func>(f) }; rec->data[0] = new capture { std::forward<Func>(f) };
rec->free_data = [](function_record *r) { delete ((capture *) r->data[0]); }; rec->free_data = [](function_record *r) { delete ((capture *) r->data[0]); };
@ -213,7 +232,7 @@ protected:
} }
/* Generate a readable signature describing the function's arguments and return value types */ /* Generate a readable signature describing the function's arguments and return value types */
static constexpr auto signature = _x("(") + cast_in::arg_names + _x(") -> ") + cast_out::name; static constexpr auto signature = _("(") + cast_in::arg_names + _(") -> ") + cast_out::name;
PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types(); PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types();
/* Register the function with Python from generic (non-templated) code */ /* Register the function with Python from generic (non-templated) code */
@ -243,7 +262,7 @@ protected:
std::free(s); std::free(s);
} }
char *operator()(const char *s) { char *operator()(const char *s) {
auto t = strdup(s); auto t = PYBIND11_COMPAT_STRDUP(s);
strings.push_back(t); strings.push_back(t);
return t; return t;
} }
@ -281,7 +300,8 @@ protected:
a.descr = guarded_strdup(repr(a.value).cast<std::string>().c_str()); a.descr = guarded_strdup(repr(a.value).cast<std::string>().c_str());
} }
rec->is_constructor = !strcmp(rec->name, "__init__") || !strcmp(rec->name, "__setstate__"); rec->is_constructor
= (strcmp(rec->name, "__init__") == 0) || (strcmp(rec->name, "__setstate__") == 0);
#if !defined(NDEBUG) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING) #if !defined(NDEBUG) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING)
if (rec->is_constructor && !rec->is_new_style_constructor) { if (rec->is_constructor && !rec->is_new_style_constructor) {
@ -395,7 +415,8 @@ protected:
rec->def = new PyMethodDef(); rec->def = new PyMethodDef();
std::memset(rec->def, 0, sizeof(PyMethodDef)); std::memset(rec->def, 0, sizeof(PyMethodDef));
rec->def->ml_name = rec->name; rec->def->ml_name = rec->name;
rec->def->ml_meth = reinterpret_cast<PyCFunction>(reinterpret_cast<void (*) (void)>(*dispatcher)); rec->def->ml_meth
= reinterpret_cast<PyCFunction>(reinterpret_cast<void (*)()>(dispatcher));
rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS; rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS;
capsule rec_capsule(unique_rec.release(), [](void *ptr) { capsule rec_capsule(unique_rec.release(), [](void *ptr) {
@ -469,7 +490,7 @@ protected:
signatures += it->signature; signatures += it->signature;
signatures += "\n"; signatures += "\n";
} }
if (it->doc && strlen(it->doc) > 0 && options::show_user_defined_docstrings()) { if (it->doc && it->doc[0] != '\0' && options::show_user_defined_docstrings()) {
// If we're appending another docstring, and aren't printing function signatures, we // If we're appending another docstring, and aren't printing function signatures, we
// need to append a newline first: // need to append a newline first:
if (!options::show_function_signatures()) { if (!options::show_function_signatures()) {
@ -486,7 +507,8 @@ protected:
auto *func = (PyCFunctionObject *) m_ptr; auto *func = (PyCFunctionObject *) m_ptr;
std::free(const_cast<char *>(func->m_ml->ml_doc)); std::free(const_cast<char *>(func->m_ml->ml_doc));
// Install docstring if it's non-empty (when at least one option is enabled) // Install docstring if it's non-empty (when at least one option is enabled)
func->m_ml->ml_doc = signatures.empty() ? nullptr : strdup(signatures.c_str()); func->m_ml->ml_doc
= signatures.empty() ? nullptr : PYBIND11_COMPAT_STRDUP(signatures.c_str());
if (rec->is_method) { if (rec->is_method) {
m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr()); m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr());
@ -555,8 +577,8 @@ protected:
auto self_value_and_holder = value_and_holder(); auto self_value_and_holder = value_and_holder();
if (overloads->is_constructor) { if (overloads->is_constructor) {
if (!PyObject_TypeCheck(parent.ptr(), (PyTypeObject *) overloads->scope.ptr())) { if (!parent || !PyObject_TypeCheck(parent.ptr(), (PyTypeObject *) overloads->scope.ptr())) {
PyErr_SetString(PyExc_TypeError, "__init__(self, ...) called with invalid `self` argument"); PyErr_SetString(PyExc_TypeError, "__init__(self, ...) called with invalid or missing `self` argument");
return nullptr; return nullptr;
} }
@ -635,7 +657,7 @@ protected:
bool bad_arg = false; bool bad_arg = false;
for (; args_copied < args_to_copy; ++args_copied) { for (; args_copied < args_to_copy; ++args_copied) {
const 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)) { if (kwargs_in && arg_rec && arg_rec->name && dict_getitemstring(kwargs_in, arg_rec->name)) {
bad_arg = true; bad_arg = true;
break; break;
} }
@ -683,7 +705,7 @@ protected:
handle value; handle value;
if (kwargs_in && arg_rec.name) if (kwargs_in && arg_rec.name)
value = PyDict_GetItemString(kwargs.ptr(), arg_rec.name); value = dict_getitemstring(kwargs.ptr(), arg_rec.name);
if (value) { if (value) {
// Consume a kwargs value // Consume a kwargs value
@ -691,7 +713,9 @@ protected:
kwargs = reinterpret_steal<dict>(PyDict_Copy(kwargs.ptr())); kwargs = reinterpret_steal<dict>(PyDict_Copy(kwargs.ptr()));
copied_kwargs = true; copied_kwargs = true;
} }
PyDict_DelItemString(kwargs.ptr(), arg_rec.name); if (PyDict_DelItemString(kwargs.ptr(), arg_rec.name) == -1) {
throw error_already_set();
}
} else if (arg_rec.value) { } else if (arg_rec.value) {
value = arg_rec.value; value = arg_rec.value;
} }
@ -921,21 +945,21 @@ protected:
append_note_if_missing_header_is_suspected(msg); append_note_if_missing_header_is_suspected(msg);
PyErr_SetString(PyExc_TypeError, msg.c_str()); PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr; return nullptr;
} else if (!result) { }
if (!result) {
std::string msg = "Unable to convert function return value to a " std::string msg = "Unable to convert function return value to a "
"Python type! The signature was\n\t"; "Python type! The signature was\n\t";
msg += it->signature; msg += it->signature;
append_note_if_missing_header_is_suspected(msg); append_note_if_missing_header_is_suspected(msg);
PyErr_SetString(PyExc_TypeError, msg.c_str()); PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr; return nullptr;
} else { }
if (overloads->is_constructor && !self_value_and_holder.holder_constructed()) { if (overloads->is_constructor && !self_value_and_holder.holder_constructed()) {
auto *pi = reinterpret_cast<instance *>(parent.ptr()); auto *pi = reinterpret_cast<instance *>(parent.ptr());
self_value_and_holder.type->init_instance(pi, nullptr); self_value_and_holder.type->init_instance(pi, nullptr);
} }
return result.ptr(); return result.ptr();
} }
}
}; };
/// Wrapper for Python extension modules /// Wrapper for Python extension modules
@ -1086,7 +1110,8 @@ protected:
pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name) + pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name) +
"\": an object with that name is already defined"); "\": an object with that name is already defined");
if (rec.module_local ? get_local_type_info(*rec.type) : get_global_type_info(*rec.type)) if ((rec.module_local ? get_local_type_info(*rec.type) : get_global_type_info(*rec.type))
!= nullptr)
pybind11_fail("generic_type: type \"" + std::string(rec.name) + pybind11_fail("generic_type: type \"" + std::string(rec.name) +
"\" is already registered!"); "\" is already registered!");
@ -1164,8 +1189,9 @@ protected:
void def_property_static_impl(const char *name, void def_property_static_impl(const char *name,
handle fget, handle fset, handle fget, handle fset,
detail::function_record *rec_func) { detail::function_record *rec_func) {
const auto is_static = rec_func && !(rec_func->is_method && rec_func->scope); const auto is_static = (rec_func != nullptr) && !(rec_func->is_method && rec_func->scope);
const auto has_doc = rec_func && rec_func->doc && pybind11::options::show_user_defined_docstrings(); const auto has_doc = (rec_func != nullptr) && (rec_func->doc != nullptr)
&& pybind11::options::show_user_defined_docstrings();
auto property = handle((PyObject *) (is_static ? get_internals().static_property_type auto property = handle((PyObject *) (is_static ? get_internals().static_property_type
: &PyProperty_Type)); : &PyProperty_Type));
attr(name) = property(fget.ptr() ? fget : none(), attr(name) = property(fget.ptr() ? fget : none(),
@ -1412,15 +1438,15 @@ public:
template <typename D, typename... Extra> template <typename D, typename... Extra>
class_ &def_readwrite_static(const char *name, D *pm, const Extra& ...extra) { class_ &def_readwrite_static(const char *name, D *pm, const Extra& ...extra) {
cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)), cpp_function fget([pm](const object &) -> const D & { return *pm; }, scope(*this)),
fset([pm](object, const D &value) { *pm = value; }, scope(*this)); fset([pm](const object &, const D &value) { *pm = value; }, scope(*this));
def_property_static(name, fget, fset, return_value_policy::reference, extra...); def_property_static(name, fget, fset, return_value_policy::reference, extra...);
return *this; return *this;
} }
template <typename D, typename... Extra> template <typename D, typename... Extra>
class_ &def_readonly_static(const char *name, const D *pm, const Extra& ...extra) { class_ &def_readonly_static(const char *name, const D *pm, const Extra& ...extra) {
cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)); cpp_function fget([pm](const object &) -> const D & { return *pm; }, scope(*this));
def_property_readonly_static(name, fget, return_value_policy::reference, extra...); def_property_readonly_static(name, fget, return_value_policy::reference, extra...);
return *this; return *this;
} }
@ -1485,7 +1511,7 @@ public:
detail::process_attributes<Extra...>::init(extra..., rec_fget); detail::process_attributes<Extra...>::init(extra..., rec_fget);
if (rec_fget->doc && rec_fget->doc != doc_prev) { if (rec_fget->doc && rec_fget->doc != doc_prev) {
free(doc_prev); free(doc_prev);
rec_fget->doc = strdup(rec_fget->doc); rec_fget->doc = PYBIND11_COMPAT_STRDUP(rec_fget->doc);
} }
} }
if (rec_fset) { if (rec_fset) {
@ -1493,7 +1519,7 @@ public:
detail::process_attributes<Extra...>::init(extra..., rec_fset); detail::process_attributes<Extra...>::init(extra..., rec_fset);
if (rec_fset->doc && rec_fset->doc != doc_prev) { if (rec_fset->doc && rec_fset->doc != doc_prev) {
free(doc_prev); free(doc_prev);
rec_fset->doc = strdup(rec_fset->doc); rec_fset->doc = PYBIND11_COMPAT_STRDUP(rec_fset->doc);
} }
if (! rec_active) rec_active = rec_fset; if (! rec_active) rec_active = rec_fset;
} }
@ -1628,12 +1654,13 @@ struct enum_base {
auto static_property = handle((PyObject *) get_internals().static_property_type); auto static_property = handle((PyObject *) get_internals().static_property_type);
m_base.attr("__repr__") = cpp_function( m_base.attr("__repr__") = cpp_function(
[](object arg) -> str { [](const object &arg) -> str {
handle type = type::handle_of(arg); handle type = type::handle_of(arg);
object type_name = type.attr("__name__"); object type_name = type.attr("__name__");
return pybind11::str("<{}.{}: {}>").format(type_name, enum_name(arg), int_(arg)); return pybind11::str("<{}.{}: {}>").format(type_name, enum_name(arg), int_(arg));
}, name("__repr__"), is_method(m_base) },
); name("__repr__"),
is_method(m_base));
m_base.attr("name") = property(cpp_function(&enum_name, name("name"), is_method(m_base))); m_base.attr("name") = property(cpp_function(&enum_name, name("name"), is_method(m_base)));
@ -1671,30 +1698,36 @@ struct enum_base {
}, name("__members__")), none(), none(), "" }, name("__members__")), none(), none(), ""
); );
#define PYBIND11_ENUM_OP_STRICT(op, expr, strict_behavior) \ #define PYBIND11_ENUM_OP_STRICT(op, expr, strict_behavior) \
m_base.attr(op) = cpp_function( \ m_base.attr(op) = cpp_function( \
[](object a, object b) { \ [](const object &a, const object &b) { \
if (!type::handle_of(a).is(type::handle_of(b))) \ if (!type::handle_of(a).is(type::handle_of(b))) \
strict_behavior; \ strict_behavior; \
return expr; \ return expr; \
}, \ }, \
name(op), is_method(m_base), arg("other")) name(op), \
is_method(m_base), \
arg("other"))
#define PYBIND11_ENUM_OP_CONV(op, expr) \ #define PYBIND11_ENUM_OP_CONV(op, expr) \
m_base.attr(op) = cpp_function( \ m_base.attr(op) = cpp_function( \
[](object a_, object b_) { \ [](const object &a_, const object &b_) { \
int_ a(a_), b(b_); \ int_ a(a_), b(b_); \
return expr; \ return expr; \
}, \ }, \
name(op), is_method(m_base), arg("other")) name(op), \
is_method(m_base), \
arg("other"))
#define PYBIND11_ENUM_OP_CONV_LHS(op, expr) \ #define PYBIND11_ENUM_OP_CONV_LHS(op, expr) \
m_base.attr(op) = cpp_function( \ m_base.attr(op) = cpp_function( \
[](object a_, object b) { \ [](const object &a_, const object &b) { \
int_ a(a_); \ int_ a(a_); \
return expr; \ return expr; \
}, \ }, \
name(op), is_method(m_base), arg("other")) name(op), \
is_method(m_base), \
arg("other"))
if (is_convertible) { if (is_convertible) {
PYBIND11_ENUM_OP_CONV_LHS("__eq__", !b.is_none() && a.equal(b)); PYBIND11_ENUM_OP_CONV_LHS("__eq__", !b.is_none() && a.equal(b));
@ -1711,8 +1744,10 @@ struct enum_base {
PYBIND11_ENUM_OP_CONV("__ror__", a | b); PYBIND11_ENUM_OP_CONV("__ror__", a | b);
PYBIND11_ENUM_OP_CONV("__xor__", a ^ b); PYBIND11_ENUM_OP_CONV("__xor__", a ^ b);
PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b); PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b);
m_base.attr("__invert__") = cpp_function( m_base.attr("__invert__")
[](object arg) { return ~(int_(arg)); }, name("__invert__"), is_method(m_base)); = cpp_function([](const object &arg) { return ~(int_(arg)); },
name("__invert__"),
is_method(m_base));
} }
} else { } else {
PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false); PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false);
@ -1733,10 +1768,10 @@ struct enum_base {
#undef PYBIND11_ENUM_OP_STRICT #undef PYBIND11_ENUM_OP_STRICT
m_base.attr("__getstate__") = cpp_function( m_base.attr("__getstate__") = cpp_function(
[](object arg) { return int_(arg); }, name("__getstate__"), is_method(m_base)); [](const object &arg) { return int_(arg); }, name("__getstate__"), is_method(m_base));
m_base.attr("__hash__") = cpp_function( m_base.attr("__hash__") = cpp_function(
[](object arg) { return int_(arg); }, name("__hash__"), is_method(m_base)); [](const object &arg) { return int_(arg); }, name("__hash__"), is_method(m_base));
} }
PYBIND11_NOINLINE void value(char const* name_, object value, const char *doc = nullptr) { PYBIND11_NOINLINE void value(char const* name_, object value, const char *doc = nullptr) {
@ -1848,9 +1883,9 @@ PYBIND11_NOINLINE inline void keep_alive_impl(size_t Nurse, size_t Patient, func
auto get_arg = [&](size_t n) { auto get_arg = [&](size_t n) {
if (n == 0) if (n == 0)
return ret; return ret;
else if (n == 1 && call.init_self) if (n == 1 && call.init_self)
return call.init_self; return call.init_self;
else if (n <= call.args.size()) if (n <= call.args.size())
return call.args[n - 1]; return call.args[n - 1];
return handle(); return handle();
}; };
@ -2054,7 +2089,7 @@ exception<CppException> &register_exception(handle scope,
} }
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) { PYBIND11_NOINLINE inline void print(const tuple &args, const dict &kwargs) {
auto strings = tuple(args.size()); auto strings = tuple(args.size());
for (size_t i = 0; i < args.size(); ++i) { for (size_t i = 0; i < args.size(); ++i) {
strings[i] = str(args[i]); strings[i] = str(args[i]);
@ -2126,10 +2161,10 @@ inline function get_type_override(const void *this_ptr, const type_info *this_ty
Unfortunately this doesn't work on PyPy. */ Unfortunately this doesn't work on PyPy. */
#if !defined(PYPY_VERSION) #if !defined(PYPY_VERSION)
PyFrameObject *frame = PyThreadState_Get()->frame; PyFrameObject *frame = PyThreadState_Get()->frame;
if (frame && (std::string) str(frame->f_code->co_name) == name && if (frame != nullptr && (std::string) str(frame->f_code->co_name) == name
frame->f_code->co_argcount > 0) { && frame->f_code->co_argcount > 0) {
PyFrame_FastToLocals(frame); PyFrame_FastToLocals(frame);
PyObject *self_caller = PyDict_GetItem( PyObject *self_caller = dict_getitem(
frame->f_locals, PyTuple_GET_ITEM(frame->f_code->co_varnames, 0)); frame->f_locals, PyTuple_GET_ITEM(frame->f_code->co_varnames, 0));
if (self_caller == self.ptr()) if (self_caller == self.ptr())
return function(); return function();
@ -2177,14 +2212,15 @@ template <class T> function get_override(const T *this_ptr, const char *name) {
#define PYBIND11_OVERRIDE_IMPL(ret_type, cname, name, ...) \ #define PYBIND11_OVERRIDE_IMPL(ret_type, cname, name, ...) \
do { \ do { \
pybind11::gil_scoped_acquire gil; \ pybind11::gil_scoped_acquire gil; \
pybind11::function override = pybind11::get_override(static_cast<const cname *>(this), name); \ pybind11::function override \
= pybind11::get_override(static_cast<const cname *>(this), name); \
if (override) { \ if (override) { \
auto o = override(__VA_ARGS__); \ auto o = override(__VA_ARGS__); \
if (pybind11::detail::cast_is_temporary_value_reference<ret_type>::value) { \ if (pybind11::detail::cast_is_temporary_value_reference<ret_type>::value) { \
static pybind11::detail::override_caster_t<ret_type> caster; \ static pybind11::detail::override_caster_t<ret_type> caster; \
return pybind11::detail::cast_ref<ret_type>(std::move(o), caster); \ return pybind11::detail::cast_ref<ret_type>(std::move(o), caster); \
} \ } \
else return pybind11::detail::cast_safe<ret_type>(std::move(o)); \ return pybind11::detail::cast_safe<ret_type>(std::move(o)); \
} \ } \
} while (false) } while (false)
@ -2281,8 +2317,12 @@ inline function get_overload(const T *this_ptr, const char *name) {
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(__GNUC__) && __GNUC__ == 7
# pragma GCC diagnostic pop // -Wnoexcept-type
#endif
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
# pragma warning(pop) # pragma warning(pop)
#elif defined(__GNUG__) && !defined(__clang__) #elif defined(__GNUG__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic pop # pragma GCC diagnostic pop
#endif #endif

View File

@ -319,11 +319,15 @@ PYBIND11_NAMESPACE_BEGIN(detail)
inline std::string error_string(); inline std::string error_string();
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4275 4251) // warning C4275: An exported class was derived from a class that wasn't exported. Can be ignored when derived from a STL class.
#endif
/// Fetch and hold an error which was already set in Python. An instance of this is typically /// Fetch and hold an error which was already set in Python. An instance of this is typically
/// thrown to propagate python-side errors back through C++ which can either be caught manually or /// thrown to propagate python-side errors back through C++ which can either be caught manually or
/// else falls back to the function dispatcher (which then raises the captured error back to /// else falls back to the function dispatcher (which then raises the captured error back to
/// python). /// python).
class error_already_set : public std::runtime_error { class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::runtime_error {
public: public:
/// Constructs a new exception from the current Python error indicator, if any. The current /// Constructs a new exception from the current Python error indicator, if any. The current
/// Python error indicator will be cleared. /// Python error indicator will be cleared.
@ -341,16 +345,17 @@ public:
/// error variables (but the `.what()` string is still available). /// error variables (but the `.what()` string is still available).
void restore() { PyErr_Restore(m_type.release().ptr(), m_value.release().ptr(), m_trace.release().ptr()); } void restore() { PyErr_Restore(m_type.release().ptr(), m_value.release().ptr(), m_trace.release().ptr()); }
/// If it is impossible to raise the currently-held error, such as in destructor, we can write /// If it is impossible to raise the currently-held error, such as in a destructor, we can write
/// it out using Python's unraisable hook (sys.unraisablehook). The error context should be /// it out using Python's unraisable hook (`sys.unraisablehook`). The error context should be
/// some object whose repr() helps identify the location of the error. Python already knows the /// some object whose `repr()` helps identify the location of the error. Python already knows the
/// type and value of the error, so there is no need to repeat that. For example, __func__ could /// type and value of the error, so there is no need to repeat that. After this call, the current
/// be helpful. After this call, the current object no longer stores the error variables, /// object no longer stores the error variables, and neither does Python.
/// and neither does Python.
void discard_as_unraisable(object err_context) { void discard_as_unraisable(object err_context) {
restore(); restore();
PyErr_WriteUnraisable(err_context.ptr()); PyErr_WriteUnraisable(err_context.ptr());
} }
/// An alternate version of `discard_as_unraisable()`, where a string provides information on the
/// location of the error. For example, `__func__` could be helpful.
void discard_as_unraisable(const char *err_context) { void discard_as_unraisable(const char *err_context) {
discard_as_unraisable(reinterpret_steal<object>(PYBIND11_FROM_STRING(err_context))); discard_as_unraisable(reinterpret_steal<object>(PYBIND11_FROM_STRING(err_context)));
} }
@ -362,7 +367,9 @@ public:
/// Check if the currently trapped error type matches the given Python exception class (or a /// 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 /// subclass thereof). May also be passed a tuple to search for any exception class matches in
/// the given tuple. /// the given tuple.
bool matches(handle exc) const { return PyErr_GivenExceptionMatches(m_type.ptr(), exc.ptr()); } bool matches(handle exc) const {
return (PyErr_GivenExceptionMatches(m_type.ptr(), exc.ptr()) != 0);
}
const object& type() const { return m_type; } const object& type() const { return m_type; }
const object& value() const { return m_value; } const object& value() const { return m_value; }
@ -371,6 +378,9 @@ public:
private: private:
object m_type, m_value, m_trace; object m_type, m_value, m_trace;
}; };
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
/** \defgroup python_builtins _ /** \defgroup python_builtins _
Unless stated otherwise, the following C++ functions behave the same Unless stated otherwise, the following C++ functions behave the same
@ -433,19 +443,17 @@ inline object getattr(handle obj, const char *name) {
inline object getattr(handle obj, handle name, handle default_) { inline object getattr(handle obj, handle name, handle default_) {
if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) { if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) {
return reinterpret_steal<object>(result); return reinterpret_steal<object>(result);
} else { }
PyErr_Clear(); PyErr_Clear();
return reinterpret_borrow<object>(default_); return reinterpret_borrow<object>(default_);
}
} }
inline object getattr(handle obj, const char *name, handle default_) { inline object getattr(handle obj, const char *name, handle default_) {
if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) { if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) {
return reinterpret_steal<object>(result); return reinterpret_steal<object>(result);
} else { }
PyErr_Clear(); PyErr_Clear();
return reinterpret_borrow<object>(default_); return reinterpret_borrow<object>(default_);
}
} }
inline void setattr(handle obj, handle name, handle value) { inline void setattr(handle obj, handle name, handle value) {
@ -478,6 +486,43 @@ inline handle get_function(handle value) {
return value; return value;
} }
// Reimplementation of python's dict helper functions to ensure that exceptions
// aren't swallowed (see #2862)
// copied from cpython _PyDict_GetItemStringWithError
inline PyObject * dict_getitemstring(PyObject *v, const char *key)
{
#if PY_MAJOR_VERSION >= 3
PyObject *kv = nullptr, *rv = nullptr;
kv = PyUnicode_FromString(key);
if (kv == NULL) {
throw error_already_set();
}
rv = PyDict_GetItemWithError(v, kv);
Py_DECREF(kv);
if (rv == NULL && PyErr_Occurred()) {
throw error_already_set();
}
return rv;
#else
return PyDict_GetItemString(v, key);
#endif
}
inline PyObject * dict_getitem(PyObject *v, PyObject *key)
{
#if PY_MAJOR_VERSION >= 3
PyObject *rv = PyDict_GetItemWithError(v, key);
if (rv == NULL && PyErr_Occurred()) {
throw error_already_set();
}
return rv;
#else
return PyDict_GetItem(v, key);
#endif
}
// Helper aliases/functions to support implicit casting of values given to python accessors/methods. // Helper aliases/functions to support implicit casting of values given to python accessors/methods.
// When given a pyobject, this simply returns the pyobject as-is; for other C++ type, the value goes // When given a pyobject, this simply returns the pyobject as-is; for other C++ type, the value goes
// through pybind11::cast(obj) to convert it to an `object`. // through pybind11::cast(obj) to convert it to an `object`.
@ -489,6 +534,10 @@ object object_or_cast(T &&o);
// Match a PyObject*, which we want to convert directly to handle via its converting constructor // Match a PyObject*, which we want to convert directly to handle via its converting constructor
inline handle object_or_cast(PyObject *ptr) { return ptr; } inline handle object_or_cast(PyObject *ptr) { return ptr; }
#if defined(_MSC_VER) && _MSC_VER < 1920
# pragma warning(push)
# pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified
#endif
template <typename Policy> template <typename Policy>
class accessor : public object_api<accessor<Policy>> { class accessor : public object_api<accessor<Policy>> {
using key_type = typename Policy::key_type; using key_type = typename Policy::key_type;
@ -496,7 +545,7 @@ class accessor : public object_api<accessor<Policy>> {
public: public:
accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) { } accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) { }
accessor(const accessor &) = default; accessor(const accessor &) = default;
accessor(accessor &&) = default; accessor(accessor &&) noexcept = default;
// accessor overload required to override default assignment operator (templates are not allowed // accessor overload required to override default assignment operator (templates are not allowed
// to replace default compiler-generated assignments). // to replace default compiler-generated assignments).
@ -537,6 +586,9 @@ private:
key_type key; key_type key;
mutable object cache; mutable object cache;
}; };
#if defined(_MSC_VER) && _MSC_VER < 1920
# pragma warning(pop)
#endif
PYBIND11_NAMESPACE_BEGIN(accessor_policies) PYBIND11_NAMESPACE_BEGIN(accessor_policies)
struct obj_attr { struct obj_attr {
@ -721,7 +773,11 @@ protected:
dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); } dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); }
reference dereference() const { return {key, value}; } reference dereference() const { return {key, value}; }
void increment() { if (!PyDict_Next(obj.ptr(), &pos, &key, &value)) { pos = -1; } } void increment() {
if (PyDict_Next(obj.ptr(), &pos, &key, &value) == 0) {
pos = -1;
}
}
bool equal(const dict_readonly &b) const { return pos == b.pos; } bool equal(const dict_readonly &b) const { return pos == b.pos; }
private: private:
@ -747,10 +803,9 @@ inline bool PyIterable_Check(PyObject *obj) {
if (iter) { if (iter) {
Py_DECREF(iter); Py_DECREF(iter);
return true; return true;
} else { }
PyErr_Clear(); PyErr_Clear();
return false; return false;
}
} }
inline bool PyNone_Check(PyObject *o) { return o == Py_None; } inline bool PyNone_Check(PyObject *o) { return o == Py_None; }
@ -804,7 +859,7 @@ PYBIND11_NAMESPACE_END(detail)
Name(handle h, borrowed_t) : Parent(h, borrowed_t{}) { } \ Name(handle h, borrowed_t) : Parent(h, borrowed_t{}) { } \
Name(handle h, stolen_t) : Parent(h, stolen_t{}) { } \ Name(handle h, stolen_t) : Parent(h, stolen_t{}) { } \
PYBIND11_DEPRECATED("Use py::isinstance<py::python_type>(obj) instead") \ PYBIND11_DEPRECATED("Use py::isinstance<py::python_type>(obj) instead") \
bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \ bool check() const { return m_ptr != nullptr && (CheckFun(m_ptr) != 0); } \
static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } \ static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } \
template <typename Policy_> \ template <typename Policy_> \
Name(const ::pybind11::detail::accessor<Policy_> &a) : Name(object(a)) { } Name(const ::pybind11::detail::accessor<Policy_> &a) : Name(object(a)) { }
@ -973,10 +1028,10 @@ public:
if (PyUnicode_Check(m_ptr)) { if (PyUnicode_Check(m_ptr)) {
temp = reinterpret_steal<object>(PyUnicode_AsUTF8String(m_ptr)); temp = reinterpret_steal<object>(PyUnicode_AsUTF8String(m_ptr));
if (!temp) if (!temp)
pybind11_fail("Unable to extract string contents! (encoding issue)"); throw error_already_set();
} }
char *buffer; char *buffer = nullptr;
ssize_t length; ssize_t length = 0;
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length))
pybind11_fail("Unable to extract string contents! (invalid type)"); pybind11_fail("Unable to extract string contents! (invalid type)");
return std::string(buffer, (size_t) length); return std::string(buffer, (size_t) length);
@ -1031,8 +1086,8 @@ public:
explicit bytes(const pybind11::str &s); explicit bytes(const pybind11::str &s);
operator std::string() const { operator std::string() const {
char *buffer; char *buffer = nullptr;
ssize_t length; ssize_t length = 0;
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length)) if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length))
pybind11_fail("Unable to extract bytes contents!"); pybind11_fail("Unable to extract bytes contents!");
return std::string(buffer, (size_t) length); return std::string(buffer, (size_t) length);
@ -1049,8 +1104,8 @@ inline bytes::bytes(const pybind11::str &s) {
if (!temp) if (!temp)
pybind11_fail("Unable to extract string contents! (encoding issue)"); pybind11_fail("Unable to extract string contents! (encoding issue)");
} }
char *buffer; char *buffer = nullptr;
ssize_t length; ssize_t length = 0;
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length))
pybind11_fail("Unable to extract string contents! (invalid type)"); pybind11_fail("Unable to extract string contents! (invalid type)");
auto obj = reinterpret_steal<object>(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length)); auto obj = reinterpret_steal<object>(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length));
@ -1060,8 +1115,8 @@ inline bytes::bytes(const pybind11::str &s) {
} }
inline str::str(const bytes& b) { inline str::str(const bytes& b) {
char *buffer; char *buffer = nullptr;
ssize_t length; ssize_t length = 0;
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length)) if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length))
pybind11_fail("Unable to extract bytes contents!"); pybind11_fail("Unable to extract bytes contents!");
auto obj = reinterpret_steal<object>(PyUnicode_FromStringAndSize(buffer, (ssize_t) length)); auto obj = reinterpret_steal<object>(PyUnicode_FromStringAndSize(buffer, (ssize_t) length));
@ -1118,14 +1173,14 @@ public:
bool_() : object(Py_False, borrowed_t{}) { } bool_() : object(Py_False, borrowed_t{}) { }
// Allow implicit conversion from and to `bool`: // Allow implicit conversion from and to `bool`:
bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) { } bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) { }
operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; } operator bool() const { return (m_ptr != nullptr) && PyLong_AsLong(m_ptr) != 0; }
private: private:
/// Return the truth value of an object -- always returns a new reference /// Return the truth value of an object -- always returns a new reference
static PyObject *raw_bool(PyObject *op) { static PyObject *raw_bool(PyObject *op) {
const auto value = PyObject_IsTrue(op); const auto value = PyObject_IsTrue(op);
if (value == -1) return nullptr; if (value == -1) return nullptr;
return handle(value ? Py_True : Py_False).inc_ref().ptr(); return handle(value != 0 ? Py_True : Py_False).inc_ref().ptr();
} }
}; };
@ -1144,10 +1199,8 @@ Unsigned as_unsigned(PyObject *o) {
unsigned long v = PyLong_AsUnsignedLong(o); unsigned long v = PyLong_AsUnsignedLong(o);
return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v;
} }
else {
unsigned long long v = PyLong_AsUnsignedLongLong(o); unsigned long long v = PyLong_AsUnsignedLongLong(o);
return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v;
}
} }
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
@ -1501,7 +1554,7 @@ public:
detail::any_container<ssize_t> shape, detail::any_container<ssize_t> shape,
detail::any_container<ssize_t> strides) { detail::any_container<ssize_t> strides) {
return memoryview::from_buffer( return memoryview::from_buffer(
const_cast<void*>(ptr), itemsize, format, shape, strides, true); const_cast<void *>(ptr), itemsize, format, std::move(shape), std::move(strides), true);
} }
template<typename T> template<typename T>
@ -1558,7 +1611,7 @@ inline memoryview memoryview::from_buffer(
size_t ndim = shape->size(); size_t ndim = shape->size();
if (ndim != strides->size()) if (ndim != strides->size())
pybind11_fail("memoryview: shape length doesn't match strides length"); pybind11_fail("memoryview: shape length doesn't match strides length");
ssize_t size = ndim ? 1 : 0; ssize_t size = ndim != 0u ? 1 : 0;
for (size_t i = 0; i < ndim; ++i) for (size_t i = 0; i < ndim; ++i)
size *= (*shape)[i]; size *= (*shape)[i];
Py_buffer view; Py_buffer view;

View File

@ -159,10 +159,13 @@ template <typename Type, typename Value> struct list_caster {
} }
private: private:
template <typename T = Type, template <
typename T = Type,
enable_if_t<std::is_same<decltype(std::declval<T>().reserve(0)), void>::value, int> = 0> enable_if_t<std::is_same<decltype(std::declval<T>().reserve(0)), void>::value, int> = 0>
void reserve_maybe(sequence s, Type *) { value.reserve(s.size()); } void reserve_maybe(const sequence &s, Type *) {
void reserve_maybe(sequence, void *) { } value.reserve(s.size());
}
void reserve_maybe(const sequence &, void *) {}
public: public:
template <typename T> template <typename T>
@ -275,7 +278,8 @@ template<typename T> struct optional_caster {
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
if (!src) { if (!src) {
return false; return false;
} else if (src.is_none()) { }
if (src.is_none()) {
return true; // default-constructed value is already empty return true; // default-constructed value is already empty
} }
value_conv inner_caster; value_conv inner_caster;
@ -377,7 +381,11 @@ struct type_caster<std::variant<Ts...>> : variant_caster<std::variant<Ts...>> {
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
inline std::ostream &operator<<(std::ostream &os, const handle &obj) { inline std::ostream &operator<<(std::ostream &os, const handle &obj) {
#ifdef PYBIND11_HAS_STRING_VIEW
os << str(obj).cast<std::string_view>();
#else
os << (std::string) str(obj); os << (std::string) str(obj);
#endif
return os; return os;
} }

View File

@ -0,0 +1,103 @@
// Copyright (c) 2021 The Pybind Development Team.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#pragma once
#include "../cast.h"
#include "../pybind11.h"
#include "../pytypes.h"
#include "../detail/common.h"
#include "../detail/descr.h"
#include <string>
#ifdef __has_include
# if defined(PYBIND11_CPP17) && __has_include(<filesystem>) && \
PY_VERSION_HEX >= 0x03060000
# include <filesystem>
# define PYBIND11_HAS_FILESYSTEM 1
# endif
#endif
#if !defined(PYBIND11_HAS_FILESYSTEM) && !defined(PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL)
# error \
"#include <filesystem> is not available. (Use -DPYBIND11_HAS_FILESYSTEM_IS_OPTIONAL to ignore.)"
#endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)
#if defined(PYBIND11_HAS_FILESYSTEM)
template<typename T> struct path_caster {
private:
static PyObject* unicode_from_fs_native(const std::string& w) {
#if !defined(PYPY_VERSION)
return PyUnicode_DecodeFSDefaultAndSize(w.c_str(), ssize_t(w.size()));
#else
// PyPy mistakenly declares the first parameter as non-const.
return PyUnicode_DecodeFSDefaultAndSize(
const_cast<char*>(w.c_str()), ssize_t(w.size()));
#endif
}
static PyObject* unicode_from_fs_native(const std::wstring& w) {
return PyUnicode_FromWideChar(w.c_str(), ssize_t(w.size()));
}
public:
static handle cast(const T& path, return_value_policy, handle) {
if (auto py_str = unicode_from_fs_native(path.native())) {
return module_::import("pathlib").attr("Path")(reinterpret_steal<object>(py_str))
.release();
}
return nullptr;
}
bool load(handle handle, bool) {
// PyUnicode_FSConverter and PyUnicode_FSDecoder normally take care of
// calling PyOS_FSPath themselves, but that's broken on PyPy (PyPy
// issue #3168) so we do it ourselves instead.
PyObject* buf = PyOS_FSPath(handle.ptr());
if (!buf) {
PyErr_Clear();
return false;
}
PyObject* native = nullptr;
if constexpr (std::is_same_v<typename T::value_type, char>) {
if (PyUnicode_FSConverter(buf, &native) != 0) {
if (auto c_str = PyBytes_AsString(native)) {
// AsString returns a pointer to the internal buffer, which
// must not be free'd.
value = c_str;
}
}
} else if constexpr (std::is_same_v<typename T::value_type, wchar_t>) {
if (PyUnicode_FSDecoder(buf, &native) != 0) {
if (auto c_str = PyUnicode_AsWideCharString(native, nullptr)) {
// AsWideCharString returns a new string that must be free'd.
value = c_str; // Copies the string.
PyMem_Free(c_str);
}
}
}
Py_XDECREF(native);
Py_DECREF(buf);
if (PyErr_Occurred()) {
PyErr_Clear();
return false;
}
return true;
}
PYBIND11_TYPE_CASTER(T, _("os.PathLike"));
};
template<> struct type_caster<std::filesystem::path>
: public path_caster<std::filesystem::path> {};
#endif // PYBIND11_HAS_FILESYSTEM
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@ -128,7 +128,7 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
arg("x"), arg("x"),
"Add an item to the end of the list"); "Add an item to the end of the list");
cl.def(init([](iterable it) { cl.def(init([](const iterable &it) {
auto v = std::unique_ptr<Vector>(new Vector()); auto v = std::unique_ptr<Vector>(new Vector());
v->reserve(len_hint(it)); v->reserve(len_hint(it));
for (handle h : it) for (handle h : it)
@ -151,8 +151,9 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
"Extend the list by appending all the items in the given list" "Extend the list by appending all the items in the given list"
); );
cl.def("extend", cl.def(
[](Vector &v, iterable it) { "extend",
[](Vector &v, const iterable &it) {
const size_t old_size = v.size(); const size_t old_size = v.size();
v.reserve(old_size + len_hint(it)); v.reserve(old_size + len_hint(it));
try { try {
@ -160,7 +161,8 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
v.push_back(h.cast<T>()); v.push_back(h.cast<T>());
} }
} catch (const cast_error &) { } catch (const cast_error &) {
v.erase(v.begin() + static_cast<typename Vector::difference_type>(old_size), v.end()); v.erase(v.begin() + static_cast<typename Vector::difference_type>(old_size),
v.end());
try { try {
v.shrink_to_fit(); v.shrink_to_fit();
} catch (const std::exception &) { } catch (const std::exception &) {
@ -170,8 +172,7 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
} }
}, },
arg("L"), arg("L"),
"Extend the list by appending all the items in the given list" "Extend the list by appending all the items in the given list");
);
cl.def("insert", cl.def("insert",
[](Vector &v, DiffType i, const T &x) { [](Vector &v, DiffType i, const T &x) {
@ -190,7 +191,7 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
[](Vector &v) { [](Vector &v) {
if (v.empty()) if (v.empty())
throw index_error(); throw index_error();
T t = v.back(); T t = std::move(v.back());
v.pop_back(); v.pop_back();
return t; return t;
}, },
@ -200,8 +201,8 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
cl.def("pop", cl.def("pop",
[wrap_i](Vector &v, DiffType i) { [wrap_i](Vector &v, DiffType i) {
i = wrap_i(i, v.size()); i = wrap_i(i, v.size());
T t = v[(SizeType) i]; T t = std::move(v[(SizeType) i]);
v.erase(v.begin() + i); v.erase(std::next(v.begin(), i));
return t; return t;
}, },
arg("i"), arg("i"),
@ -216,9 +217,10 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
); );
/// Slicing protocol /// Slicing protocol
cl.def("__getitem__", cl.def(
"__getitem__",
[](const Vector &v, slice slice) -> Vector * { [](const Vector &v, slice slice) -> Vector * {
size_t start, stop, step, slicelength; size_t start = 0, stop = 0, step = 0, slicelength = 0;
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) if (!slice.compute(v.size(), &start, &stop, &step, &slicelength))
throw error_already_set(); throw error_already_set();
@ -233,12 +235,12 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
return seq; return seq;
}, },
arg("s"), arg("s"),
"Retrieve list elements using a slice object" "Retrieve list elements using a slice object");
);
cl.def("__setitem__", cl.def(
"__setitem__",
[](Vector &v, slice slice, const Vector &value) { [](Vector &v, slice slice, const Vector &value) {
size_t start, stop, step, slicelength; size_t start = 0, stop = 0, step = 0, slicelength = 0;
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) if (!slice.compute(v.size(), &start, &stop, &step, &slicelength))
throw error_already_set(); throw error_already_set();
@ -250,8 +252,7 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
start += step; start += step;
} }
}, },
"Assign list elements using a slice object" "Assign list elements using a slice object");
);
cl.def("__delitem__", cl.def("__delitem__",
[wrap_i](Vector &v, DiffType i) { [wrap_i](Vector &v, DiffType i) {
@ -261,9 +262,10 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
"Delete the list elements at index ``i``" "Delete the list elements at index ``i``"
); );
cl.def("__delitem__", cl.def(
"__delitem__",
[](Vector &v, slice slice) { [](Vector &v, slice slice) {
size_t start, stop, step, slicelength; size_t start = 0, stop = 0, step = 0, slicelength = 0;
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) if (!slice.compute(v.size(), &start, &stop, &step, &slicelength))
throw error_already_set(); throw error_already_set();
@ -277,9 +279,7 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
} }
} }
}, },
"Delete list elements using a slice object" "Delete list elements using a slice object");
);
} }
// If the type has an operator[] that doesn't return a reference (most notably std::vector<bool>), // If the type has an operator[] that doesn't return a reference (most notably std::vector<bool>),
@ -400,7 +400,7 @@ void vector_buffer_impl(Class_& cl, std::true_type) {
return buffer_info(v.data(), static_cast<ssize_t>(sizeof(T)), format_descriptor<T>::format(), 1, {v.size()}, {sizeof(T)}); return buffer_info(v.data(), static_cast<ssize_t>(sizeof(T)), format_descriptor<T>::format(), 1, {v.size()}, {sizeof(T)});
}); });
cl.def(init([](buffer buf) { cl.def(init([](const buffer &buf) {
auto info = buf.request(); auto info = buf.request();
if (info.ndim != 1 || info.strides[0] % static_cast<ssize_t>(sizeof(T))) if (info.ndim != 1 || info.strides[0] % static_cast<ssize_t>(sizeof(T)))
throw type_error("Only valid 1D buffers can be copied to a vector"); throw type_error("Only valid 1D buffers can be copied to a vector");
@ -413,13 +413,12 @@ void vector_buffer_impl(Class_& cl, std::true_type) {
if (step == 1) { if (step == 1) {
return Vector(p, end); return Vector(p, end);
} }
else {
Vector vec; Vector vec;
vec.reserve((size_t) info.shape[0]); vec.reserve((size_t) info.shape[0]);
for (; p != end; p += step) for (; p != end; p += step)
vec.push_back(*p); vec.push_back(*p);
return vec; return vec;
}
})); }));
return; return;

View File

@ -8,5 +8,5 @@ def _to_int(s):
return s return s
__version__ = "2.6.3.dev1" __version__ = "2.7.1"
version_info = tuple(_to_int(s) for s in __version__.split(".")) version_info = tuple(_to_int(s) for s in __version__.split("."))

View File

@ -47,6 +47,7 @@ import tempfile
import threading import threading
import platform import platform
import warnings import warnings
import sysconfig
try: try:
from setuptools.command.build_ext import build_ext as _build_ext from setuptools.command.build_ext import build_ext as _build_ext
@ -58,8 +59,7 @@ except ImportError:
import distutils.errors import distutils.errors
import distutils.ccompiler import distutils.ccompiler
WIN = sys.platform.startswith("win32") and "mingw" not in sysconfig.get_platform()
WIN = sys.platform.startswith("win32")
PY2 = sys.version_info[0] < 3 PY2 = sys.version_info[0] < 3
MACOS = sys.platform.startswith("darwin") MACOS = sys.platform.startswith("darwin")
STD_TMPL = "/std:c++{}" if WIN else "-std=c++{}" STD_TMPL = "/std:c++{}" if WIN else "-std=c++{}"
@ -84,7 +84,7 @@ class Pybind11Extension(_Extension):
* ``stdlib=libc++`` on macOS * ``stdlib=libc++`` on macOS
* ``visibility=hidden`` and ``-g0`` on Unix * ``visibility=hidden`` and ``-g0`` on Unix
Finally, you can set ``cxx_std`` via constructor or afterwords to enable Finally, you can set ``cxx_std`` via constructor or afterwards to enable
flags for C++ std, and a few extra helper flags related to the C++ standard flags for C++ std, and a few extra helper flags related to the C++ standard
level. It is _highly_ recommended you either set this, or use the provided level. It is _highly_ recommended you either set this, or use the provided
``build_ext``, which will search for the highest supported extension for ``build_ext``, which will search for the highest supported extension for
@ -302,6 +302,42 @@ class build_ext(_build_ext): # noqa: N801
_build_ext.build_extensions(self) _build_ext.build_extensions(self)
def intree_extensions(paths, package_dir=None):
"""
Generate Pybind11Extensions from source files directly located in a Python
source tree.
``package_dir`` behaves as in ``setuptools.setup``. If unset, the Python
package root parent is determined as the first parent directory that does
not contain an ``__init__.py`` file.
"""
exts = []
for path in paths:
if package_dir is None:
parent, _ = os.path.split(path)
while os.path.exists(os.path.join(parent, "__init__.py")):
parent, _ = os.path.split(parent)
relname, _ = os.path.splitext(os.path.relpath(path, parent))
qualified_name = relname.replace(os.path.sep, ".")
exts.append(Pybind11Extension(qualified_name, [path]))
else:
found = False
for prefix, parent in package_dir.items():
if path.startswith(parent):
found = True
relname, _ = os.path.splitext(os.path.relpath(path, parent))
qualified_name = relname.replace(os.path.sep, ".")
if prefix:
qualified_name = prefix + "." + qualified_name
exts.append(Pybind11Extension(qualified_name, [path]))
if not found:
raise ValueError(
"path {} is not a child of any of the directories listed "
"in 'package_dir' ({})".format(path, package_dir)
)
return exts
def naive_recompile(obj, src): def naive_recompile(obj, src):
""" """
This will recompile only if the source file changes. It does not check This will recompile only if the source file changes. It does not check
@ -409,7 +445,9 @@ class ParallelCompile(object):
compiler._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) compiler._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
try: try:
import multiprocessing # Importing .synchronize checks for platforms that have some multiprocessing
# capabilities but lack semaphores, such as AWS Lambda and Android Termux.
import multiprocessing.synchronize
from multiprocessing.pool import ThreadPool from multiprocessing.pool import ThreadPool
except ImportError: except ImportError:
threads = 1 threads = 1

View File

@ -1,7 +1,7 @@
# IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI / # IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI /
# pre-commit). # pre-commit).
from typing import Any, Callable, Iterator, Optional, Type, TypeVar, Union from typing import Any, Callable, Dict, Iterator, List, Optional, Type, TypeVar, Union
from types import TracebackType from types import TracebackType
from distutils.command.build_ext import build_ext as _build_ext # type: ignore from distutils.command.build_ext import build_ext as _build_ext # type: ignore
@ -33,6 +33,9 @@ def auto_cpp_level(compiler: distutils.ccompiler.CCompiler) -> Union[int, str]:
class build_ext(_build_ext): # type: ignore class build_ext(_build_ext): # type: ignore
def build_extensions(self) -> None: ... def build_extensions(self) -> None: ...
def intree_extensions(
paths: Iterator[str], package_dir: Optional[Dict[str, str]] = None
) -> List[Pybind11Extension]: ...
def no_recompile(obj: str, src: str) -> bool: ... def no_recompile(obj: str, src: str) -> bool: ...
def naive_recompile(obj: str, src: str) -> bool: ... def naive_recompile(obj: str, src: str) -> bool: ...

View File

@ -247,6 +247,41 @@ if(Boost_FOUND)
endif() endif()
endif() endif()
# Check if we need to add -lstdc++fs or -lc++fs or nothing
if(MSVC)
set(STD_FS_NO_LIB_NEEDED TRUE)
else()
file(
WRITE ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
"#include <filesystem>\nint main(int argc, char ** argv) {\n std::filesystem::path p(argv[0]);\n return p.string().length();\n}"
)
try_compile(
STD_FS_NO_LIB_NEEDED ${CMAKE_CURRENT_BINARY_DIR}
SOURCES ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
COMPILE_DEFINITIONS -std=c++17)
try_compile(
STD_FS_NEEDS_STDCXXFS ${CMAKE_CURRENT_BINARY_DIR}
SOURCES ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
COMPILE_DEFINITIONS -std=c++17
LINK_LIBRARIES stdc++fs)
try_compile(
STD_FS_NEEDS_CXXFS ${CMAKE_CURRENT_BINARY_DIR}
SOURCES ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
COMPILE_DEFINITIONS -std=c++17
LINK_LIBRARIES c++fs)
endif()
if(${STD_FS_NEEDS_STDCXXFS})
set(STD_FS_LIB stdc++fs)
elseif(${STD_FS_NEEDS_CXXFS})
set(STD_FS_LIB c++fs)
elseif(${STD_FS_NO_LIB_NEEDED})
set(STD_FS_LIB "")
else()
message(WARNING "Unknown compiler - not passing -lstdc++fs")
set(STD_FS_LIB "")
endif()
# Compile with compiler warnings turned on # Compile with compiler warnings turned on
function(pybind11_enable_warnings target_name) function(pybind11_enable_warnings target_name)
if(MSVC) if(MSVC)
@ -268,12 +303,19 @@ function(pybind11_enable_warnings target_name)
target_compile_options(${target_name} PRIVATE /WX) target_compile_options(${target_name} PRIVATE /WX)
elseif(PYBIND11_CUDA_TESTS) elseif(PYBIND11_CUDA_TESTS)
target_compile_options(${target_name} PRIVATE "SHELL:-Werror all-warnings") target_compile_options(${target_name} PRIVATE "SHELL:-Werror all-warnings")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)") elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang|IntelLLVM)")
target_compile_options(${target_name} PRIVATE -Werror) target_compile_options(${target_name} PRIVATE -Werror)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
target_compile_options(
${target_name}
PRIVATE
-Werror-all
# "Inlining inhibited by limit max-size", "Inlining inhibited by limit max-total-size"
-diag-disable 11074,11076)
endif() endif()
endif() endif()
# Needs to be readded since the ordering requires these to be after the ones above # Needs to be re-added since the ordering requires these to be after the ones above
if(CMAKE_CXX_STANDARD if(CMAKE_CXX_STANDARD
AND CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_ID MATCHES "Clang"
AND PYTHON_VERSION VERSION_LESS 3.0) AND PYTHON_VERSION VERSION_LESS 3.0)
@ -350,6 +392,8 @@ foreach(target ${test_targets})
target_compile_definitions(${target} PRIVATE -DPYBIND11_TEST_BOOST) target_compile_definitions(${target} PRIVATE -DPYBIND11_TEST_BOOST)
endif() endif()
target_link_libraries(${target} PRIVATE ${STD_FS_LIB})
# Always write the output file directly into the 'tests' directory (even on MSVC) # Always write the output file directly into the 'tests' directory (even on MSVC)
if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY

View File

@ -2,6 +2,8 @@
import platform import platform
import sys import sys
import pytest
LINUX = sys.platform.startswith("linux") LINUX = sys.platform.startswith("linux")
MACOS = sys.platform.startswith("darwin") MACOS = sys.platform.startswith("darwin")
WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin") WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin")
@ -12,3 +14,20 @@ PYPY = platform.python_implementation() == "PyPy"
PY2 = sys.version_info.major == 2 PY2 = sys.version_info.major == 2
PY = sys.version_info PY = sys.version_info
def deprecated_call():
"""
pytest.deprecated_call() seems broken in pytest<3.9.x; concretely, it
doesn't work on CPython 3.8.0 with pytest==3.3.2 on Ubuntu 18.04 (#2922).
This is a narrowed reimplementation of the following PR :(
https://github.com/pytest-dev/pytest/pull/4104
"""
# TODO: Remove this when testing requires pytest>=3.9.
pieces = pytest.__version__.split(".")
pytest_major_minor = (int(pieces[0]), int(pieces[1]))
if pytest_major_minor < (3, 9):
return pytest.warns((DeprecationWarning, PendingDeprecationWarning))
else:
return pytest.deprecated_call()

View File

@ -46,6 +46,10 @@ detail_headers = {
"include/pybind11/detail/typeid.h", "include/pybind11/detail/typeid.h",
} }
stl_headers = {
"include/pybind11/stl/filesystem.h",
}
cmake_files = { cmake_files = {
"share/cmake/pybind11/FindPythonLibsNew.cmake", "share/cmake/pybind11/FindPythonLibsNew.cmake",
"share/cmake/pybind11/pybind11Common.cmake", "share/cmake/pybind11/pybind11Common.cmake",
@ -67,7 +71,7 @@ py_files = {
"setup_helpers.pyi", "setup_helpers.pyi",
} }
headers = main_headers | detail_headers headers = main_headers | detail_headers | stl_headers
src_files = headers | cmake_files src_files = headers | cmake_files
all_files = src_files | py_files all_files = src_files | py_files
@ -77,6 +81,7 @@ sdist_files = {
"pybind11/include", "pybind11/include",
"pybind11/include/pybind11", "pybind11/include/pybind11",
"pybind11/include/pybind11/detail", "pybind11/include/pybind11/detail",
"pybind11/include/pybind11/stl",
"pybind11/share", "pybind11/share",
"pybind11/share/cmake", "pybind11/share/cmake",
"pybind11/share/cmake/pybind11", "pybind11/share/cmake/pybind11",
@ -121,7 +126,7 @@ def test_build_sdist(monkeypatch, tmpdir):
with tarfile.open(str(sdist)) as tar: with tarfile.open(str(sdist)) as tar:
start = tar.getnames()[0] + "/" start = tar.getnames()[0] + "/"
version = start[9:-1] version = start[9:-1]
simpler = set(n.split("/", 1)[-1] for n in tar.getnames()[1:]) simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]}
with contextlib.closing( with contextlib.closing(
tar.extractfile(tar.getmember(start + "setup.py")) tar.extractfile(tar.getmember(start + "setup.py"))
@ -133,9 +138,19 @@ def test_build_sdist(monkeypatch, tmpdir):
) as f: ) as f:
pyproject_toml = f.read() pyproject_toml = f.read()
files = set("pybind11/{}".format(n) for n in all_files) with contextlib.closing(
tar.extractfile(
tar.getmember(
start + "pybind11/share/cmake/pybind11/pybind11Config.cmake"
)
)
) as f:
contents = f.read().decode("utf8")
assert 'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")' in contents
files = {"pybind11/{}".format(n) for n in all_files}
files |= sdist_files files |= sdist_files
files |= set("pybind11{}".format(n) for n in local_sdist_files) files |= {"pybind11{}".format(n) for n in local_sdist_files}
files.add("pybind11.egg-info/entry_points.txt") files.add("pybind11.egg-info/entry_points.txt")
files.add("pybind11.egg-info/requires.txt") files.add("pybind11.egg-info/requires.txt")
assert simpler == files assert simpler == files
@ -176,7 +191,7 @@ def test_build_global_dist(monkeypatch, tmpdir):
with tarfile.open(str(sdist)) as tar: with tarfile.open(str(sdist)) as tar:
start = tar.getnames()[0] + "/" start = tar.getnames()[0] + "/"
version = start[16:-1] version = start[16:-1]
simpler = set(n.split("/", 1)[-1] for n in tar.getnames()[1:]) simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]}
with contextlib.closing( with contextlib.closing(
tar.extractfile(tar.getmember(start + "setup.py")) tar.extractfile(tar.getmember(start + "setup.py"))
@ -188,9 +203,9 @@ def test_build_global_dist(monkeypatch, tmpdir):
) as f: ) as f:
pyproject_toml = f.read() pyproject_toml = f.read()
files = set("pybind11/{}".format(n) for n in all_files) files = {"pybind11/{}".format(n) for n in all_files}
files |= sdist_files files |= sdist_files
files |= set("pybind11_global{}".format(n) for n in local_sdist_files) files |= {"pybind11_global{}".format(n) for n in local_sdist_files}
assert simpler == files assert simpler == files
with open(os.path.join(MAIN_DIR, "tools", "setup_global.py.in"), "rb") as f: with open(os.path.join(MAIN_DIR, "tools", "setup_global.py.in"), "rb") as f:
@ -215,7 +230,7 @@ def tests_build_wheel(monkeypatch, tmpdir):
(wheel,) = tmpdir.visit("*.whl") (wheel,) = tmpdir.visit("*.whl")
files = set("pybind11/{}".format(n) for n in all_files) files = {"pybind11/{}".format(n) for n in all_files}
files |= { files |= {
"dist-info/LICENSE", "dist-info/LICENSE",
"dist-info/METADATA", "dist-info/METADATA",
@ -228,10 +243,10 @@ def tests_build_wheel(monkeypatch, tmpdir):
with zipfile.ZipFile(str(wheel)) as z: with zipfile.ZipFile(str(wheel)) as z:
names = z.namelist() names = z.namelist()
trimmed = set(n for n in names if "dist-info" not in n) trimmed = {n for n in names if "dist-info" not in n}
trimmed |= set( trimmed |= {
"dist-info/{}".format(n.split("/", 1)[-1]) for n in names if "dist-info" in n "dist-info/{}".format(n.split("/", 1)[-1]) for n in names if "dist-info" in n
) }
assert files == trimmed assert files == trimmed
@ -245,8 +260,8 @@ def tests_build_global_wheel(monkeypatch, tmpdir):
(wheel,) = tmpdir.visit("*.whl") (wheel,) = tmpdir.visit("*.whl")
files = set("data/data/{}".format(n) for n in src_files) files = {"data/data/{}".format(n) for n in src_files}
files |= set("data/headers/{}".format(n[8:]) for n in headers) files |= {"data/headers/{}".format(n[8:]) for n in headers}
files |= { files |= {
"dist-info/LICENSE", "dist-info/LICENSE",
"dist-info/METADATA", "dist-info/METADATA",
@ -259,6 +274,6 @@ def tests_build_global_wheel(monkeypatch, tmpdir):
names = z.namelist() names = z.namelist()
beginning = names[0].split("/", 1)[0].rsplit(".", 1)[0] beginning = names[0].split("/", 1)[0].rsplit(".", 1)[0]
trimmed = set(n[len(beginning) + 1 :] for n in names) trimmed = {n[len(beginning) + 1 :] for n in names}
assert files == trimmed assert files == trimmed

View File

@ -99,3 +99,45 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
subprocess.check_call( subprocess.check_call(
[sys.executable, "test.py"], stdout=sys.stdout, stderr=sys.stderr [sys.executable, "test.py"], stdout=sys.stdout, stderr=sys.stderr
) )
def test_intree_extensions(monkeypatch, tmpdir):
monkeypatch.syspath_prepend(MAIN_DIR)
from pybind11.setup_helpers import intree_extensions
monkeypatch.chdir(tmpdir)
root = tmpdir
root.ensure_dir()
subdir = root / "dir"
subdir.ensure_dir()
src = subdir / "ext.cpp"
src.ensure()
(ext,) = intree_extensions([src.relto(tmpdir)])
assert ext.name == "ext"
subdir.ensure("__init__.py")
(ext,) = intree_extensions([src.relto(tmpdir)])
assert ext.name == "dir.ext"
def test_intree_extensions_package_dir(monkeypatch, tmpdir):
monkeypatch.syspath_prepend(MAIN_DIR)
from pybind11.setup_helpers import intree_extensions
monkeypatch.chdir(tmpdir)
root = tmpdir / "src"
root.ensure_dir()
subdir = root / "dir"
subdir.ensure_dir()
src = subdir / "ext.cpp"
src.ensure()
(ext,) = intree_extensions([src.relto(tmpdir)], package_dir={"": "src"})
assert ext.name == "dir.ext"
(ext,) = intree_extensions([src.relto(tmpdir)], package_dir={"foo": "src"})
assert ext.name == "foo.dir.ext"
subdir.ensure("__init__.py")
(ext,) = intree_extensions([src.relto(tmpdir)], package_dir={"": "src"})
assert ext.name == "dir.ext"
(ext,) = intree_extensions([src.relto(tmpdir)], package_dir={"foo": "src"})
assert ext.name == "foo.dir.ext"

View File

@ -1,4 +1,6 @@
#pragma once #pragma once
#include <utility>
#include "pybind11_tests.h" #include "pybind11_tests.h"
/// Simple class used to test py::local: /// Simple class used to test py::local:
@ -54,9 +56,9 @@ py::class_<T> bind_local(Args && ...args) {
namespace pets { namespace pets {
class Pet { class Pet {
public: public:
Pet(std::string name) : name_(name) {} Pet(std::string name) : name_(std::move(name)) {}
std::string name_; std::string name_;
const std::string &name() { return name_; } const std::string &name() const { return name_; }
}; };
} // namespace pets } // namespace pets

View File

@ -81,7 +81,7 @@ public:
} }
/// Move constructor /// Move constructor
ref(ref &&r) : m_ptr(r.m_ptr) { ref(ref &&r) noexcept : m_ptr(r.m_ptr) {
r.m_ptr = nullptr; r.m_ptr = nullptr;
print_move_created(this, "with pointer", m_ptr); track_move_created((ref_tag*) this); print_move_created(this, "with pointer", m_ptr); track_move_created((ref_tag*) this);
@ -96,7 +96,7 @@ public:
} }
/// Move another reference into the current one /// Move another reference into the current one
ref& operator=(ref&& r) { ref &operator=(ref &&r) noexcept {
print_move_assigned(this, "pointer", r.m_ptr); track_move_assigned((ref_tag*) this); print_move_assigned(this, "pointer", r.m_ptr); track_move_assigned((ref_tag*) this);
if (*this == r) if (*this == r)

View File

@ -9,8 +9,12 @@
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include "local_bindings.h" #include "local_bindings.h"
#include "test_exceptions.h"
#include <pybind11/stl_bind.h> #include <pybind11/stl_bind.h>
#include <numeric> #include <numeric>
#include <utility>
PYBIND11_MODULE(pybind11_cross_module_tests, m) { PYBIND11_MODULE(pybind11_cross_module_tests, m) {
m.doc() = "pybind11 cross-module test module"; m.doc() = "pybind11 cross-module test module";
@ -30,6 +34,13 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
m.def("throw_pybind_value_error", []() { throw py::value_error("pybind11 value error"); }); m.def("throw_pybind_value_error", []() { throw py::value_error("pybind11 value error"); });
m.def("throw_pybind_type_error", []() { throw py::type_error("pybind11 type error"); }); m.def("throw_pybind_type_error", []() { throw py::type_error("pybind11 type error"); });
m.def("throw_stop_iteration", []() { throw py::stop_iteration(); }); m.def("throw_stop_iteration", []() { throw py::stop_iteration(); });
py::register_exception_translator([](std::exception_ptr p) {
try {
if (p) std::rethrow_exception(p);
} catch (const shared_exception &e) {
PyErr_SetString(PyExc_KeyError, e.what());
}
});
// test_local_bindings.py // test_local_bindings.py
// Local to both: // Local to both:
@ -96,7 +107,10 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
m.def("return_self", [](LocalVec *v) { return v; }); m.def("return_self", [](LocalVec *v) { return v; });
m.def("return_copy", [](const LocalVec &v) { return LocalVec(v); }); m.def("return_copy", [](const LocalVec &v) { return LocalVec(v); });
class Dog : public pets::Pet { public: Dog(std::string name) : Pet(name) {}; }; class Dog : public pets::Pet {
public:
Dog(std::string name) : Pet(std::move(name)) {}
};
py::class_<pets::Pet>(m, "Pet", py::module_local()) py::class_<pets::Pet>(m, "Pet", py::module_local())
.def("name", &pets::Pet::name); .def("name", &pets::Pet::name);
// Binding for local extending class: // Binding for local extending class:
@ -118,6 +132,6 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
// test_missing_header_message // test_missing_header_message
// The main module already includes stl.h, but we need to test the error message // The main module already includes stl.h, but we need to test the error message
// which appears when this header is missing. // which appears when this header is missing.
m.def("missing_header_arg", [](std::vector<float>) { }); m.def("missing_header_arg", [](const std::vector<float> &) {});
m.def("missing_header_return", []() { return std::vector<float>(); }); m.def("missing_header_return", []() { return std::vector<float>(); });
} }

View File

@ -1,10 +1,15 @@
#pragma once #pragma once
// This must be kept first for MSVC 2015.
// Do not remove the empty line between the #includes.
#include <pybind11/pybind11.h> #include <pybind11/pybind11.h>
#include <pybind11/eval.h> #include <pybind11/eval.h>
#if defined(_MSC_VER) && _MSC_VER < 1910 #if defined(_MSC_VER) && _MSC_VER < 1910
// We get some really long type names here which causes MSVC 2015 to emit warnings // We get some really long type names here which causes MSVC 2015 to emit warnings
# pragma warning(disable: 4503) // warning C4503: decorated name length exceeded, name was truncated # pragma warning( \
disable : 4503) // warning C4503: decorated name length exceeded, name was truncated
#endif #endif
namespace py = pybind11; namespace py = pybind11;

View File

@ -5,7 +5,8 @@ numpy==1.19.3; (platform_python_implementation!="PyPy" or sys_platform=="linux")
numpy==1.20.0; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.7" and python_version<"3.10" numpy==1.20.0; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.7" and python_version<"3.10"
pytest==4.6.9; python_version<"3.5" pytest==4.6.9; python_version<"3.5"
pytest==6.1.2; python_version=="3.5" pytest==6.1.2; python_version=="3.5"
pytest==6.2.1; python_version>="3.6" pytest==6.2.1; python_version>="3.6" and python_version<="3.9"
pytest @ git+https://github.com/pytest-dev/pytest@c117bc350ec1e570672fda3b2ad234fd52e72b53; python_version>="3.10"
pytest-timeout pytest-timeout
scipy==1.2.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version<"3.6" scipy==1.2.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version<"3.6"
scipy==1.5.4; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.6" and python_version<"3.10" scipy==1.5.4; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.6" and python_version<"3.10"

View File

@ -27,7 +27,7 @@ TEST_SUBMODULE(buffers, m) {
memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols)); memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols));
} }
Matrix(Matrix &&s) : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) { Matrix(Matrix &&s) noexcept : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) {
print_move_created(this); print_move_created(this);
s.m_rows = 0; s.m_rows = 0;
s.m_cols = 0; s.m_cols = 0;
@ -49,7 +49,7 @@ TEST_SUBMODULE(buffers, m) {
return *this; return *this;
} }
Matrix &operator=(Matrix &&s) { Matrix &operator=(Matrix &&s) noexcept {
print_move_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); print_move_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
if (&s != this) { if (&s != this) {
delete[] m_data; delete[] m_data;
@ -79,7 +79,7 @@ TEST_SUBMODULE(buffers, m) {
py::class_<Matrix>(m, "Matrix", py::buffer_protocol()) py::class_<Matrix>(m, "Matrix", py::buffer_protocol())
.def(py::init<py::ssize_t, py::ssize_t>()) .def(py::init<py::ssize_t, py::ssize_t>())
/// Construct from a buffer /// Construct from a buffer
.def(py::init([](py::buffer const b) { .def(py::init([](const py::buffer &b) {
py::buffer_info info = b.request(); py::buffer_info info = b.request();
if (info.format != py::format_descriptor<float>::format() || info.ndim != 2) if (info.format != py::format_descriptor<float>::format() || info.ndim != 2)
throw std::runtime_error("Incompatible buffer format!"); throw std::runtime_error("Incompatible buffer format!");
@ -93,12 +93,14 @@ TEST_SUBMODULE(buffers, m) {
.def("cols", &Matrix::cols) .def("cols", &Matrix::cols)
/// Bare bones interface /// Bare bones interface
.def("__getitem__", [](const Matrix &m, std::pair<py::ssize_t, py::ssize_t> i) { .def("__getitem__",
[](const Matrix &m, std::pair<py::ssize_t, py::ssize_t> i) {
if (i.first >= m.rows() || i.second >= m.cols()) if (i.first >= m.rows() || i.second >= m.cols())
throw py::index_error(); throw py::index_error();
return m(i.first, i.second); return m(i.first, i.second);
}) })
.def("__setitem__", [](Matrix &m, std::pair<py::ssize_t, py::ssize_t> i, float v) { .def("__setitem__",
[](Matrix &m, std::pair<py::ssize_t, py::ssize_t> i, float v) {
if (i.first >= m.rows() || i.second >= m.cols()) if (i.first >= m.rows() || i.second >= m.cols())
throw py::index_error(); throw py::index_error();
m(i.first, i.second) = v; m(i.first, i.second) = v;
@ -111,9 +113,7 @@ TEST_SUBMODULE(buffers, m) {
{ sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */ { sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */
sizeof(float) } sizeof(float) }
); );
}) });
;
// test_inherited_protocol // test_inherited_protocol
class SquareMatrix : public Matrix { class SquareMatrix : public Matrix {
@ -154,7 +154,7 @@ TEST_SUBMODULE(buffers, m) {
py::format_descriptor<int32_t>::format(), 1); py::format_descriptor<int32_t>::format(), 1);
} }
ConstBuffer() : value(new int32_t{0}) { }; ConstBuffer() : value(new int32_t{0}) {}
}; };
py::class_<ConstBuffer>(m, "ConstBuffer", py::buffer_protocol()) py::class_<ConstBuffer>(m, "ConstBuffer", py::buffer_protocol())
.def(py::init<>()) .def(py::init<>())
@ -208,7 +208,5 @@ TEST_SUBMODULE(buffers, m) {
}) })
; ;
m.def("get_buffer_info", [](py::buffer buffer) { m.def("get_buffer_info", [](const py::buffer &buffer) { return buffer.request(); });
return buffer.request();
});
} }

View File

@ -30,7 +30,11 @@ class type_caster<ConstRefCasted> {
// cast operator. // cast operator.
bool load(handle, bool) { return true; } bool load(handle, bool) { return true; }
operator ConstRefCasted&&() { value = {1}; return std::move(value); } operator ConstRefCasted &&() {
value = {1};
// NOLINTNEXTLINE(performance-move-const-arg)
return std::move(value);
}
operator ConstRefCasted&() { value = {2}; return value; } operator ConstRefCasted&() { value = {2}; return value; }
operator ConstRefCasted*() { value = {3}; return &value; } operator ConstRefCasted*() { value = {3}; return &value; }
@ -101,7 +105,7 @@ TEST_SUBMODULE(builtin_casters, m) {
// test_bytes_to_string // test_bytes_to_string
m.def("strlen", [](char *s) { return strlen(s); }); m.def("strlen", [](char *s) { return strlen(s); });
m.def("string_length", [](std::string s) { return s.length(); }); m.def("string_length", [](const std::string &s) { return s.length(); });
#ifdef PYBIND11_HAS_U8STRING #ifdef PYBIND11_HAS_U8STRING
m.attr("has_u8string") = true; m.attr("has_u8string") = true;
@ -146,9 +150,12 @@ TEST_SUBMODULE(builtin_casters, m) {
m.def("int_passthrough_noconvert", [](int arg) { return arg; }, py::arg{}.noconvert()); m.def("int_passthrough_noconvert", [](int arg) { return arg; }, py::arg{}.noconvert());
// test_tuple // test_tuple
m.def("pair_passthrough", [](std::pair<bool, std::string> input) { m.def(
"pair_passthrough",
[](const std::pair<bool, std::string> &input) {
return std::make_pair(input.second, input.first); return std::make_pair(input.second, input.first);
}, "Return a pair in reversed order"); },
"Return a pair in reversed order");
m.def("tuple_passthrough", [](std::tuple<bool, std::string, int> input) { m.def("tuple_passthrough", [](std::tuple<bool, std::string, int> input) {
return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input)); return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input));
}, "Return a triple in reversed order"); }, "Return a triple in reversed order");
@ -177,11 +184,11 @@ TEST_SUBMODULE(builtin_casters, m) {
// test_none_deferred // test_none_deferred
m.def("defer_none_cstring", [](char *) { return false; }); m.def("defer_none_cstring", [](char *) { return false; });
m.def("defer_none_cstring", [](py::none) { return true; }); m.def("defer_none_cstring", [](const py::none &) { return true; });
m.def("defer_none_custom", [](UserType *) { return false; }); m.def("defer_none_custom", [](UserType *) { return false; });
m.def("defer_none_custom", [](py::none) { return true; }); m.def("defer_none_custom", [](const py::none &) { return true; });
m.def("nodefer_none_void", [](void *) { return true; }); m.def("nodefer_none_void", [](void *) { return true; });
m.def("nodefer_none_void", [](py::none) { return false; }); m.def("nodefer_none_void", [](const py::none &) { return false; });
// test_void_caster // test_void_caster
m.def("load_nullptr_t", [](std::nullptr_t) {}); // not useful, but it should still compile m.def("load_nullptr_t", [](std::nullptr_t) {}); // not useful, but it should still compile
@ -231,7 +238,7 @@ TEST_SUBMODULE(builtin_casters, m) {
}, "copy"_a); }, "copy"_a);
m.def("refwrap_iiw", [](const IncType &w) { return w.value(); }); m.def("refwrap_iiw", [](const IncType &w) { return w.value(); });
m.def("refwrap_call_iiw", [](IncType &w, py::function f) { m.def("refwrap_call_iiw", [](IncType &w, const py::function &f) {
py::list l; py::list l;
l.append(f(std::ref(w))); l.append(f(std::ref(w)));
l.append(f(std::cref(w))); l.append(f(std::cref(w)));

View File

@ -50,7 +50,7 @@ def test_single_char_arguments():
"""Tests failures for passing invalid inputs to char-accepting functions""" """Tests failures for passing invalid inputs to char-accepting functions"""
def toobig_message(r): def toobig_message(r):
return "Character code point not in range({0:#x})".format(r) return "Character code point not in range({:#x})".format(r)
toolong_message = "Expected a character, but multi-character string found" toolong_message = "Expected a character, but multi-character string found"
@ -301,7 +301,7 @@ def test_int_convert():
cant_convert(3.14159) cant_convert(3.14159)
# TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar)
if (3, 8) <= env.PY < (3, 10): if (3, 8) <= env.PY < (3, 10):
with pytest.deprecated_call(): with env.deprecated_call():
assert convert(Int()) == 42 assert convert(Int()) == 42
else: else:
assert convert(Int()) == 42 assert convert(Int()) == 42
@ -336,7 +336,7 @@ def test_numpy_int_convert():
# The implicit conversion from np.float32 is undesirable but currently accepted. # The implicit conversion from np.float32 is undesirable but currently accepted.
# TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar)
if (3, 8) <= env.PY < (3, 10): if (3, 8) <= env.PY < (3, 10):
with pytest.deprecated_call(): with env.deprecated_call():
assert convert(np.float32(3.14159)) == 3 assert convert(np.float32(3.14159)) == 3
else: else:
assert convert(np.float32(3.14159)) == 3 assert convert(np.float32(3.14159)) == 3
@ -521,7 +521,7 @@ def test_void_caster_2():
def test_const_ref_caster(): def test_const_ref_caster():
"""Verifies that const-ref is propagated through type_caster cast_op. """Verifies that const-ref is propagated through type_caster cast_op.
The returned ConstRefCasted type is a mimimal type that is constructed to The returned ConstRefCasted type is a minimal type that is constructed to
reference the casting mode used. reference the casting mode used.
""" """
x = False x = False

View File

@ -51,6 +51,7 @@ TEST_SUBMODULE(call_policies, m) {
void addChild(Child *) { } void addChild(Child *) { }
Child *returnChild() { return new Child(); } Child *returnChild() { return new Child(); }
Child *returnNullChild() { return nullptr; } Child *returnNullChild() { return nullptr; }
static Child *staticFunction(Parent*) { return new Child(); }
}; };
py::class_<Parent>(m, "Parent") py::class_<Parent>(m, "Parent")
.def(py::init<>()) .def(py::init<>())
@ -60,7 +61,12 @@ TEST_SUBMODULE(call_policies, m) {
.def("returnChild", &Parent::returnChild) .def("returnChild", &Parent::returnChild)
.def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>()) .def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>())
.def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>()) .def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>())
.def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>()); .def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>())
.def_static(
"staticFunction", &Parent::staticFunction, py::keep_alive<1, 0>());
m.def("free_function", [](Parent*, Child*) {}, py::keep_alive<1, 2>());
m.def("invalid_arg_index", []{}, py::keep_alive<0, 1>());
#if !defined(PYPY_VERSION) #if !defined(PYPY_VERSION)
// test_alive_gc // test_alive_gc

View File

@ -46,6 +46,19 @@ def test_keep_alive_argument(capture):
""" """
) )
p = m.Parent()
c = m.Child()
assert ConstructorStats.detail_reg_inst() == n_inst + 2
m.free_function(p, c)
del c
assert ConstructorStats.detail_reg_inst() == n_inst + 2
del p
assert ConstructorStats.detail_reg_inst() == n_inst
with pytest.raises(RuntimeError) as excinfo:
m.invalid_arg_index()
assert str(excinfo.value) == "Could not activate keep_alive!"
def test_keep_alive_return_value(capture): def test_keep_alive_return_value(capture):
n_inst = ConstructorStats.detail_reg_inst() n_inst = ConstructorStats.detail_reg_inst()
@ -85,6 +98,23 @@ def test_keep_alive_return_value(capture):
""" """
) )
p = m.Parent()
assert ConstructorStats.detail_reg_inst() == n_inst + 1
with capture:
m.Parent.staticFunction(p)
assert ConstructorStats.detail_reg_inst() == n_inst + 2
assert capture == "Allocating child."
with capture:
del p
assert ConstructorStats.detail_reg_inst() == n_inst
assert (
capture
== """
Releasing parent.
Releasing child.
"""
)
# https://foss.heptapod.net/pypy/pypy/-/issues/2447 # https://foss.heptapod.net/pypy/pypy/-/issues/2447
@pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented") @pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented")

View File

@ -17,8 +17,8 @@ int dummy_function(int i) { return i + 1; }
TEST_SUBMODULE(callbacks, m) { TEST_SUBMODULE(callbacks, m) {
// test_callbacks, test_function_signatures // test_callbacks, test_function_signatures
m.def("test_callback1", [](py::object func) { return func(); }); m.def("test_callback1", [](const py::object &func) { return func(); });
m.def("test_callback2", [](py::object func) { return func("Hello", 'x', true, 5); }); m.def("test_callback2", [](const py::object &func) { return func("Hello", 'x', true, 5); });
m.def("test_callback3", [](const std::function<int(int)> &func) { m.def("test_callback3", [](const std::function<int(int)> &func) {
return "func(43) = " + std::to_string(func(43)); }); return "func(43) = " + std::to_string(func(43)); });
m.def("test_callback4", []() -> std::function<int(int)> { return [](int i) { return i+1; }; }); m.def("test_callback4", []() -> std::function<int(int)> { return [](int i) { return i+1; }; });
@ -27,51 +27,48 @@ TEST_SUBMODULE(callbacks, m) {
}); });
// test_keyword_args_and_generalized_unpacking // test_keyword_args_and_generalized_unpacking
m.def("test_tuple_unpacking", [](py::function f) { m.def("test_tuple_unpacking", [](const py::function &f) {
auto t1 = py::make_tuple(2, 3); auto t1 = py::make_tuple(2, 3);
auto t2 = py::make_tuple(5, 6); auto t2 = py::make_tuple(5, 6);
return f("positional", 1, *t1, 4, *t2); return f("positional", 1, *t1, 4, *t2);
}); });
m.def("test_dict_unpacking", [](py::function f) { m.def("test_dict_unpacking", [](const py::function &f) {
auto d1 = py::dict("key"_a="value", "a"_a=1); auto d1 = py::dict("key"_a="value", "a"_a=1);
auto d2 = py::dict(); auto d2 = py::dict();
auto d3 = py::dict("b"_a=2); auto d3 = py::dict("b"_a=2);
return f("positional", 1, **d1, **d2, **d3); return f("positional", 1, **d1, **d2, **d3);
}); });
m.def("test_keyword_args", [](py::function f) { m.def("test_keyword_args", [](const py::function &f) { return f("x"_a = 10, "y"_a = 20); });
return f("x"_a=10, "y"_a=20);
});
m.def("test_unpacking_and_keywords1", [](py::function f) { m.def("test_unpacking_and_keywords1", [](const py::function &f) {
auto args = py::make_tuple(2); auto args = py::make_tuple(2);
auto kwargs = py::dict("d"_a=4); auto kwargs = py::dict("d"_a=4);
return f(1, *args, "c"_a=3, **kwargs); return f(1, *args, "c"_a=3, **kwargs);
}); });
m.def("test_unpacking_and_keywords2", [](py::function f) { m.def("test_unpacking_and_keywords2", [](const py::function &f) {
auto kwargs1 = py::dict("a"_a=1); auto kwargs1 = py::dict("a"_a=1);
auto kwargs2 = py::dict("c"_a=3, "d"_a=4); auto kwargs2 = py::dict("c"_a=3, "d"_a=4);
return f("positional", *py::make_tuple(1), 2, *py::make_tuple(3, 4), 5, return f("positional", *py::make_tuple(1), 2, *py::make_tuple(3, 4), 5,
"key"_a="value", **kwargs1, "b"_a=2, **kwargs2, "e"_a=5); "key"_a="value", **kwargs1, "b"_a=2, **kwargs2, "e"_a=5);
}); });
m.def("test_unpacking_error1", [](py::function f) { m.def("test_unpacking_error1", [](const py::function &f) {
auto kwargs = py::dict("x"_a=3); auto kwargs = py::dict("x"_a=3);
return f("x"_a=1, "y"_a=2, **kwargs); // duplicate ** after keyword return f("x"_a=1, "y"_a=2, **kwargs); // duplicate ** after keyword
}); });
m.def("test_unpacking_error2", [](py::function f) { m.def("test_unpacking_error2", [](const py::function &f) {
auto kwargs = py::dict("x"_a=3); auto kwargs = py::dict("x"_a=3);
return f(**kwargs, "x"_a=1); // duplicate keyword after ** return f(**kwargs, "x"_a=1); // duplicate keyword after **
}); });
m.def("test_arg_conversion_error1", [](py::function f) { m.def("test_arg_conversion_error1",
f(234, UnregisteredType(), "kw"_a=567); [](const py::function &f) { f(234, UnregisteredType(), "kw"_a = 567); });
});
m.def("test_arg_conversion_error2", [](py::function f) { m.def("test_arg_conversion_error2", [](const py::function &f) {
f(234, "expected_name"_a=UnregisteredType(), "kw"_a=567); f(234, "expected_name"_a=UnregisteredType(), "kw"_a=567);
}); });
@ -80,12 +77,12 @@ TEST_SUBMODULE(callbacks, m) {
Payload() { print_default_created(this); } Payload() { print_default_created(this); }
~Payload() { print_destroyed(this); } ~Payload() { print_destroyed(this); }
Payload(const Payload &) { print_copy_created(this); } Payload(const Payload &) { print_copy_created(this); }
Payload(Payload &&) { print_move_created(this); } Payload(Payload &&) noexcept { print_move_created(this); }
}; };
// Export the payload constructor statistics for testing purposes: // Export the payload constructor statistics for testing purposes:
m.def("payload_cstats", &ConstructorStats::get<Payload>); m.def("payload_cstats", &ConstructorStats::get<Payload>);
/* Test cleanup of lambda closure */ /* Test cleanup of lambda closure */
m.def("test_cleanup", []() -> std::function<void(void)> { m.def("test_cleanup", []() -> std::function<void()> {
Payload p; Payload p;
return [p]() { return [p]() {
@ -97,6 +94,8 @@ TEST_SUBMODULE(callbacks, m) {
// test_cpp_function_roundtrip // test_cpp_function_roundtrip
/* Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer */ /* Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer */
m.def("dummy_function", &dummy_function); m.def("dummy_function", &dummy_function);
m.def("dummy_function_overloaded", [](int i, int j) { return i + j; });
m.def("dummy_function_overloaded", &dummy_function);
m.def("dummy_function2", [](int i, int j) { return i + j; }); m.def("dummy_function2", [](int i, int j) { return i + j; });
m.def("roundtrip", [](std::function<int(int)> f, bool expect_none = false) { m.def("roundtrip", [](std::function<int(int)> f, bool expect_none = false) {
if (expect_none && f) if (expect_none && f)
@ -109,12 +108,13 @@ TEST_SUBMODULE(callbacks, m) {
if (!result) { if (!result) {
auto r = f(1); auto r = f(1);
return "can't convert to function pointer: eval(1) = " + std::to_string(r); return "can't convert to function pointer: eval(1) = " + std::to_string(r);
} else if (*result == dummy_function) { }
if (*result == dummy_function) {
auto r = (*result)(1); auto r = (*result)(1);
return "matches dummy_function: eval(1) = " + std::to_string(r); return "matches dummy_function: eval(1) = " + std::to_string(r);
} else {
return "argument does NOT match dummy_function. This should never happen!";
} }
return "argument does NOT match dummy_function. This should never happen!";
}); });
class AbstractBase { class AbstractBase {
@ -122,10 +122,11 @@ TEST_SUBMODULE(callbacks, m) {
// [workaround(intel)] = default does not work here // [workaround(intel)] = default does not work here
// Defaulting this destructor results in linking errors with the Intel compiler // Defaulting this destructor results in linking errors with the Intel compiler
// (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827) // (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827)
virtual ~AbstractBase() {}; // NOLINT(modernize-use-equals-default) virtual ~AbstractBase() {} // NOLINT(modernize-use-equals-default)
virtual unsigned int func() = 0; virtual unsigned int func() = 0;
}; };
m.def("func_accepting_func_accepting_base", [](std::function<double(AbstractBase&)>) { }); m.def("func_accepting_func_accepting_base",
[](const std::function<double(AbstractBase &)> &) {});
struct MovableObject { struct MovableObject {
bool valid = true; bool valid = true;
@ -133,8 +134,8 @@ TEST_SUBMODULE(callbacks, m) {
MovableObject() = default; MovableObject() = default;
MovableObject(const MovableObject &) = default; MovableObject(const MovableObject &) = default;
MovableObject &operator=(const MovableObject &) = default; MovableObject &operator=(const MovableObject &) = default;
MovableObject(MovableObject &&o) : valid(o.valid) { o.valid = false; } MovableObject(MovableObject &&o) noexcept : valid(o.valid) { o.valid = false; }
MovableObject &operator=(MovableObject &&o) { MovableObject &operator=(MovableObject &&o) noexcept {
valid = o.valid; valid = o.valid;
o.valid = false; o.valid = false;
return *this; return *this;
@ -143,7 +144,7 @@ TEST_SUBMODULE(callbacks, m) {
py::class_<MovableObject>(m, "MovableObject"); py::class_<MovableObject>(m, "MovableObject");
// test_movable_object // test_movable_object
m.def("callback_with_movable", [](std::function<void(MovableObject &)> f) { m.def("callback_with_movable", [](const std::function<void(MovableObject &)> &f) {
auto x = MovableObject(); auto x = MovableObject();
f(x); // lvalue reference shouldn't move out object f(x); // lvalue reference shouldn't move out object
return x.valid; // must still return `true` return x.valid; // must still return `true`
@ -155,9 +156,15 @@ TEST_SUBMODULE(callbacks, m) {
.def(py::init<>()) .def(py::init<>())
.def("triple", [](CppBoundMethodTest &, int val) { return 3 * val; }); .def("triple", [](CppBoundMethodTest &, int val) { return 3 * val; });
// This checks that builtin functions can be passed as callbacks
// rather than throwing RuntimeError due to trying to extract as capsule
m.def("test_sum_builtin", [](const std::function<double(py::iterable)> &sum_builtin, const py::iterable &i) {
return sum_builtin(i);
});
// test async Python callbacks // test async Python callbacks
using callback_f = std::function<void(int)>; using callback_f = std::function<void(int)>;
m.def("test_async_callback", [](callback_f f, py::list work) { m.def("test_async_callback", [](const callback_f &f, const py::list &work) {
// make detached thread that calls `f` with piece of work after a little delay // make detached thread that calls `f` with piece of work after a little delay
auto start_f = [f](int j) { auto start_f = [f](int j) {
auto invoke_f = [f, j] { auto invoke_f = [f, j] {
@ -172,4 +179,10 @@ TEST_SUBMODULE(callbacks, m) {
for (auto i : work) for (auto i : work)
start_f(py::cast<int>(i)); start_f(py::cast<int>(i));
}); });
m.def("callback_num_times", [](const py::function &f, std::size_t num) {
for (std::size_t i = 0; i < num; i++) {
f();
}
});
} }

View File

@ -2,6 +2,8 @@
import pytest import pytest
from pybind11_tests import callbacks as m from pybind11_tests import callbacks as m
from threading import Thread from threading import Thread
import time
import env # NOQA: F401
def test_callbacks(): def test_callbacks():
@ -92,6 +94,10 @@ def test_cpp_function_roundtrip():
m.test_dummy_function(m.roundtrip(m.dummy_function)) m.test_dummy_function(m.roundtrip(m.dummy_function))
== "matches dummy_function: eval(1) = 2" == "matches dummy_function: eval(1) = 2"
) )
assert (
m.test_dummy_function(m.dummy_function_overloaded)
== "matches dummy_function: eval(1) = 2"
)
assert m.roundtrip(None, expect_none=True) is None assert m.roundtrip(None, expect_none=True) is None
assert ( assert (
m.test_dummy_function(lambda x: x + 2) m.test_dummy_function(lambda x: x + 2)
@ -119,6 +125,16 @@ def test_movable_object():
assert m.callback_with_movable(lambda _: None) is True assert m.callback_with_movable(lambda _: None) is True
@pytest.mark.skipif(
"env.PYPY",
reason="PyPy segfaults on here. See discussion on #1413.",
)
def test_python_builtins():
"""Test if python builtins like sum() can be used as callbacks"""
assert m.test_sum_builtin(sum, [1, 2, 3]) == 6
assert m.test_sum_builtin(sum, []) == 0
def test_async_callbacks(): def test_async_callbacks():
# serves as state for async callback # serves as state for async callback
class Item: class Item:
@ -139,10 +155,41 @@ def test_async_callbacks():
from time import sleep from time import sleep
sleep(0.5) sleep(0.5)
assert sum(res) == sum([x + 3 for x in work]) assert sum(res) == sum(x + 3 for x in work)
def test_async_async_callbacks(): def test_async_async_callbacks():
t = Thread(target=test_async_callbacks) t = Thread(target=test_async_callbacks)
t.start() t.start()
t.join() t.join()
def test_callback_num_times():
# Super-simple micro-benchmarking related to PR #2919.
# Example runtimes (Intel Xeon 2.2GHz, fully optimized):
# num_millions 1, repeats 2: 0.1 secs
# num_millions 20, repeats 10: 11.5 secs
one_million = 1000000
num_millions = 1 # Try 20 for actual micro-benchmarking.
repeats = 2 # Try 10.
rates = []
for rep in range(repeats):
t0 = time.time()
m.callback_num_times(lambda: None, num_millions * one_million)
td = time.time() - t0
rate = num_millions / td if td else 0
rates.append(rate)
if not rep:
print()
print(
"callback_num_times: {:d} million / {:.3f} seconds = {:.3f} million / second".format(
num_millions, td, rate
)
)
if len(rates) > 1:
print("Min Mean Max")
print(
"{:6.3f} {:6.3f} {:6.3f}".format(
min(rates), sum(rates) / len(rates), max(rates)
)
)

View File

@ -39,9 +39,7 @@ def test_chrono_system_clock_roundtrip():
# They should be identical (no information lost on roundtrip) # They should be identical (no information lost on roundtrip)
diff = abs(date1 - date2) diff = abs(date1 - date2)
assert diff.days == 0 assert diff == datetime.timedelta(0)
assert diff.seconds == 0
assert diff.microseconds == 0
def test_chrono_system_clock_roundtrip_date(): def test_chrono_system_clock_roundtrip_date():
@ -64,9 +62,7 @@ def test_chrono_system_clock_roundtrip_date():
assert diff.microseconds == 0 assert diff.microseconds == 0
# Year, Month & Day should be the same after the round trip # Year, Month & Day should be the same after the round trip
assert date1.year == date2.year assert date1 == date2
assert date1.month == date2.month
assert date1.day == date2.day
# There should be no time information # There should be no time information
assert time2.hour == 0 assert time2.hour == 0
@ -117,10 +113,7 @@ def test_chrono_system_clock_roundtrip_time(time1, tz, monkeypatch):
assert isinstance(time2, datetime.time) assert isinstance(time2, datetime.time)
# Hour, Minute, Second & Microsecond should be the same after the round trip # Hour, Minute, Second & Microsecond should be the same after the round trip
assert time1.hour == time2.hour assert time1 == time2
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) # There should be no date information (i.e. date = python base date)
assert date2.year == 1970 assert date2.year == 1970
@ -140,9 +133,13 @@ def test_chrono_duration_roundtrip():
cpp_diff = m.test_chrono3(diff) cpp_diff = m.test_chrono3(diff)
assert cpp_diff.days == diff.days assert cpp_diff == diff
assert cpp_diff.seconds == diff.seconds
assert cpp_diff.microseconds == diff.microseconds # Negative timedelta roundtrip
diff = datetime.timedelta(microseconds=-1)
cpp_diff = m.test_chrono3(diff)
assert cpp_diff == diff
def test_chrono_duration_subtraction_equivalence(): def test_chrono_duration_subtraction_equivalence():
@ -153,9 +150,7 @@ def test_chrono_duration_subtraction_equivalence():
diff = date2 - date1 diff = date2 - date1
cpp_diff = m.test_chrono4(date2, date1) cpp_diff = m.test_chrono4(date2, date1)
assert cpp_diff.days == diff.days assert cpp_diff == diff
assert cpp_diff.seconds == diff.seconds
assert cpp_diff.microseconds == diff.microseconds
def test_chrono_duration_subtraction_equivalence_date(): def test_chrono_duration_subtraction_equivalence_date():
@ -166,9 +161,7 @@ def test_chrono_duration_subtraction_equivalence_date():
diff = date2 - date1 diff = date2 - date1
cpp_diff = m.test_chrono4(date2, date1) cpp_diff = m.test_chrono4(date2, date1)
assert cpp_diff.days == diff.days assert cpp_diff == diff
assert cpp_diff.seconds == diff.seconds
assert cpp_diff.microseconds == diff.microseconds
def test_chrono_steady_clock(): def test_chrono_steady_clock():
@ -183,9 +176,7 @@ def test_chrono_steady_clock_roundtrip():
assert isinstance(time2, datetime.timedelta) assert isinstance(time2, datetime.timedelta)
# They should be identical (no information lost on roundtrip) # They should be identical (no information lost on roundtrip)
assert time1.days == time2.days assert time1 == time2
assert time1.seconds == time2.seconds
assert time1.microseconds == time2.microseconds
def test_floating_point_duration(): def test_floating_point_duration():

View File

@ -19,6 +19,8 @@
#include "local_bindings.h" #include "local_bindings.h"
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include <utility>
#if defined(_MSC_VER) #if defined(_MSC_VER)
# pragma warning(disable: 4324) // warning C4324: structure was padded due to alignment specifier # pragma warning(disable: 4324) // warning C4324: structure was padded due to alignment specifier
#endif #endif
@ -129,7 +131,7 @@ TEST_SUBMODULE(class_, m) {
m.def("return_none", []() -> BaseClass* { return nullptr; }); m.def("return_none", []() -> BaseClass* { return nullptr; });
// test_isinstance // test_isinstance
m.def("check_instances", [](py::list l) { m.def("check_instances", [](const py::list &l) {
return py::make_tuple( return py::make_tuple(
py::isinstance<py::tuple>(l[0]), py::isinstance<py::tuple>(l[0]),
py::isinstance<py::dict>(l[1]), py::isinstance<py::dict>(l[1]),
@ -151,21 +153,16 @@ TEST_SUBMODULE(class_, m) {
// return py::type::of<int>(); // return py::type::of<int>();
if (category == 1) if (category == 1)
return py::type::of<DerivedClass1>(); return py::type::of<DerivedClass1>();
else
return py::type::of<Invalid>(); return py::type::of<Invalid>();
}); });
m.def("get_type_of", [](py::object ob) { m.def("get_type_of", [](py::object ob) { return py::type::of(std::move(ob)); });
return py::type::of(ob);
});
m.def("get_type_classic", [](py::handle h) { m.def("get_type_classic", [](py::handle h) {
return h.get_type(); return h.get_type();
}); });
m.def("as_type", [](py::object ob) { m.def("as_type", [](const py::object &ob) { return py::type(ob); });
return py::type(ob);
});
// test_mismatched_holder // test_mismatched_holder
struct MismatchBase1 { }; struct MismatchBase1 { };
@ -219,7 +216,7 @@ TEST_SUBMODULE(class_, m) {
py::implicitly_convertible<UserType, ConvertibleFromUserType>(); py::implicitly_convertible<UserType, ConvertibleFromUserType>();
m.def("implicitly_convert_argument", [](const ConvertibleFromUserType &r) { return r.i; }); m.def("implicitly_convert_argument", [](const ConvertibleFromUserType &r) { return r.i; });
m.def("implicitly_convert_variable", [](py::object o) { m.def("implicitly_convert_variable", [](const py::object &o) {
// `o` is `UserType` and `r` is a reference to a temporary created by implicit // `o` is `UserType` and `r` is a reference to a temporary created by implicit
// conversion. This is valid when called inside a bound function because the temp // conversion. This is valid when called inside a bound function because the temp
// object is attached to the same life support system as the arguments. // object is attached to the same life support system as the arguments.
@ -397,7 +394,7 @@ TEST_SUBMODULE(class_, m) {
struct StringWrapper { std::string str; }; struct StringWrapper { std::string str; };
m.def("test_error_after_conversions", [](int) {}); m.def("test_error_after_conversions", [](int) {});
m.def("test_error_after_conversions", m.def("test_error_after_conversions",
[](StringWrapper) -> NotRegistered { return {}; }); [](const StringWrapper &) -> NotRegistered { return {}; });
py::class_<StringWrapper>(m, "StringWrapper").def(py::init<std::string>()); py::class_<StringWrapper>(m, "StringWrapper").def(py::init<std::string>());
py::implicitly_convertible<std::string, StringWrapper>(); py::implicitly_convertible<std::string, StringWrapper>();
@ -461,19 +458,20 @@ TEST_SUBMODULE(class_, m) {
struct OtherDuplicate {}; struct OtherDuplicate {};
struct DuplicateNested {}; struct DuplicateNested {};
struct OtherDuplicateNested {}; struct OtherDuplicateNested {};
m.def("register_duplicate_class_name", [](py::module_ m) {
m.def("register_duplicate_class_name", [](const py::module_ &m) {
py::class_<Duplicate>(m, "Duplicate"); py::class_<Duplicate>(m, "Duplicate");
py::class_<OtherDuplicate>(m, "Duplicate"); py::class_<OtherDuplicate>(m, "Duplicate");
}); });
m.def("register_duplicate_class_type", [](py::module_ m) { m.def("register_duplicate_class_type", [](const py::module_ &m) {
py::class_<OtherDuplicate>(m, "OtherDuplicate"); py::class_<OtherDuplicate>(m, "OtherDuplicate");
py::class_<OtherDuplicate>(m, "YetAnotherDuplicate"); py::class_<OtherDuplicate>(m, "YetAnotherDuplicate");
}); });
m.def("register_duplicate_nested_class_name", [](py::object gt) { m.def("register_duplicate_nested_class_name", [](const py::object &gt) {
py::class_<DuplicateNested>(gt, "DuplicateNested"); py::class_<DuplicateNested>(gt, "DuplicateNested");
py::class_<OtherDuplicateNested>(gt, "DuplicateNested"); py::class_<OtherDuplicateNested>(gt, "DuplicateNested");
}); });
m.def("register_duplicate_nested_class_type", [](py::object gt) { m.def("register_duplicate_nested_class_type", [](const py::object &gt) {
py::class_<OtherDuplicateNested>(gt, "OtherDuplicateNested"); py::class_<OtherDuplicateNested>(gt, "OtherDuplicateNested");
py::class_<OtherDuplicateNested>(gt, "YetAnotherDuplicateNested"); py::class_<OtherDuplicateNested>(gt, "YetAnotherDuplicateNested");
}); });

View File

@ -321,7 +321,7 @@ def test_bind_protected_functions():
def test_brace_initialization(): def test_brace_initialization():
""" Tests that simple POD classes can be constructed using C++11 brace initialization """ """Tests that simple POD classes can be constructed using C++11 brace initialization"""
a = m.BraceInitialization(123, "test") a = m.BraceInitialization(123, "test")
assert a.field1 == 123 assert a.field1 == 123
assert a.field2 == "test" assert a.field2 == "test"

View File

@ -1,5 +1,6 @@
/* /*
tests/test_constants_and_functions.cpp -- global constants and functions, enumerations, raw byte strings tests/test_constants_and_functions.cpp -- global constants and functions, enumerations, raw
byte strings
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch> Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
@ -33,7 +34,7 @@ py::bytes return_bytes() {
return std::string(data, 4); return std::string(data, 4);
} }
std::string print_bytes(py::bytes bytes) { std::string print_bytes(const py::bytes &bytes) {
std::string ret = "bytes["; std::string ret = "bytes[";
const auto value = static_cast<std::string>(bytes); const auto value = static_cast<std::string>(bytes);
for (size_t i = 0; i < value.length(); ++i) { for (size_t i = 0; i < value.length(); ++i) {
@ -56,12 +57,13 @@ int f1(int x) noexcept { return x+1; }
#endif #endif
int f2(int x) noexcept(true) { return x+2; } int f2(int x) noexcept(true) { return x+2; }
int f3(int x) noexcept(false) { return x+3; } int f3(int x) noexcept(false) { return x+3; }
#if defined(__GNUG__) #if defined(__GNUG__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic push # pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated" # pragma GCC diagnostic ignored "-Wdeprecated"
#endif #endif
// NOLINTNEXTLINE(modernize-use-noexcept)
int f4(int x) throw() { return x+4; } // Deprecated equivalent to noexcept(true) int f4(int x) throw() { return x+4; } // Deprecated equivalent to noexcept(true)
#if defined(__GNUG__) #if defined(__GNUG__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic pop # pragma GCC diagnostic pop
#endif #endif
struct C { struct C {
@ -71,13 +73,15 @@ struct C {
int m4(int x) const noexcept(true) { return x-4; } int m4(int x) const noexcept(true) { return x-4; }
int m5(int x) noexcept(false) { return x-5; } int m5(int x) noexcept(false) { return x-5; }
int m6(int x) const noexcept(false) { return x-6; } int m6(int x) const noexcept(false) { return x-6; }
#if defined(__GNUG__) #if defined(__GNUG__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic push # pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated" # pragma GCC diagnostic ignored "-Wdeprecated"
#endif #endif
int m7(int x) throw() { return x-7; } // NOLINTNEXTLINE(modernize-use-noexcept)
int m8(int x) const throw() { return x-8; } int m7(int x) throw() { return x - 7; }
#if defined(__GNUG__) // NOLINTNEXTLINE(modernize-use-noexcept)
int m8(int x) const throw() { return x - 8; }
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic pop # pragma GCC diagnostic pop
#endif #endif
}; };
@ -129,7 +133,14 @@ TEST_SUBMODULE(constants_and_functions, m) {
; ;
m.def("f1", f1); m.def("f1", f1);
m.def("f2", f2); m.def("f2", f2);
#if defined(__INTEL_COMPILER)
# pragma warning push
# pragma warning disable 878 // incompatible exception specifications
#endif
m.def("f3", f3); m.def("f3", f3);
#if defined(__INTEL_COMPILER)
# pragma warning pop
#endif
m.def("f4", f4); m.def("f4", f4);
// test_function_record_leaks // test_function_record_leaks
@ -142,8 +153,13 @@ TEST_SUBMODULE(constants_and_functions, m) {
LargeCapture capture; // VS 2015's MSVC is acting up if we create the array here LargeCapture capture; // VS 2015's MSVC is acting up if we create the array here
m.def("should_raise", [capture](int) { return capture.zeros[9] + 33; }, py::kw_only(), py::arg()); m.def("should_raise", [capture](int) { return capture.zeros[9] + 33; }, py::kw_only(), py::arg());
}); });
m.def("register_with_raising_repr", [](py::module_ m, py::object default_value) { m.def("register_with_raising_repr", [](py::module_ m, const py::object &default_value) {
m.def("should_raise", [](int, int, py::object) { return 42; }, "some docstring", m.def(
py::arg_v("x", 42), py::arg_v("y", 42, "<the answer>"), py::arg_v("z", default_value)); "should_raise",
[](int, int, const py::object &) { return 42; },
"some docstring",
py::arg_v("x", 42),
py::arg_v("y", 42, "<the answer>"),
py::arg_v("z", default_value));
}); });
} }

View File

@ -37,9 +37,16 @@ template <> lacking_move_ctor empty<lacking_move_ctor>::instance_ = {};
class MoveOnlyInt { class MoveOnlyInt {
public: public:
MoveOnlyInt() { print_default_created(this); } MoveOnlyInt() { print_default_created(this); }
MoveOnlyInt(int v) : value{std::move(v)} { print_created(this, value); } MoveOnlyInt(int v) : value{v} { print_created(this, value); }
MoveOnlyInt(MoveOnlyInt &&m) { print_move_created(this, m.value); std::swap(value, m.value); } MoveOnlyInt(MoveOnlyInt &&m) noexcept {
MoveOnlyInt &operator=(MoveOnlyInt &&m) { print_move_assigned(this, m.value); std::swap(value, m.value); return *this; } print_move_created(this, m.value);
std::swap(value, m.value);
}
MoveOnlyInt &operator=(MoveOnlyInt &&m) noexcept {
print_move_assigned(this, m.value);
std::swap(value, m.value);
return *this;
}
MoveOnlyInt(const MoveOnlyInt &) = delete; MoveOnlyInt(const MoveOnlyInt &) = delete;
MoveOnlyInt &operator=(const MoveOnlyInt &) = delete; MoveOnlyInt &operator=(const MoveOnlyInt &) = delete;
~MoveOnlyInt() { print_destroyed(this); } ~MoveOnlyInt() { print_destroyed(this); }
@ -49,9 +56,16 @@ public:
class MoveOrCopyInt { class MoveOrCopyInt {
public: public:
MoveOrCopyInt() { print_default_created(this); } MoveOrCopyInt() { print_default_created(this); }
MoveOrCopyInt(int v) : value{std::move(v)} { print_created(this, value); } MoveOrCopyInt(int v) : value{v} { print_created(this, value); }
MoveOrCopyInt(MoveOrCopyInt &&m) { print_move_created(this, m.value); std::swap(value, m.value); } MoveOrCopyInt(MoveOrCopyInt &&m) noexcept {
MoveOrCopyInt &operator=(MoveOrCopyInt &&m) { print_move_assigned(this, m.value); std::swap(value, m.value); return *this; } print_move_created(this, m.value);
std::swap(value, m.value);
}
MoveOrCopyInt &operator=(MoveOrCopyInt &&m) noexcept {
print_move_assigned(this, m.value);
std::swap(value, m.value);
return *this;
}
MoveOrCopyInt(const MoveOrCopyInt &c) { print_copy_created(this, c.value); value = c.value; } MoveOrCopyInt(const MoveOrCopyInt &c) { print_copy_created(this, c.value); value = c.value; }
MoveOrCopyInt &operator=(const MoveOrCopyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; } MoveOrCopyInt &operator=(const MoveOrCopyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; }
~MoveOrCopyInt() { print_destroyed(this); } ~MoveOrCopyInt() { print_destroyed(this); }
@ -61,7 +75,7 @@ public:
class CopyOnlyInt { class CopyOnlyInt {
public: public:
CopyOnlyInt() { print_default_created(this); } CopyOnlyInt() { print_default_created(this); }
CopyOnlyInt(int v) : value{std::move(v)} { print_created(this, value); } CopyOnlyInt(int v) : value{v} { print_created(this, value); }
CopyOnlyInt(const CopyOnlyInt &c) { print_copy_created(this, c.value); value = c.value; } CopyOnlyInt(const CopyOnlyInt &c) { print_copy_created(this, c.value); value = c.value; }
CopyOnlyInt &operator=(const CopyOnlyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; } CopyOnlyInt &operator=(const CopyOnlyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; }
~CopyOnlyInt() { print_destroyed(this); } ~CopyOnlyInt() { print_destroyed(this); }
@ -111,7 +125,8 @@ TEST_SUBMODULE(copy_move_policies, m) {
py::return_value_policy::move); py::return_value_policy::move);
// test_move_and_copy_casts // test_move_and_copy_casts
m.def("move_and_copy_casts", [](py::object o) { // NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("move_and_copy_casts", [](const py::object &o) {
int r = 0; int r = 0;
r += py::cast<MoveOrCopyInt>(o).value; /* moves */ r += py::cast<MoveOrCopyInt>(o).value; /* moves */
r += py::cast<MoveOnlyInt>(o).value; /* moves */ r += py::cast<MoveOnlyInt>(o).value; /* moves */
@ -126,7 +141,11 @@ TEST_SUBMODULE(copy_move_policies, m) {
// test_move_and_copy_loads // test_move_and_copy_loads
m.def("move_only", [](MoveOnlyInt m) { return m.value; }); m.def("move_only", [](MoveOnlyInt m) { return m.value; });
// Changing this breaks the existing test: needs careful review.
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("move_or_copy", [](MoveOrCopyInt m) { return m.value; }); m.def("move_or_copy", [](MoveOrCopyInt m) { return m.value; });
// Changing this breaks the existing test: needs careful review.
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("copy_only", [](CopyOnlyInt m) { return m.value; }); m.def("copy_only", [](CopyOnlyInt m) { return m.value; });
m.def("move_pair", [](std::pair<MoveOnlyInt, MoveOrCopyInt> p) { m.def("move_pair", [](std::pair<MoveOnlyInt, MoveOrCopyInt> p) {
return p.first.value + p.second.value; return p.first.value + p.second.value;
@ -186,7 +205,6 @@ TEST_SUBMODULE(copy_move_policies, m) {
void *ptr = std::malloc(bytes); void *ptr = std::malloc(bytes);
if (ptr) if (ptr)
return ptr; return ptr;
else
throw std::bad_alloc{}; throw std::bad_alloc{};
} }
}; };

View File

@ -67,9 +67,12 @@ public:
DestructionTester() { print_default_created(this); } DestructionTester() { print_default_created(this); }
~DestructionTester() { print_destroyed(this); } ~DestructionTester() { print_destroyed(this); }
DestructionTester(const DestructionTester &) { print_copy_created(this); } DestructionTester(const DestructionTester &) { print_copy_created(this); }
DestructionTester(DestructionTester &&) { print_move_created(this); } DestructionTester(DestructionTester &&) noexcept { print_move_created(this); }
DestructionTester &operator=(const DestructionTester &) { print_copy_assigned(this); return *this; } DestructionTester &operator=(const DestructionTester &) { print_copy_assigned(this); return *this; }
DestructionTester &operator=(DestructionTester &&) { print_move_assigned(this); return *this; } DestructionTester &operator=(DestructionTester &&) noexcept {
print_move_assigned(this);
return *this;
}
}; };
namespace pybind11 { namespace detail { namespace pybind11 { namespace detail {
template <> struct type_caster<DestructionTester> { template <> struct type_caster<DestructionTester> {
@ -94,7 +97,11 @@ TEST_SUBMODULE(custom_type_casters, m) {
class ArgInspector { class ArgInspector {
public: public:
ArgInspector1 f(ArgInspector1 a, ArgAlwaysConverts) { return a; } ArgInspector1 f(ArgInspector1 a, ArgAlwaysConverts) { return a; }
std::string g(ArgInspector1 a, const ArgInspector1 &b, int c, ArgInspector2 *d, ArgAlwaysConverts) { std::string g(const ArgInspector1 &a,
const ArgInspector1 &b,
int c,
ArgInspector2 *d,
ArgAlwaysConverts) {
return a.arg + "\n" + b.arg + "\n" + std::to_string(c) + "\n" + d->arg; return a.arg + "\n" + b.arg + "\n" + std::to_string(c) + "\n" + d->arg;
} }
static ArgInspector2 h(ArgInspector2 a, ArgAlwaysConverts) { return a; } static ArgInspector2 h(ArgInspector2 a, ArgAlwaysConverts) { return a; }
@ -106,8 +113,14 @@ TEST_SUBMODULE(custom_type_casters, m) {
.def("g", &ArgInspector::g, "a"_a.noconvert(), "b"_a, "c"_a.noconvert()=13, "d"_a=ArgInspector2(), py::arg() = ArgAlwaysConverts()) .def("g", &ArgInspector::g, "a"_a.noconvert(), "b"_a, "c"_a.noconvert()=13, "d"_a=ArgInspector2(), py::arg() = ArgAlwaysConverts())
.def_static("h", &ArgInspector::h, py::arg{}.noconvert(), py::arg() = ArgAlwaysConverts()) .def_static("h", &ArgInspector::h, py::arg{}.noconvert(), py::arg() = ArgAlwaysConverts())
; ;
m.def("arg_inspect_func", [](ArgInspector2 a, ArgInspector1 b, ArgAlwaysConverts) { return a.arg + "\n" + b.arg; }, m.def(
py::arg{}.noconvert(false), py::arg_v(nullptr, ArgInspector1()).noconvert(true), py::arg() = ArgAlwaysConverts()); "arg_inspect_func",
[](const ArgInspector2 &a, const ArgInspector1 &b, ArgAlwaysConverts) {
return a.arg + "\n" + b.arg;
},
py::arg{}.noconvert(false),
py::arg_v(nullptr, ArgInspector1()).noconvert(true),
py::arg() = ArgAlwaysConverts());
m.def("floats_preferred", [](double f) { return 0.5 * f; }, "f"_a); m.def("floats_preferred", [](double f) { return 0.5 * f; }, "f"_a);
m.def("floats_only", [](double f) { return 0.5 * f; }, "f"_a.noconvert()); m.def("floats_only", [](double f) { return 0.5 * f; }, "f"_a.noconvert());

View File

@ -54,8 +54,7 @@ void reset_refs() {
} }
// Returns element 2,1 from a matrix (used to test copy/nocopy) // Returns element 2,1 from a matrix (used to test copy/nocopy)
double get_elem(Eigen::Ref<const Eigen::MatrixXd> m) { return m(2, 1); }; double get_elem(const Eigen::Ref<const Eigen::MatrixXd> &m) { return m(2, 1); };
// Returns a matrix with 10*r + 100*c added to each matrix element (to help test that the matrix // Returns a matrix with 10*r + 100*c added to each matrix element (to help test that the matrix
// reference is referencing rows/columns correctly). // reference is referencing rows/columns correctly).
@ -94,15 +93,18 @@ TEST_SUBMODULE(eigen, m) {
m.def("double_complex", [](const Eigen::VectorXcf &x) -> Eigen::VectorXcf { return 2.0f * x; }); m.def("double_complex", [](const Eigen::VectorXcf &x) -> Eigen::VectorXcf { return 2.0f * x; });
m.def("double_threec", [](py::EigenDRef<Eigen::Vector3f> x) { x *= 2; }); m.def("double_threec", [](py::EigenDRef<Eigen::Vector3f> x) { x *= 2; });
m.def("double_threer", [](py::EigenDRef<Eigen::RowVector3f> x) { x *= 2; }); m.def("double_threer", [](py::EigenDRef<Eigen::RowVector3f> x) { x *= 2; });
m.def("double_mat_cm", [](Eigen::MatrixXf x) -> Eigen::MatrixXf { return 2.0f * x; }); m.def("double_mat_cm", [](const Eigen::MatrixXf &x) -> Eigen::MatrixXf { return 2.0f * x; });
m.def("double_mat_rm", [](DenseMatrixR x) -> DenseMatrixR { return 2.0f * x; }); m.def("double_mat_rm", [](const DenseMatrixR &x) -> DenseMatrixR { return 2.0f * x; });
// test_eigen_ref_to_python // test_eigen_ref_to_python
// Different ways of passing via Eigen::Ref; the first and second are the Eigen-recommended // Different ways of passing via Eigen::Ref; the first and second are the Eigen-recommended
m.def("cholesky1", [](Eigen::Ref<MatrixXdR> x) -> Eigen::MatrixXd { return x.llt().matrixL(); }); m.def("cholesky1",
[](const Eigen::Ref<MatrixXdR> &x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
m.def("cholesky2", [](const Eigen::Ref<const MatrixXdR> &x) -> Eigen::MatrixXd { return x.llt().matrixL(); }); m.def("cholesky2", [](const Eigen::Ref<const MatrixXdR> &x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
m.def("cholesky3", [](const Eigen::Ref<MatrixXdR> &x) -> Eigen::MatrixXd { return x.llt().matrixL(); }); m.def("cholesky3", [](const Eigen::Ref<MatrixXdR> &x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
m.def("cholesky4", [](Eigen::Ref<const MatrixXdR> x) -> Eigen::MatrixXd { return x.llt().matrixL(); }); m.def("cholesky4", [](const Eigen::Ref<const MatrixXdR> &x) -> Eigen::MatrixXd {
return x.llt().matrixL();
});
// test_eigen_ref_mutators // test_eigen_ref_mutators
// Mutators: these add some value to the given element using Eigen, but Eigen should be mapping into // Mutators: these add some value to the given element using Eigen, but Eigen should be mapping into
@ -247,16 +249,19 @@ TEST_SUBMODULE(eigen, m) {
m.def("fixed_copy_r", [](const FixedMatrixR &m) -> FixedMatrixR { return m; }); m.def("fixed_copy_r", [](const FixedMatrixR &m) -> FixedMatrixR { return m; });
m.def("fixed_copy_c", [](const FixedMatrixC &m) -> FixedMatrixC { return m; }); m.def("fixed_copy_c", [](const FixedMatrixC &m) -> FixedMatrixC { return m; });
// test_mutator_descriptors // test_mutator_descriptors
m.def("fixed_mutator_r", [](Eigen::Ref<FixedMatrixR>) {}); m.def("fixed_mutator_r", [](const Eigen::Ref<FixedMatrixR> &) {});
m.def("fixed_mutator_c", [](Eigen::Ref<FixedMatrixC>) {}); m.def("fixed_mutator_c", [](const Eigen::Ref<FixedMatrixC> &) {});
m.def("fixed_mutator_a", [](py::EigenDRef<FixedMatrixC>) {}); m.def("fixed_mutator_a", [](const py::EigenDRef<FixedMatrixC> &) {});
// test_dense // test_dense
m.def("dense_r", [mat]() -> DenseMatrixR { return DenseMatrixR(mat); }); m.def("dense_r", [mat]() -> DenseMatrixR { return DenseMatrixR(mat); });
m.def("dense_c", [mat]() -> DenseMatrixC { return DenseMatrixC(mat); }); m.def("dense_c", [mat]() -> DenseMatrixC { return DenseMatrixC(mat); });
m.def("dense_copy_r", [](const DenseMatrixR &m) -> DenseMatrixR { return m; }); m.def("dense_copy_r", [](const DenseMatrixR &m) -> DenseMatrixR { return m; });
m.def("dense_copy_c", [](const DenseMatrixC &m) -> DenseMatrixC { return m; }); m.def("dense_copy_c", [](const DenseMatrixC &m) -> DenseMatrixC { return m; });
// test_sparse, test_sparse_signature // test_sparse, test_sparse_signature
m.def("sparse_r", [mat]() -> SparseMatrixR { return Eigen::SparseView<Eigen::MatrixXf>(mat); }); //NOLINT(clang-analyzer-core.uninitialized.UndefReturn) m.def("sparse_r", [mat]() -> SparseMatrixR {
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
return Eigen::SparseView<Eigen::MatrixXf>(mat);
});
m.def("sparse_c", [mat]() -> SparseMatrixC { return Eigen::SparseView<Eigen::MatrixXf>(mat); }); m.def("sparse_c", [mat]() -> SparseMatrixC { return Eigen::SparseView<Eigen::MatrixXf>(mat); });
m.def("sparse_copy_r", [](const SparseMatrixR &m) -> SparseMatrixR { return m; }); m.def("sparse_copy_r", [](const SparseMatrixR &m) -> SparseMatrixR { return m; });
m.def("sparse_copy_c", [](const SparseMatrixC &m) -> SparseMatrixC { return m; }); m.def("sparse_copy_c", [](const SparseMatrixC &m) -> SparseMatrixC { return m; });
@ -280,7 +285,9 @@ TEST_SUBMODULE(eigen, m) {
// that would allow copying (if types or strides don't match) for comparison: // that would allow copying (if types or strides don't match) for comparison:
m.def("get_elem", &get_elem); m.def("get_elem", &get_elem);
// Now this alternative that calls the tells pybind to fail rather than copy: // Now this alternative that calls the tells pybind to fail rather than copy:
m.def("get_elem_nocopy", [](Eigen::Ref<const Eigen::MatrixXd> m) -> double { return get_elem(m); }, m.def(
"get_elem_nocopy",
[](const Eigen::Ref<const Eigen::MatrixXd> &m) -> double { return get_elem(m); },
py::arg{}.noconvert()); py::arg{}.noconvert());
// Also test a row-major-only no-copy const ref: // Also test a row-major-only no-copy const ref:
m.def("get_elem_rm_nocopy", [](Eigen::Ref<const Eigen::Matrix<long, -1, -1, Eigen::RowMajor>> &m) -> long { return m(2, 1); }, m.def("get_elem_rm_nocopy", [](Eigen::Ref<const Eigen::Matrix<long, -1, -1, Eigen::RowMajor>> &m) -> long { return m(2, 1); },
@ -295,18 +302,23 @@ TEST_SUBMODULE(eigen, m) {
// test_issue1105 // test_issue1105
// Issue #1105: when converting from a numpy two-dimensional (Nx1) or (1xN) value into a dense // 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: // eigen Vector or RowVector, the argument would fail to load because the numpy copy would
// numpy won't broadcast a Nx1 into a 1-dimensional vector. // fail: numpy won't broadcast a Nx1 into a 1-dimensional vector.
m.def("iss1105_col", [](Eigen::VectorXd) { return true; }); m.def("iss1105_col", [](const Eigen::VectorXd &) { return true; });
m.def("iss1105_row", [](Eigen::RowVectorXd) { return true; }); m.def("iss1105_row", [](const Eigen::RowVectorXd &) { return true; });
// test_named_arguments // test_named_arguments
// Make sure named arguments are working properly: // 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) m.def(
-> Eigen::MatrixXd { "matrix_multiply",
if (A.cols() != B.rows()) throw std::domain_error("Nonconformable matrices!"); [](const py::EigenDRef<const Eigen::MatrixXd> &A,
const py::EigenDRef<const Eigen::MatrixXd> &B) -> Eigen::MatrixXd {
if (A.cols() != B.rows())
throw std::domain_error("Nonconformable matrices!");
return A * B; return A * B;
}, py::arg("A"), py::arg("B")); },
py::arg("A"),
py::arg("B"));
// test_custom_operator_new // test_custom_operator_new
py::class_<CustomOperatorNew>(m, "CustomOperatorNew") py::class_<CustomOperatorNew>(m, "CustomOperatorNew")
@ -318,7 +330,7 @@ TEST_SUBMODULE(eigen, m) {
// In case of a failure (the caster's temp array does not live long enough), creating // In case of a failure (the caster's temp array does not live long enough), creating
// a new array (np.ones(10)) increases the chances that the temp array will be garbage // a new array (np.ones(10)) increases the chances that the temp array will be garbage
// collected and/or that its memory will be overridden with different values. // collected and/or that its memory will be overridden with different values.
m.def("get_elem_direct", [](Eigen::Ref<const Eigen::VectorXd> v) { m.def("get_elem_direct", [](const Eigen::Ref<const Eigen::VectorXd> &v) {
py::module_::import("numpy").attr("ones")(10); py::module_::import("numpy").attr("ones")(10);
return v(5); return v(5);
}); });

View File

@ -8,16 +8,17 @@
#include <catch.hpp> #include <catch.hpp>
#include <thread>
#include <fstream> #include <fstream>
#include <functional> #include <functional>
#include <thread>
#include <utility>
namespace py = pybind11; namespace py = pybind11;
using namespace py::literals; using namespace py::literals;
class Widget { class Widget {
public: public:
Widget(std::string message) : message(message) { } Widget(std::string message) : message(std::move(message)) {}
virtual ~Widget() = default; virtual ~Widget() = default;
std::string the_message() const { return message; } std::string the_message() const { return message; }
@ -102,7 +103,7 @@ bool has_pybind11_internals_builtin() {
bool has_pybind11_internals_static() { bool has_pybind11_internals_static() {
auto **&ipp = py::detail::get_internals_pp(); auto **&ipp = py::detail::get_internals_pp();
return ipp && *ipp; return (ipp != nullptr) && (*ipp != nullptr);
} }
TEST_CASE("Restart the interpreter") { TEST_CASE("Restart the interpreter") {

View File

@ -9,7 +9,9 @@
#include <pybind11/eval.h> #include <pybind11/eval.h>
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include <utility>
TEST_SUBMODULE(eval_, m) { TEST_SUBMODULE(eval_, m) {
// test_evals // test_evals
@ -64,10 +66,10 @@ TEST_SUBMODULE(eval_, m) {
auto local = py::dict(); auto local = py::dict();
local["y"] = py::int_(43); local["y"] = py::int_(43);
int val_out; int val_out = 0;
local["call_test2"] = py::cpp_function([&](int value) { val_out = value; }); local["call_test2"] = py::cpp_function([&](int value) { val_out = value; });
auto result = py::eval_file(filename, global, local); auto result = py::eval_file(std::move(filename), global, local);
return val_out == 43 && result.is_none(); return val_out == 43 && result.is_none();
}); });

View File

@ -7,7 +7,10 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "test_exceptions.h"
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include <utility>
// A type that should be raised as an exception in Python // A type that should be raised as an exception in Python
class MyException : public std::exception { class MyException : public std::exception {
@ -198,26 +201,29 @@ TEST_SUBMODULE(exceptions, m) {
throw py::error_already_set(); throw py::error_already_set();
}); });
m.def("python_call_in_destructor", [](py::dict d) { m.def("python_call_in_destructor", [](const py::dict &d) {
bool retval = false;
try { try {
PythonCallInDestructor set_dict_in_destructor(d); PythonCallInDestructor set_dict_in_destructor(d);
PyErr_SetString(PyExc_ValueError, "foo"); PyErr_SetString(PyExc_ValueError, "foo");
throw py::error_already_set(); throw py::error_already_set();
} catch (const py::error_already_set&) { } catch (const py::error_already_set&) {
return true; retval = true;
} }
return false; return retval;
}); });
m.def("python_alreadyset_in_destructor", [](py::str s) { m.def("python_alreadyset_in_destructor", [](const py::str &s) {
PythonAlreadySetInDestructor alreadyset_in_destructor(s); PythonAlreadySetInDestructor alreadyset_in_destructor(s);
return true; return true;
}); });
// test_nested_throws // test_nested_throws
m.def("try_catch", [m](py::object exc_type, py::function f, py::args args) { m.def("try_catch",
try { f(*args); } [m](const py::object &exc_type, const py::function &f, const py::args &args) {
catch (py::error_already_set &ex) { try {
f(*args);
} catch (py::error_already_set &ex) {
if (ex.matches(exc_type)) if (ex.matches(exc_type))
py::print(ex.what()); py::print(ex.what());
else else
@ -228,4 +234,5 @@ TEST_SUBMODULE(exceptions, m) {
// Test repr that cannot be displayed // Test repr that cannot be displayed
m.def("simple_bool_passthrough", [](bool x) {return x;}); m.def("simple_bool_passthrough", [](bool x) {return x;});
m.def("throw_should_be_translated_to_key_error", []() { throw shared_exception(); });
} }

View File

@ -0,0 +1,12 @@
#pragma once
#include "pybind11_tests.h"
#include <stdexcept>
// shared exceptions for cross_module_tests
class PYBIND11_EXPORT_EXCEPTION shared_exception : public pybind11::builtin_exception {
public:
using builtin_exception::builtin_exception;
explicit shared_exception() : shared_exception("") {}
void set_error() const override { PyErr_SetString(PyExc_RuntimeError, what()); }
};

View File

@ -3,6 +3,8 @@ import sys
import pytest import pytest
import env # noqa: F401
from pybind11_tests import exceptions as m from pybind11_tests import exceptions as m
import pybind11_cross_module_tests as cm import pybind11_cross_module_tests as cm
@ -44,6 +46,18 @@ def test_cross_module_exceptions():
cm.throw_stop_iteration() cm.throw_stop_iteration()
# TODO: FIXME
@pytest.mark.xfail(
"env.PYPY and env.MACOS",
raises=RuntimeError,
reason="Expected failure with PyPy and libc++ (Issue #2847 & PR #2999)",
)
def test_cross_module_exception_translator():
with pytest.raises(KeyError):
# translator registered in cross_module_tests
m.throw_should_be_translated_to_key_error()
def test_python_call_in_catch(): def test_python_call_in_catch():
d = {} d = {}
assert m.python_call_in_destructor(d) is True assert m.python_call_in_destructor(d) is True

View File

@ -8,10 +8,11 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include "constructor_stats.h" #include "constructor_stats.h"
#include "pybind11_tests.h"
#include <cmath> #include <cmath>
#include <new> #include <new>
#include <utility>
// Classes for testing python construction via C++ factory function: // Classes for testing python construction via C++ factory function:
// Not publicly constructible, copyable, or movable: // Not publicly constructible, copyable, or movable:
@ -20,12 +21,12 @@ class TestFactory1 {
TestFactory1() : value("(empty)") { print_default_created(this); } TestFactory1() : value("(empty)") { print_default_created(this); }
TestFactory1(int v) : value(std::to_string(v)) { print_created(this, value); } TestFactory1(int v) : value(std::to_string(v)) { print_created(this, value); }
TestFactory1(std::string v) : value(std::move(v)) { print_created(this, value); } TestFactory1(std::string v) : value(std::move(v)) { print_created(this, value); }
public:
std::string value;
TestFactory1(TestFactory1 &&) = delete; TestFactory1(TestFactory1 &&) = delete;
TestFactory1(const TestFactory1 &) = delete; TestFactory1(const TestFactory1 &) = delete;
TestFactory1 &operator=(TestFactory1 &&) = delete; TestFactory1 &operator=(TestFactory1 &&) = delete;
TestFactory1 &operator=(const TestFactory1 &) = delete; TestFactory1 &operator=(const TestFactory1 &) = delete;
public:
std::string value;
~TestFactory1() { print_destroyed(this); } ~TestFactory1() { print_destroyed(this); }
}; };
// Non-public construction, but moveable: // Non-public construction, but moveable:
@ -35,8 +36,15 @@ class TestFactory2 {
TestFactory2(int v) : value(std::to_string(v)) { print_created(this, value); } TestFactory2(int v) : value(std::to_string(v)) { print_created(this, value); }
TestFactory2(std::string v) : value(std::move(v)) { print_created(this, value); } TestFactory2(std::string v) : value(std::move(v)) { print_created(this, value); }
public: public:
TestFactory2(TestFactory2 &&m) { value = std::move(m.value); print_move_created(this); } TestFactory2(TestFactory2 &&m) noexcept {
TestFactory2 &operator=(TestFactory2 &&m) { value = std::move(m.value); print_move_assigned(this); return *this; } value = std::move(m.value);
print_move_created(this);
}
TestFactory2 &operator=(TestFactory2 &&m) noexcept {
value = std::move(m.value);
print_move_assigned(this);
return *this;
}
std::string value; std::string value;
~TestFactory2() { print_destroyed(this); } ~TestFactory2() { print_destroyed(this); }
}; };
@ -48,8 +56,15 @@ protected:
TestFactory3(int v) : value(std::to_string(v)) { print_created(this, value); } TestFactory3(int v) : value(std::to_string(v)) { print_created(this, value); }
public: public:
TestFactory3(std::string v) : value(std::move(v)) { print_created(this, value); } TestFactory3(std::string v) : value(std::move(v)) { print_created(this, value); }
TestFactory3(TestFactory3 &&m) { value = std::move(m.value); print_move_created(this); } TestFactory3(TestFactory3 &&m) noexcept {
TestFactory3 &operator=(TestFactory3 &&m) { value = std::move(m.value); print_move_assigned(this); return *this; } value = std::move(m.value);
print_move_created(this);
}
TestFactory3 &operator=(TestFactory3 &&m) noexcept {
value = std::move(m.value);
print_move_assigned(this);
return *this;
}
std::string value; std::string value;
virtual ~TestFactory3() { print_destroyed(this); } virtual ~TestFactory3() { print_destroyed(this); }
}; };
@ -73,11 +88,15 @@ protected:
bool alias = false; bool alias = false;
public: public:
TestFactory6(int i) : value{i} { print_created(this, i); } TestFactory6(int i) : value{i} { print_created(this, i); }
TestFactory6(TestFactory6 &&f) { print_move_created(this); value = f.value; alias = f.alias; } TestFactory6(TestFactory6 &&f) noexcept {
print_move_created(this);
value = f.value;
alias = f.alias;
}
TestFactory6(const TestFactory6 &f) { print_copy_created(this); value = f.value; alias = f.alias; } TestFactory6(const TestFactory6 &f) { print_copy_created(this); value = f.value; alias = f.alias; }
virtual ~TestFactory6() { print_destroyed(this); } virtual ~TestFactory6() { print_destroyed(this); }
virtual int get() { return value; } virtual int get() { return value; }
bool has_alias() { return alias; } bool has_alias() const { return alias; }
}; };
class PyTF6 : public TestFactory6 { class PyTF6 : public TestFactory6 {
public: public:
@ -85,7 +104,7 @@ public:
// when an alias is needed: // when an alias is needed:
PyTF6(TestFactory6 &&base) : TestFactory6(std::move(base)) { alias = true; print_created(this, "move", value); } PyTF6(TestFactory6 &&base) : TestFactory6(std::move(base)) { alias = true; print_created(this, "move", value); }
PyTF6(int i) : TestFactory6(i) { alias = true; print_created(this, i); } PyTF6(int i) : TestFactory6(i) { alias = true; print_created(this, i); }
PyTF6(PyTF6 &&f) : TestFactory6(std::move(f)) { print_move_created(this); } PyTF6(PyTF6 &&f) noexcept : TestFactory6(std::move(f)) { print_move_created(this); }
PyTF6(const PyTF6 &f) : TestFactory6(f) { print_copy_created(this); } PyTF6(const PyTF6 &f) : TestFactory6(f) { print_copy_created(this); }
PyTF6(std::string s) : TestFactory6((int) s.size()) { alias = true; print_created(this, s); } PyTF6(std::string s) : TestFactory6((int) s.size()) { alias = true; print_created(this, s); }
~PyTF6() override { print_destroyed(this); } ~PyTF6() override { print_destroyed(this); }
@ -98,16 +117,20 @@ protected:
bool alias = false; bool alias = false;
public: public:
TestFactory7(int i) : value{i} { print_created(this, i); } TestFactory7(int i) : value{i} { print_created(this, i); }
TestFactory7(TestFactory7 &&f) { print_move_created(this); value = f.value; alias = f.alias; } TestFactory7(TestFactory7 &&f) noexcept {
print_move_created(this);
value = f.value;
alias = f.alias;
}
TestFactory7(const TestFactory7 &f) { print_copy_created(this); value = f.value; alias = f.alias; } TestFactory7(const TestFactory7 &f) { print_copy_created(this); value = f.value; alias = f.alias; }
virtual ~TestFactory7() { print_destroyed(this); } virtual ~TestFactory7() { print_destroyed(this); }
virtual int get() { return value; } virtual int get() { return value; }
bool has_alias() { return alias; } bool has_alias() const { return alias; }
}; };
class PyTF7 : public TestFactory7 { class PyTF7 : public TestFactory7 {
public: public:
PyTF7(int i) : TestFactory7(i) { alias = true; print_created(this, i); } PyTF7(int i) : TestFactory7(i) { alias = true; print_created(this, i); }
PyTF7(PyTF7 &&f) : TestFactory7(std::move(f)) { print_move_created(this); } PyTF7(PyTF7 &&f) noexcept : TestFactory7(std::move(f)) { print_move_created(this); }
PyTF7(const PyTF7 &f) : TestFactory7(f) { print_copy_created(this); } PyTF7(const PyTF7 &f) : TestFactory7(f) { print_copy_created(this); }
~PyTF7() override { print_destroyed(this); } ~PyTF7() override { print_destroyed(this); }
int get() override { PYBIND11_OVERRIDE(int, TestFactory7, get, /*no args*/); } int get() override { PYBIND11_OVERRIDE(int, TestFactory7, get, /*no args*/); }
@ -122,7 +145,9 @@ public:
// Holder: // Holder:
static std::unique_ptr<TestFactory1> construct1(int a) { return std::unique_ptr<TestFactory1>(new TestFactory1(a)); } static std::unique_ptr<TestFactory1> construct1(int a) { return std::unique_ptr<TestFactory1>(new TestFactory1(a)); }
// pointer again // pointer again
static TestFactory1 *construct1_string(std::string a) { return new TestFactory1(a); } static TestFactory1 *construct1_string(std::string a) {
return new TestFactory1(std::move(a));
}
// Moveable type: // Moveable type:
// pointer: // pointer:
@ -130,7 +155,7 @@ public:
// holder: // holder:
static std::unique_ptr<TestFactory2> construct2(int a) { return std::unique_ptr<TestFactory2>(new TestFactory2(a)); } static std::unique_ptr<TestFactory2> construct2(int a) { return std::unique_ptr<TestFactory2>(new TestFactory2(a)); }
// by value moving: // by value moving:
static TestFactory2 construct2(std::string a) { return TestFactory2(a); } static TestFactory2 construct2(std::string a) { return TestFactory2(std::move(a)); }
// shared_ptr holder type: // shared_ptr holder type:
// pointer: // pointer:
@ -173,10 +198,11 @@ TEST_SUBMODULE(factory_constructors, m) {
; ;
py::class_<TestFactory2>(m, "TestFactory2") py::class_<TestFactory2>(m, "TestFactory2")
.def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct2(v); })) .def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct2(v); }))
.def(py::init([](unique_ptr_tag, std::string v) { return TestFactoryHelper::construct2(v); })) .def(py::init([](unique_ptr_tag, std::string v) {
return TestFactoryHelper::construct2(std::move(v));
}))
.def(py::init([](move_tag) { return TestFactoryHelper::construct2(); })) .def(py::init([](move_tag) { return TestFactoryHelper::construct2(); }))
.def_readwrite("value", &TestFactory2::value) .def_readwrite("value", &TestFactory2::value);
;
// Stateful & reused: // Stateful & reused:
int c = 1; int c = 1;
@ -188,7 +214,9 @@ TEST_SUBMODULE(factory_constructors, m) {
.def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct3(v); })) .def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct3(v); }))
.def(py::init([](shared_ptr_tag) { return TestFactoryHelper::construct3(); })); .def(py::init([](shared_ptr_tag) { return TestFactoryHelper::construct3(); }));
ignoreOldStyleInitWarnings([&pyTestFactory3]() { ignoreOldStyleInitWarnings([&pyTestFactory3]() {
pyTestFactory3.def("__init__", [](TestFactory3 &self, std::string v) { new (&self) TestFactory3(v); }); // placement-new ctor pyTestFactory3.def("__init__", [](TestFactory3 &self, std::string v) {
new (&self) TestFactory3(std::move(v));
}); // placement-new ctor
}); });
pyTestFactory3 pyTestFactory3
// factories returning a derived type: // factories returning a derived type:
@ -219,52 +247,54 @@ TEST_SUBMODULE(factory_constructors, m) {
py::class_<TestFactory6, PyTF6>(m, "TestFactory6") py::class_<TestFactory6, PyTF6>(m, "TestFactory6")
.def(py::init([](base_tag, int i) { return TestFactory6(i); })) .def(py::init([](base_tag, int i) { return TestFactory6(i); }))
.def(py::init([](alias_tag, int i) { return PyTF6(i); })) .def(py::init([](alias_tag, int i) { return PyTF6(i); }))
.def(py::init([](alias_tag, std::string s) { return PyTF6(s); })) .def(py::init([](alias_tag, std::string s) { return PyTF6(std::move(s)); }))
.def(py::init([](alias_tag, pointer_tag, int i) { return new PyTF6(i); })) .def(py::init([](alias_tag, pointer_tag, int i) { return new PyTF6(i); }))
.def(py::init([](base_tag, pointer_tag, int i) { return new TestFactory6(i); })) .def(py::init([](base_tag, pointer_tag, int i) { return new TestFactory6(i); }))
.def(py::init([](base_tag, alias_tag, pointer_tag, int i) { return (TestFactory6 *) new PyTF6(i); })) .def(py::init(
[](base_tag, alias_tag, pointer_tag, int i) { return (TestFactory6 *) new PyTF6(i); }))
.def("get", &TestFactory6::get) .def("get", &TestFactory6::get)
.def("has_alias", &TestFactory6::has_alias) .def("has_alias", &TestFactory6::has_alias)
.def_static("get_cstats", &ConstructorStats::get<TestFactory6>, py::return_value_policy::reference) .def_static(
.def_static("get_alias_cstats", &ConstructorStats::get<PyTF6>, py::return_value_policy::reference) "get_cstats", &ConstructorStats::get<TestFactory6>, py::return_value_policy::reference)
; .def_static(
"get_alias_cstats", &ConstructorStats::get<PyTF6>, py::return_value_policy::reference);
// test_init_factory_dual // test_init_factory_dual
// Separate alias constructor testing // Separate alias constructor testing
py::class_<TestFactory7, PyTF7, std::shared_ptr<TestFactory7>>(m, "TestFactory7") py::class_<TestFactory7, PyTF7, std::shared_ptr<TestFactory7>>(m, "TestFactory7")
.def(py::init( .def(py::init([](int i) { return TestFactory7(i); }, [](int i) { return PyTF7(i); }))
[](int i) { return TestFactory7(i); }, .def(py::init([](pointer_tag, int i) { return new TestFactory7(i); },
[](int i) { return PyTF7(i); }))
.def(py::init(
[](pointer_tag, int i) { return new TestFactory7(i); },
[](pointer_tag, int i) { return new PyTF7(i); })) [](pointer_tag, int i) { return new PyTF7(i); }))
.def(py::init( .def(py::init([](mixed_tag, int i) { return new TestFactory7(i); },
[](mixed_tag, int i) { return new TestFactory7(i); },
[](mixed_tag, int i) { return PyTF7(i); })) [](mixed_tag, int i) { return PyTF7(i); }))
.def(py::init( .def(py::init([](mixed_tag, const std::string &s) { return TestFactory7((int) s.size()); },
[](mixed_tag, std::string s) { return TestFactory7((int) s.size()); }, [](mixed_tag, const std::string &s) { return new PyTF7((int) s.size()); }))
[](mixed_tag, std::string s) { return new PyTF7((int) s.size()); })) .def(py::init([](base_tag, pointer_tag, int i) { return new TestFactory7(i); },
.def(py::init(
[](base_tag, pointer_tag, int i) { return new TestFactory7(i); },
[](base_tag, pointer_tag, int i) { return (TestFactory7 *) new PyTF7(i); })) [](base_tag, pointer_tag, int i) { return (TestFactory7 *) new PyTF7(i); }))
.def(py::init( .def(py::init([](alias_tag, pointer_tag, int i) { return new PyTF7(i); },
[](alias_tag, pointer_tag, int i) { return new PyTF7(i); }, [](alias_tag, pointer_tag, int i) { return new PyTF7(10 * i); }))
[](alias_tag, pointer_tag, int i) { return new PyTF7(10*i); }))
.def(py::init( .def(py::init(
[](shared_ptr_tag, base_tag, int i) { return std::make_shared<TestFactory7>(i); }, [](shared_ptr_tag, base_tag, int i) { return std::make_shared<TestFactory7>(i); },
[](shared_ptr_tag, base_tag, int i) { auto *p = new PyTF7(i); return std::shared_ptr<TestFactory7>(p); })) [](shared_ptr_tag, base_tag, int i) {
.def(py::init( auto *p = new PyTF7(i);
[](shared_ptr_tag, invalid_base_tag, int i) { return std::make_shared<TestFactory7>(i); }, return std::shared_ptr<TestFactory7>(p);
[](shared_ptr_tag, invalid_base_tag, int i) { return std::make_shared<TestFactory7>(i); })) // <-- invalid alias factory }))
.def(py::init([](shared_ptr_tag,
invalid_base_tag,
int i) { return std::make_shared<TestFactory7>(i); },
[](shared_ptr_tag, invalid_base_tag, int i) {
return std::make_shared<TestFactory7>(i);
})) // <-- invalid alias factory
.def("get", &TestFactory7::get) .def("get", &TestFactory7::get)
.def("has_alias", &TestFactory7::has_alias) .def("has_alias", &TestFactory7::has_alias)
.def_static("get_cstats", &ConstructorStats::get<TestFactory7>, py::return_value_policy::reference) .def_static(
.def_static("get_alias_cstats", &ConstructorStats::get<PyTF7>, py::return_value_policy::reference) "get_cstats", &ConstructorStats::get<TestFactory7>, py::return_value_policy::reference)
; .def_static(
"get_alias_cstats", &ConstructorStats::get<PyTF7>, py::return_value_policy::reference);
// test_placement_new_alternative // test_placement_new_alternative
// Class with a custom new operator but *without* a placement new operator (issue #948) // Class with a custom new operator but *without* a placement new operator (issue #948)
@ -331,12 +361,10 @@ TEST_SUBMODULE(factory_constructors, m) {
pyNoisyAlloc.def(py::init([](int i, double) { return new NoisyAlloc(i); })); pyNoisyAlloc.def(py::init([](int i, double) { return new NoisyAlloc(i); }));
// Regular again: requires yet another preallocation // Regular again: requires yet another preallocation
ignoreOldStyleInitWarnings([&pyNoisyAlloc]() { ignoreOldStyleInitWarnings([&pyNoisyAlloc]() {
pyNoisyAlloc.def("__init__", [](NoisyAlloc &a, int i, std::string) { new (&a) NoisyAlloc(i); }); pyNoisyAlloc.def(
"__init__", [](NoisyAlloc &a, int i, const std::string &) { new (&a) NoisyAlloc(i); });
}); });
// static_assert testing (the following def's should all fail with appropriate compilation errors): // static_assert testing (the following def's should all fail with appropriate compilation errors):
#if 0 #if 0
struct BadF1Base {}; struct BadF1Base {};

View File

@ -486,7 +486,9 @@ def test_invalid_self():
# Same as above, but for a class with an alias: # Same as above, but for a class with an alias:
class BrokenTF6(m.TestFactory6): class BrokenTF6(m.TestFactory6):
def __init__(self, bad): def __init__(self, bad):
if bad == 1: if bad == 0:
m.TestFactory6.__init__()
elif bad == 1:
a = m.TestFactory2(tag.pointer, 1) a = m.TestFactory2(tag.pointer, 1)
m.TestFactory6.__init__(a, tag.base, 1) m.TestFactory6.__init__(a, tag.base, 1)
elif bad == 2: elif bad == 2:
@ -506,13 +508,13 @@ def test_invalid_self():
BrokenTF1(arg) BrokenTF1(arg)
assert ( assert (
str(excinfo.value) str(excinfo.value)
== "__init__(self, ...) called with invalid `self` argument" == "__init__(self, ...) called with invalid or missing `self` argument"
) )
for arg in (1, 2, 3, 4): for arg in (0, 1, 2, 3, 4):
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
BrokenTF6(arg) BrokenTF6(arg)
assert ( assert (
str(excinfo.value) str(excinfo.value)
== "__init__(self, ...) called with invalid `self` argument" == "__init__(self, ...) called with invalid or missing `self` argument"
) )

View File

@ -35,19 +35,14 @@ TEST_SUBMODULE(gil_scoped, m) {
.def("virtual_func", &VirtClass::virtual_func) .def("virtual_func", &VirtClass::virtual_func)
.def("pure_virtual_func", &VirtClass::pure_virtual_func); .def("pure_virtual_func", &VirtClass::pure_virtual_func);
m.def("test_callback_py_obj", m.def("test_callback_py_obj", [](py::object &func) { func(); });
[](py::object func) { func(); }); m.def("test_callback_std_func", [](const std::function<void()> &func) { func(); });
m.def("test_callback_std_func", m.def("test_callback_virtual_func", [](VirtClass &virt) { virt.virtual_func(); });
[](const std::function<void()> &func) { func(); }); m.def("test_callback_pure_virtual_func", [](VirtClass &virt) { virt.pure_virtual_func(); });
m.def("test_callback_virtual_func", m.def("test_cross_module_gil", []() {
[](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 cm = py::module_::import("cross_module_gil_utils");
auto gil_acquire = reinterpret_cast<void (*)()>( auto gil_acquire
PyLong_AsVoidPtr(cm.attr("gil_acquire_funcaddr").ptr())); = reinterpret_cast<void (*)()>(PyLong_AsVoidPtr(cm.attr("gil_acquire_funcaddr").ptr()));
py::gil_scoped_release gil_release; py::gil_scoped_release gil_release;
gil_acquire(); gil_acquire();
}); });

View File

@ -15,17 +15,18 @@
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include <atomic> #include <atomic>
#include <iostream> #include <iostream>
#include <mutex>
#include <string>
#include <thread> #include <thread>
void noisy_function(const std::string &msg, bool flush) {
void noisy_function(std::string msg, bool flush) {
std::cout << msg; std::cout << msg;
if (flush) if (flush)
std::cout << std::flush; std::cout << std::flush;
} }
void noisy_funct_dual(std::string msg, std::string emsg) { void noisy_funct_dual(const std::string &msg, const std::string &emsg) {
std::cout << msg; std::cout << msg;
std::cerr << emsg; std::cerr << emsg;
} }
@ -34,10 +35,20 @@ void noisy_funct_dual(std::string msg, std::string emsg) {
// simply repeatedly write to std::cerr until stopped // simply repeatedly write to std::cerr until stopped
// redirect is called at some point to test the safety of scoped_estream_redirect // redirect is called at some point to test the safety of scoped_estream_redirect
struct TestThread { struct TestThread {
TestThread() : t_{nullptr}, stop_{false} { TestThread() : stop_{false} {
auto thread_f = [this] { auto thread_f = [this] {
static std::mutex cout_mutex;
while (!stop_) { while (!stop_) {
{
// #HelpAppreciated: Work on iostream.h thread safety.
// Without this lock, the clang ThreadSanitizer (tsan) reliably reports a
// data race, and this test is predictably flakey on Windows.
// For more background see the discussion under
// https://github.com/pybind/pybind11/pull/2982 and
// https://github.com/pybind/pybind11/pull/2995.
const std::lock_guard<std::mutex> lock(cout_mutex);
std::cout << "x" << std::flush; std::cout << "x" << std::flush;
}
std::this_thread::sleep_for(std::chrono::microseconds(50)); std::this_thread::sleep_for(std::chrono::microseconds(50));
} }; } };
t_ = new std::thread(std::move(thread_f)); t_ = new std::thread(std::move(thread_f));
@ -49,7 +60,7 @@ struct TestThread {
void stop() { stop_ = true; } void stop() { stop_ = true; }
void join() { void join() const {
py::gil_scoped_release gil_lock; py::gil_scoped_release gil_lock;
t_->join(); t_->join();
} }
@ -59,7 +70,7 @@ struct TestThread {
std::this_thread::sleep_for(std::chrono::milliseconds(50)); std::this_thread::sleep_for(std::chrono::milliseconds(50));
} }
std::thread * t_; std::thread *t_{nullptr};
std::atomic<bool> stop_; std::atomic<bool> stop_;
}; };
@ -70,12 +81,12 @@ TEST_SUBMODULE(iostream, m) {
// test_evals // test_evals
m.def("captured_output_default", [](std::string msg) { m.def("captured_output_default", [](const std::string &msg) {
py::scoped_ostream_redirect redir; py::scoped_ostream_redirect redir;
std::cout << msg << std::flush; std::cout << msg << std::flush;
}); });
m.def("captured_output", [](std::string msg) { m.def("captured_output", [](const std::string &msg) {
py::scoped_ostream_redirect redir(std::cout, py::module_::import("sys").attr("stdout")); py::scoped_ostream_redirect redir(std::cout, py::module_::import("sys").attr("stdout"));
std::cout << msg << std::flush; std::cout << msg << std::flush;
}); });
@ -84,7 +95,7 @@ TEST_SUBMODULE(iostream, m) {
py::call_guard<py::scoped_ostream_redirect>(), py::call_guard<py::scoped_ostream_redirect>(),
py::arg("msg"), py::arg("flush")=true); py::arg("msg"), py::arg("flush")=true);
m.def("captured_err", [](std::string msg) { m.def("captured_err", [](const std::string &msg) {
py::scoped_ostream_redirect redir(std::cerr, py::module_::import("sys").attr("stderr")); py::scoped_ostream_redirect redir(std::cerr, py::module_::import("sys").attr("stderr"));
std::cerr << msg << std::flush; std::cerr << msg << std::flush;
}); });
@ -95,15 +106,11 @@ TEST_SUBMODULE(iostream, m) {
py::call_guard<py::scoped_ostream_redirect, py::scoped_estream_redirect>(), py::call_guard<py::scoped_ostream_redirect, py::scoped_estream_redirect>(),
py::arg("msg"), py::arg("emsg")); py::arg("msg"), py::arg("emsg"));
m.def("raw_output", [](std::string msg) { m.def("raw_output", [](const std::string &msg) { std::cout << msg << std::flush; });
std::cout << msg << std::flush;
});
m.def("raw_err", [](std::string msg) { m.def("raw_err", [](const std::string &msg) { std::cerr << msg << std::flush; });
std::cerr << msg << std::flush;
});
m.def("captured_dual", [](std::string msg, std::string emsg) { m.def("captured_dual", [](const std::string &msg, const std::string &emsg) {
py::scoped_ostream_redirect redirout(std::cout, py::module_::import("sys").attr("stdout")); py::scoped_ostream_redirect redirout(std::cout, py::module_::import("sys").attr("stdout"));
py::scoped_ostream_redirect redirerr(std::cerr, py::module_::import("sys").attr("stderr")); py::scoped_ostream_redirect redirerr(std::cerr, py::module_::import("sys").attr("stderr"));
std::cout << msg << std::flush; std::cout << msg << std::flush;

View File

@ -69,6 +69,96 @@ def test_captured_large_string(capsys):
assert stderr == "" assert stderr == ""
def test_captured_utf8_2byte_offset0(capsys):
msg = "\u07FF"
msg = "" + msg * (1024 // len(msg) + 1)
m.captured_output_default(msg)
stdout, stderr = capsys.readouterr()
assert stdout == msg
assert stderr == ""
def test_captured_utf8_2byte_offset1(capsys):
msg = "\u07FF"
msg = "1" + msg * (1024 // len(msg) + 1)
m.captured_output_default(msg)
stdout, stderr = capsys.readouterr()
assert stdout == msg
assert stderr == ""
def test_captured_utf8_3byte_offset0(capsys):
msg = "\uFFFF"
msg = "" + msg * (1024 // len(msg) + 1)
m.captured_output_default(msg)
stdout, stderr = capsys.readouterr()
assert stdout == msg
assert stderr == ""
def test_captured_utf8_3byte_offset1(capsys):
msg = "\uFFFF"
msg = "1" + msg * (1024 // len(msg) + 1)
m.captured_output_default(msg)
stdout, stderr = capsys.readouterr()
assert stdout == msg
assert stderr == ""
def test_captured_utf8_3byte_offset2(capsys):
msg = "\uFFFF"
msg = "12" + msg * (1024 // len(msg) + 1)
m.captured_output_default(msg)
stdout, stderr = capsys.readouterr()
assert stdout == msg
assert stderr == ""
def test_captured_utf8_4byte_offset0(capsys):
msg = "\U0010FFFF"
msg = "" + msg * (1024 // len(msg) + 1)
m.captured_output_default(msg)
stdout, stderr = capsys.readouterr()
assert stdout == msg
assert stderr == ""
def test_captured_utf8_4byte_offset1(capsys):
msg = "\U0010FFFF"
msg = "1" + msg * (1024 // len(msg) + 1)
m.captured_output_default(msg)
stdout, stderr = capsys.readouterr()
assert stdout == msg
assert stderr == ""
def test_captured_utf8_4byte_offset2(capsys):
msg = "\U0010FFFF"
msg = "12" + msg * (1024 // len(msg) + 1)
m.captured_output_default(msg)
stdout, stderr = capsys.readouterr()
assert stdout == msg
assert stderr == ""
def test_captured_utf8_4byte_offset3(capsys):
msg = "\U0010FFFF"
msg = "123" + msg * (1024 // len(msg) + 1)
m.captured_output_default(msg)
stdout, stderr = capsys.readouterr()
assert stdout == msg
assert stderr == ""
def test_guard_capture(capsys): def test_guard_capture(capsys):
msg = "I've been redirected to Python, I hope!" msg = "I've been redirected to Python, I hope!"
m.guard_output(msg) m.guard_output(msg)

View File

@ -11,6 +11,8 @@
#include "constructor_stats.h" #include "constructor_stats.h"
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include <utility>
TEST_SUBMODULE(kwargs_and_defaults, m) { TEST_SUBMODULE(kwargs_and_defaults, m) {
auto kw_func = [](int x, int y) { return "x=" + std::to_string(x) + ", y=" + std::to_string(y); }; auto kw_func = [](int x, int y) { return "x=" + std::to_string(x) + ", y=" + std::to_string(y); };
@ -37,18 +39,16 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
m.def("args_function", [](py::args args) -> py::tuple { m.def("args_function", [](py::args args) -> py::tuple {
return std::move(args); return std::move(args);
}); });
m.def("args_kwargs_function", [](py::args args, py::kwargs kwargs) { m.def("args_kwargs_function", [](const py::args &args, const py::kwargs &kwargs) {
return py::make_tuple(args, kwargs); return py::make_tuple(args, kwargs);
}); });
// test_mixed_args_and_kwargs // test_mixed_args_and_kwargs
m.def("mixed_plus_args", [](int i, double j, py::args args) { m.def("mixed_plus_args",
return py::make_tuple(i, j, args); [](int i, double j, const py::args &args) { return py::make_tuple(i, j, args); });
}); m.def("mixed_plus_kwargs",
m.def("mixed_plus_kwargs", [](int i, double j, py::kwargs kwargs) { [](int i, double j, const py::kwargs &kwargs) { return py::make_tuple(i, j, kwargs); });
return py::make_tuple(i, j, kwargs); auto mixed_plus_both = [](int i, double j, const py::args &args, const py::kwargs &kwargs) {
});
auto mixed_plus_both = [](int i, double j, py::args args, py::kwargs kwargs) {
return py::make_tuple(i, j, args, kwargs); return py::make_tuple(i, j, args, kwargs);
}; };
m.def("mixed_plus_args_kwargs", mixed_plus_both); m.def("mixed_plus_args_kwargs", mixed_plus_both);
@ -65,7 +65,10 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
#endif #endif
m.def("arg_refcount_h", [](py::handle h) { GC_IF_NEEDED; return h.ref_count(); }); 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_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("arg_refcount_o", [](const py::object &o) {
GC_IF_NEEDED;
return o.ref_count();
});
m.def("args_refcount", [](py::args a) { m.def("args_refcount", [](py::args a) {
GC_IF_NEEDED; GC_IF_NEEDED;
py::tuple t(a.size()); py::tuple t(a.size());
@ -74,7 +77,7 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
t[i] = (int) Py_REFCNT(PyTuple_GET_ITEM(a.ptr(), static_cast<py::ssize_t>(i))); t[i] = (int) Py_REFCNT(PyTuple_GET_ITEM(a.ptr(), static_cast<py::ssize_t>(i)));
return t; return t;
}); });
m.def("mixed_args_refcount", [](py::object o, py::args a) { m.def("mixed_args_refcount", [](const py::object &o, py::args a) {
GC_IF_NEEDED; GC_IF_NEEDED;
py::tuple t(a.size() + 1); py::tuple t(a.size() + 1);
t[0] = o.ref_count(); t[0] = o.ref_count();
@ -103,9 +106,15 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
py::arg() = 3, "j"_a = 4, py::kw_only(), "k"_a = 5, "z"_a); py::arg() = 3, "j"_a = 4, py::kw_only(), "k"_a = 5, "z"_a);
m.def("kw_only_mixed", [](int i, int j) { return py::make_tuple(i, j); }, m.def("kw_only_mixed", [](int i, int j) { return py::make_tuple(i, j); },
"i"_a, py::kw_only(), "j"_a); "i"_a, py::kw_only(), "j"_a);
m.def("kw_only_plus_more", [](int i, int j, int k, py::kwargs kwargs) { m.def(
return py::make_tuple(i, j, k, kwargs); }, "kw_only_plus_more",
py::arg() /* positional */, py::arg("j") = -1 /* both */, py::kw_only(), py::arg("k") /* kw-only */); [](int i, int j, int k, const py::kwargs &kwargs) {
return py::make_tuple(i, j, k, kwargs);
},
py::arg() /* positional */,
py::arg("j") = -1 /* both */,
py::kw_only(),
py::arg("k") /* kw-only */);
m.def("register_invalid_kw_only", [](py::module_ m) { m.def("register_invalid_kw_only", [](py::module_ m) {
m.def("bad_kw_only", [](int i, int j) { return py::make_tuple(i, j); }, m.def("bad_kw_only", [](int i, int j) { return py::make_tuple(i, j); },
@ -137,6 +146,8 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
// Make sure a class (not an instance) can be used as a default argument. // Make sure a class (not an instance) can be used as a default argument.
// The return value doesn't matter, only that the module is importable. // The return value doesn't matter, only that the module is importable.
m.def("class_default_argument", [](py::object a) { return py::repr(a); }, m.def(
"class_default_argument",
[](py::object a) { return py::repr(std::move(a)); },
"a"_a = py::module_::import("decimal").attr("Decimal")); "a"_a = py::module_::import("decimal").attr("Decimal"));
} }

View File

@ -10,9 +10,12 @@
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include "local_bindings.h" #include "local_bindings.h"
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include <pybind11/stl_bind.h> #include <pybind11/stl_bind.h>
#include <numeric> #include <numeric>
#include <utility>
TEST_SUBMODULE(local_bindings, m) { TEST_SUBMODULE(local_bindings, m) {
// test_load_external // test_load_external
@ -86,7 +89,10 @@ TEST_SUBMODULE(local_bindings, m) {
m.def("return_self", [](LocalVec *v) { return v; }); m.def("return_self", [](LocalVec *v) { return v; });
m.def("return_copy", [](const LocalVec &v) { return LocalVec(v); }); m.def("return_copy", [](const LocalVec &v) { return LocalVec(v); });
class Cat : public pets::Pet { public: Cat(std::string name) : Pet(name) {}; }; class Cat : public pets::Pet {
public:
Cat(std::string name) : Pet(std::move(name)) {}
};
py::class_<pets::Pet>(m, "Pet", py::module_local()) py::class_<pets::Pet>(m, "Pet", py::module_local())
.def("get_name", &pets::Pet::name); .def("get_name", &pets::Pet::name);
// Binding for local extending class: // Binding for local extending class:

View File

@ -22,16 +22,18 @@ public:
ExampleMandA(int value) : value(value) { print_created(this, value); } ExampleMandA(int value) : value(value) { print_created(this, value); }
ExampleMandA(const ExampleMandA &e) : value(e.value) { print_copy_created(this); } ExampleMandA(const ExampleMandA &e) : value(e.value) { print_copy_created(this); }
ExampleMandA(std::string&&) {} ExampleMandA(std::string&&) {}
ExampleMandA(ExampleMandA &&e) : value(e.value) { print_move_created(this); } ExampleMandA(ExampleMandA &&e) noexcept : value(e.value) { print_move_created(this); }
~ExampleMandA() { print_destroyed(this); } ~ExampleMandA() { print_destroyed(this); }
std::string toString() { std::string toString() const { return "ExampleMandA[value=" + std::to_string(value) + "]"; }
return "ExampleMandA[value=" + std::to_string(value) + "]";
}
void operator=(const ExampleMandA &e) { print_copy_assigned(this); value = e.value; } void operator=(const ExampleMandA &e) { print_copy_assigned(this); value = e.value; }
void operator=(ExampleMandA &&e) { print_move_assigned(this); value = e.value; } void operator=(ExampleMandA &&e) noexcept {
print_move_assigned(this);
value = e.value;
}
// NOLINTNEXTLINE(performance-unnecessary-value-param)
void add1(ExampleMandA other) { value += other.value; } // passing by value void add1(ExampleMandA other) { value += other.value; } // passing by value
void add2(ExampleMandA &other) { value += other.value; } // passing by reference void add2(ExampleMandA &other) { value += other.value; } // passing by reference
void add3(const ExampleMandA &other) { value += other.value; } // passing by const reference void add3(const ExampleMandA &other) { value += other.value; } // passing by const reference
@ -41,6 +43,7 @@ public:
void add6(int other) { value += other; } // passing by value void add6(int other) { value += other; } // passing by value
void add7(int &other) { value += other; } // passing by reference void add7(int &other) { value += other; } // passing by reference
void add8(const int &other) { value += other; } // passing by const reference void add8(const int &other) { value += other; } // passing by const reference
// NOLINTNEXTLINE(readability-non-const-parameter) Deliberately non-const for testing
void add9(int *other) { value += *other; } // passing by pointer void add9(int *other) { value += *other; } // passing by pointer
void add10(const int *other) { value += *other; } // passing by const pointer void add10(const int *other) { value += *other; } // passing by const pointer
@ -48,13 +51,13 @@ public:
ExampleMandA self1() { return *this; } // return by value ExampleMandA self1() { return *this; } // return by value
ExampleMandA &self2() { return *this; } // return by reference ExampleMandA &self2() { return *this; } // return by reference
const ExampleMandA &self3() { return *this; } // return by const reference const ExampleMandA &self3() const { return *this; } // return by const reference
ExampleMandA *self4() { return this; } // return by pointer ExampleMandA *self4() { return this; } // return by pointer
const ExampleMandA *self5() { return this; } // return by const pointer const ExampleMandA *self5() const { return this; } // return by const pointer
int internal1() { return value; } // return by value int internal1() const { return value; } // return by value
int &internal2() { return value; } // return by reference int &internal2() { return value; } // return by reference
const int &internal3() { return value; } // return by const reference const int &internal3() const { return value; } // return by const reference
int *internal4() { return &value; } // return by pointer int *internal4() { return &value; } // return by pointer
const int *internal5() { return &value; } // return by const pointer const int *internal5() { return &value; } // return by const pointer
@ -114,7 +117,15 @@ int none1(const NoneTester &obj) { return obj.answer; }
int none2(NoneTester *obj) { return obj ? obj->answer : -1; } int none2(NoneTester *obj) { return obj ? obj->answer : -1; }
int none3(std::shared_ptr<NoneTester> &obj) { return obj ? obj->answer : -1; } int none3(std::shared_ptr<NoneTester> &obj) { return obj ? obj->answer : -1; }
int none4(std::shared_ptr<NoneTester> *obj) { return obj && *obj ? (*obj)->answer : -1; } int none4(std::shared_ptr<NoneTester> *obj) { return obj && *obj ? (*obj)->answer : -1; }
int none5(std::shared_ptr<NoneTester> obj) { return obj ? obj->answer : -1; } int none5(const std::shared_ptr<NoneTester> &obj) { return obj ? obj->answer : -1; }
// Issue #2778: implicit casting from None to object (not pointer)
class NoneCastTester {
public:
int answer = -1;
NoneCastTester() = default;
NoneCastTester(int v) : answer(v) {}
};
struct StrIssue { struct StrIssue {
int val = -1; int val = -1;
@ -228,36 +239,41 @@ TEST_SUBMODULE(methods_and_attributes, m) {
.def(py::init<>()) .def(py::init<>())
.def_readonly("def_readonly", &TestProperties::value) .def_readonly("def_readonly", &TestProperties::value)
.def_readwrite("def_readwrite", &TestProperties::value) .def_readwrite("def_readwrite", &TestProperties::value)
.def_property("def_writeonly", nullptr, .def_property("def_writeonly", nullptr, [](TestProperties &s, int v) { s.value = v; })
[](TestProperties& s,int v) { s.value = v; } )
.def_property("def_property_writeonly", nullptr, &TestProperties::set) .def_property("def_property_writeonly", nullptr, &TestProperties::set)
.def_property_readonly("def_property_readonly", &TestProperties::get) .def_property_readonly("def_property_readonly", &TestProperties::get)
.def_property("def_property", &TestProperties::get, &TestProperties::set) .def_property("def_property", &TestProperties::get, &TestProperties::set)
.def_property("def_property_impossible", nullptr, nullptr) .def_property("def_property_impossible", nullptr, nullptr)
.def_readonly_static("def_readonly_static", &TestProperties::static_value) .def_readonly_static("def_readonly_static", &TestProperties::static_value)
.def_readwrite_static("def_readwrite_static", &TestProperties::static_value) .def_readwrite_static("def_readwrite_static", &TestProperties::static_value)
.def_property_static("def_writeonly_static", nullptr, .def_property_static("def_writeonly_static",
[](py::object, int v) { TestProperties::static_value = v; }) nullptr,
.def_property_readonly_static("def_property_readonly_static", [](const py::object &, int v) { TestProperties::static_value = v; })
[](py::object) { return TestProperties::static_get(); }) .def_property_readonly_static(
.def_property_static("def_property_writeonly_static", nullptr, "def_property_readonly_static",
[](py::object, int v) { return TestProperties::static_set(v); }) [](const py::object &) { return TestProperties::static_get(); })
.def_property_static("def_property_static", .def_property_static(
[](py::object) { return TestProperties::static_get(); }, "def_property_writeonly_static",
[](py::object, int v) { TestProperties::static_set(v); }) nullptr,
.def_property_static("static_cls", [](const py::object &, int v) { return TestProperties::static_set(v); })
.def_property_static(
"def_property_static",
[](const py::object &) { return TestProperties::static_get(); },
[](const py::object &, int v) { TestProperties::static_set(v); })
.def_property_static(
"static_cls",
[](py::object cls) { return cls; }, [](py::object cls) { return cls; },
[](py::object cls, py::function f) { f(cls); }); [](const py::object &cls, const py::function &f) { f(cls); });
py::class_<TestPropertiesOverride, TestProperties>(m, "TestPropertiesOverride") py::class_<TestPropertiesOverride, TestProperties>(m, "TestPropertiesOverride")
.def(py::init<>()) .def(py::init<>())
.def_readonly("def_readonly", &TestPropertiesOverride::value) .def_readonly("def_readonly", &TestPropertiesOverride::value)
.def_readonly_static("def_readonly_static", &TestPropertiesOverride::static_value); .def_readonly_static("def_readonly_static", &TestPropertiesOverride::static_value);
auto static_get1 = [](py::object) -> const UserType & { return TestPropRVP::sv1; }; auto static_get1 = [](const py::object &) -> const UserType & { return TestPropRVP::sv1; };
auto static_get2 = [](py::object) -> const UserType & { return TestPropRVP::sv2; }; auto static_get2 = [](const py::object &) -> const UserType & { return TestPropRVP::sv2; };
auto static_set1 = [](py::object, int v) { TestPropRVP::sv1.set(v); }; auto static_set1 = [](const py::object &, int v) { TestPropRVP::sv1.set(v); };
auto static_set2 = [](py::object, int v) { TestPropRVP::sv2.set(v); }; auto static_set2 = [](const py::object &, int v) { TestPropRVP::sv2.set(v); };
auto rvp_copy = py::return_value_policy::copy; auto rvp_copy = py::return_value_policy::copy;
// test_property_return_value_policies // test_property_return_value_policies
@ -268,25 +284,28 @@ TEST_SUBMODULE(methods_and_attributes, m) {
.def_property_readonly("ro_func", py::cpp_function(&TestPropRVP::get2, rvp_copy)) .def_property_readonly("ro_func", py::cpp_function(&TestPropRVP::get2, rvp_copy))
.def_property("rw_ref", &TestPropRVP::get1, &TestPropRVP::set1) .def_property("rw_ref", &TestPropRVP::get1, &TestPropRVP::set1)
.def_property("rw_copy", &TestPropRVP::get2, &TestPropRVP::set2, rvp_copy) .def_property("rw_copy", &TestPropRVP::get2, &TestPropRVP::set2, rvp_copy)
.def_property("rw_func", py::cpp_function(&TestPropRVP::get2, rvp_copy), &TestPropRVP::set2) .def_property(
"rw_func", py::cpp_function(&TestPropRVP::get2, rvp_copy), &TestPropRVP::set2)
.def_property_readonly_static("static_ro_ref", static_get1) .def_property_readonly_static("static_ro_ref", static_get1)
.def_property_readonly_static("static_ro_copy", static_get2, rvp_copy) .def_property_readonly_static("static_ro_copy", static_get2, rvp_copy)
.def_property_readonly_static("static_ro_func", py::cpp_function(static_get2, rvp_copy)) .def_property_readonly_static("static_ro_func", py::cpp_function(static_get2, rvp_copy))
.def_property_static("static_rw_ref", static_get1, static_set1) .def_property_static("static_rw_ref", static_get1, static_set1)
.def_property_static("static_rw_copy", static_get2, static_set2, rvp_copy) .def_property_static("static_rw_copy", static_get2, static_set2, rvp_copy)
.def_property_static("static_rw_func", py::cpp_function(static_get2, rvp_copy), static_set2) .def_property_static(
"static_rw_func", py::cpp_function(static_get2, rvp_copy), static_set2)
// test_property_rvalue_policy // test_property_rvalue_policy
.def_property_readonly("rvalue", &TestPropRVP::get_rvalue) .def_property_readonly("rvalue", &TestPropRVP::get_rvalue)
.def_property_readonly_static("static_rvalue", [](py::object) { return UserType(1); }); .def_property_readonly_static("static_rvalue",
[](const py::object &) { return UserType(1); });
// test_metaclass_override // test_metaclass_override
struct MetaclassOverride { }; struct MetaclassOverride { };
py::class_<MetaclassOverride>(m, "MetaclassOverride", py::metaclass((PyObject *) &PyType_Type)) py::class_<MetaclassOverride>(m, "MetaclassOverride", py::metaclass((PyObject *) &PyType_Type))
.def_property_readonly_static("readonly", [](py::object) { return 1; }); .def_property_readonly_static("readonly", [](const py::object &) { return 1; });
// test_overload_ordering // test_overload_ordering
m.def("overload_order", [](std::string) { return 1; }); m.def("overload_order", [](const std::string &) { return 1; });
m.def("overload_order", [](std::string) { return 2; }); m.def("overload_order", [](const std::string &) { return 2; });
m.def("overload_order", [](int) { return 3; }); m.def("overload_order", [](int) { return 3; });
m.def("overload_order", [](int) { return 4; }, py::prepend{}); m.def("overload_order", [](int) { return 4; }, py::prepend{});
@ -341,6 +360,16 @@ TEST_SUBMODULE(methods_and_attributes, m) {
m.def("no_none_kwarg", &none2, "a"_a.none(false)); m.def("no_none_kwarg", &none2, "a"_a.none(false));
m.def("no_none_kwarg_kw_only", &none2, py::kw_only(), "a"_a.none(false)); m.def("no_none_kwarg_kw_only", &none2, py::kw_only(), "a"_a.none(false));
// test_casts_none
// Issue #2778: implicit casting from None to object (not pointer)
py::class_<NoneCastTester>(m, "NoneCastTester")
.def(py::init<>())
.def(py::init<int>())
.def(py::init([](py::none const&) { return NoneCastTester{}; }));
py::implicitly_convertible<py::none, NoneCastTester>();
m.def("ok_obj_or_none", [](NoneCastTester const& foo) { return foo.answer; });
// test_str_issue // test_str_issue
// Issue #283: __str__ called on uninitialized instance when constructor arguments invalid // Issue #283: __str__ called on uninitialized instance when constructor arguments invalid
py::class_<StrIssue>(m, "StrIssue") py::class_<StrIssue>(m, "StrIssue")
@ -362,14 +391,14 @@ TEST_SUBMODULE(methods_and_attributes, m) {
.def("increase_value", &RegisteredDerived::increase_value) .def("increase_value", &RegisteredDerived::increase_value)
.def_readwrite("rw_value", &RegisteredDerived::rw_value) .def_readwrite("rw_value", &RegisteredDerived::rw_value)
.def_readonly("ro_value", &RegisteredDerived::ro_value) .def_readonly("ro_value", &RegisteredDerived::ro_value)
// These should trigger a static_assert if uncommented // Uncommenting the next line should trigger a static_assert:
//.def_readwrite("fails", &UserType::value) // should trigger a static_assert if uncommented // .def_readwrite("fails", &UserType::value)
//.def_readonly("fails", &UserType::value) // should trigger a static_assert if uncommented // Uncommenting the next line should trigger a static_assert:
// .def_readonly("fails", &UserType::value)
.def_property("rw_value_prop", &RegisteredDerived::get_int, &RegisteredDerived::set_int) .def_property("rw_value_prop", &RegisteredDerived::get_int, &RegisteredDerived::set_int)
.def_property_readonly("ro_value_prop", &RegisteredDerived::get_double) .def_property_readonly("ro_value_prop", &RegisteredDerived::get_double)
// This one is in the registered class: // This one is in the registered class:
.def("sum", &RegisteredDerived::sum) .def("sum", &RegisteredDerived::sum);
;
using Adapted = decltype(py::method_adaptor<RegisteredDerived>(&RegisteredDerived::do_nothing)); using Adapted = decltype(py::method_adaptor<RegisteredDerived>(&RegisteredDerived::do_nothing));
static_assert(std::is_same<Adapted, void (RegisteredDerived::*)() const>::value, ""); static_assert(std::is_same<Adapted, void (RegisteredDerived::*)() const>::value, "");

View File

@ -431,6 +431,17 @@ def test_accepts_none(msg):
assert "incompatible function arguments" in str(excinfo.value) assert "incompatible function arguments" in str(excinfo.value)
def test_casts_none():
"""#2778: implicit casting from None to object (not pointer)"""
a = m.NoneCastTester()
assert m.ok_obj_or_none(a) == -1
a = m.NoneCastTester(4)
assert m.ok_obj_or_none(a) == 4
a = m.NoneCastTester(None)
assert m.ok_obj_or_none(a) == -1
assert m.ok_obj_or_none(None) == -1
def test_str_issue(msg): def test_str_issue(msg):
"""#283: __str__ called on uninitialized instance when constructor arguments invalid""" """#283: __str__ called on uninitialized instance when constructor arguments invalid"""

View File

@ -24,7 +24,8 @@ TEST_SUBMODULE(modules, m) {
~A() { print_destroyed(this); } ~A() { print_destroyed(this); }
A(const A&) { print_copy_created(this); } A(const A&) { print_copy_created(this); }
A& operator=(const A &copy) { print_copy_assigned(this); v = copy.v; return *this; } A& operator=(const A &copy) { print_copy_assigned(this); v = copy.v; return *this; }
std::string toString() { return "A[" + std::to_string(v) + "]"; } std::string toString() const { return "A[" + std::to_string(v) + "]"; }
private: private:
int v; int v;
}; };

View File

@ -11,6 +11,8 @@
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include "constructor_stats.h" #include "constructor_stats.h"
namespace {
// Many bases for testing that multiple inheritance from many classes (i.e. requiring extra // Many bases for testing that multiple inheritance from many classes (i.e. requiring extra
// space for holder constructed flags) works. // space for holder constructed flags) works.
template <int N> struct BaseN { template <int N> struct BaseN {
@ -43,13 +45,40 @@ int WithStatic2::static_value2 = 2;
int VanillaStaticMix1::static_value = 12; int VanillaStaticMix1::static_value = 12;
int VanillaStaticMix2::static_value = 12; int VanillaStaticMix2::static_value = 12;
// test_multiple_inheritance_virtbase
struct Base1a {
Base1a(int i) : i(i) { }
int foo() const { return i; }
int i;
};
struct Base2a {
Base2a(int i) : i(i) { }
int bar() const { return i; }
int i;
};
struct Base12a : Base1a, Base2a {
Base12a(int i, int j) : Base1a(i), Base2a(j) { }
};
// test_mi_unaligned_base
// test_mi_base_return
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
} // namespace
TEST_SUBMODULE(multiple_inheritance, m) { TEST_SUBMODULE(multiple_inheritance, m) {
// Please do not interleave `struct` and `class` definitions with bindings code,
// but implement `struct`s and `class`es in the anonymous namespace above.
// This helps keeping the smart_holder branch in sync with master.
// test_multiple_inheritance_mix1 // test_multiple_inheritance_mix1
// test_multiple_inheritance_mix2 // test_multiple_inheritance_mix2
struct Base1 { struct Base1 {
Base1(int i) : i(i) { } Base1(int i) : i(i) { }
int foo() { return i; } int foo() const { return i; }
int i; int i;
}; };
py::class_<Base1> b1(m, "Base1"); py::class_<Base1> b1(m, "Base1");
@ -58,7 +87,7 @@ TEST_SUBMODULE(multiple_inheritance, m) {
struct Base2 { struct Base2 {
Base2(int i) : i(i) { } Base2(int i) : i(i) { }
int bar() { return i; } int bar() const { return i; }
int i; int i;
}; };
py::class_<Base2> b2(m, "Base2"); py::class_<Base2> b2(m, "Base2");
@ -99,41 +128,24 @@ TEST_SUBMODULE(multiple_inheritance, m) {
// test_multiple_inheritance_virtbase // test_multiple_inheritance_virtbase
// Test the case where not all base classes are specified, and where pybind11 requires the // Test the case where not all base classes are specified, and where pybind11 requires the
// py::multiple_inheritance flag to perform proper casting between types. // py::multiple_inheritance flag to perform proper casting between types.
struct Base1a {
Base1a(int i) : i(i) { }
int foo() { return i; }
int i;
};
py::class_<Base1a, std::shared_ptr<Base1a>>(m, "Base1a") py::class_<Base1a, std::shared_ptr<Base1a>>(m, "Base1a")
.def(py::init<int>()) .def(py::init<int>())
.def("foo", &Base1a::foo); .def("foo", &Base1a::foo);
struct Base2a {
Base2a(int i) : i(i) { }
int bar() { return i; }
int i;
};
py::class_<Base2a, std::shared_ptr<Base2a>>(m, "Base2a") py::class_<Base2a, std::shared_ptr<Base2a>>(m, "Base2a")
.def(py::init<int>()) .def(py::init<int>())
.def("bar", &Base2a::bar); .def("bar", &Base2a::bar);
struct Base12a : Base1a, Base2a {
Base12a(int i, int j) : Base1a(i), Base2a(j) { }
};
py::class_<Base12a, /* Base1 missing */ Base2a, py::class_<Base12a, /* Base1 missing */ Base2a,
std::shared_ptr<Base12a>>(m, "Base12a", py::multiple_inheritance()) std::shared_ptr<Base12a>>(m, "Base12a", py::multiple_inheritance())
.def(py::init<int, int>()); .def(py::init<int, int>());
m.def("bar_base2a", [](Base2a *b) { return b->bar(); }); m.def("bar_base2a", [](Base2a *b) { return b->bar(); });
m.def("bar_base2a_sharedptr", [](std::shared_ptr<Base2a> b) { return b->bar(); }); m.def("bar_base2a_sharedptr", [](const std::shared_ptr<Base2a> &b) { return b->bar(); });
// test_mi_unaligned_base // test_mi_unaligned_base
// test_mi_base_return // test_mi_base_return
// Issue #801: invalid casting to derived type with MI bases // Issue #801: invalid casting to derived type with MI bases
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: // Unregistered classes:
struct I801B3 { int c = 3; virtual ~I801B3() = default; }; struct I801B3 { int c = 3; virtual ~I801B3() = default; };
struct I801E : I801B3, I801D {}; struct I801E : I801B3, I801D {};

View File

@ -13,6 +13,7 @@
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include <cstdint> #include <cstdint>
#include <utility>
// Size / dtype checks. // Size / dtype checks.
struct DtypeCheck { struct DtypeCheck {
@ -192,7 +193,7 @@ TEST_SUBMODULE(numpy_array, sm) {
sm.def("scalar_int", []() { return py::array(py::dtype("i"), {}, {}, &data_i); }); sm.def("scalar_int", []() { return py::array(py::dtype("i"), {}, {}, &data_i); });
// test_wrap // test_wrap
sm.def("wrap", [](py::array a) { sm.def("wrap", [](const py::array &a) {
return py::array( return py::array(
a.dtype(), a.dtype(),
{a.shape(), a.shape() + a.ndim()}, {a.shape(), a.shape() + a.ndim()},
@ -222,9 +223,10 @@ TEST_SUBMODULE(numpy_array, sm) {
// test_isinstance // test_isinstance
sm.def("isinstance_untyped", [](py::object yes, py::object no) { sm.def("isinstance_untyped", [](py::object yes, py::object no) {
return py::isinstance<py::array>(yes) && !py::isinstance<py::array>(no); return py::isinstance<py::array>(std::move(yes))
&& !py::isinstance<py::array>(std::move(no));
}); });
sm.def("isinstance_typed", [](py::object o) { sm.def("isinstance_typed", [](const py::object &o) {
return py::isinstance<py::array_t<double>>(o) && !py::isinstance<py::array_t<int>>(o); return py::isinstance<py::array_t<double>>(o) && !py::isinstance<py::array_t<int>>(o);
}); });
@ -236,7 +238,7 @@ TEST_SUBMODULE(numpy_array, sm) {
"array_t<double>"_a=py::array_t<double>() "array_t<double>"_a=py::array_t<double>()
); );
}); });
sm.def("converting_constructors", [](py::object o) { sm.def("converting_constructors", [](const py::object &o) {
return py::dict( return py::dict(
"array"_a=py::array(o), "array"_a=py::array(o),
"array_t<int32>"_a=py::array_t<std::int32_t>(o), "array_t<int32>"_a=py::array_t<std::int32_t>(o),
@ -245,40 +247,47 @@ TEST_SUBMODULE(numpy_array, sm) {
}); });
// test_overload_resolution // test_overload_resolution
sm.def("overloaded", [](py::array_t<double>) { return "double"; }); sm.def("overloaded", [](const py::array_t<double> &) { return "double"; });
sm.def("overloaded", [](py::array_t<float>) { return "float"; }); sm.def("overloaded", [](const py::array_t<float> &) { return "float"; });
sm.def("overloaded", [](py::array_t<int>) { return "int"; }); sm.def("overloaded", [](const py::array_t<int> &) { return "int"; });
sm.def("overloaded", [](py::array_t<unsigned short>) { return "unsigned short"; }); sm.def("overloaded", [](const py::array_t<unsigned short> &) { return "unsigned short"; });
sm.def("overloaded", [](py::array_t<long long>) { return "long long"; }); sm.def("overloaded", [](const py::array_t<long long> &) { return "long long"; });
sm.def("overloaded", [](py::array_t<std::complex<double>>) { return "double complex"; }); sm.def("overloaded",
sm.def("overloaded", [](py::array_t<std::complex<float>>) { return "float complex"; }); [](const py::array_t<std::complex<double>> &) { return "double complex"; });
sm.def("overloaded", [](const py::array_t<std::complex<float>> &) { return "float complex"; });
sm.def("overloaded2", [](py::array_t<std::complex<double>>) { return "double complex"; }); sm.def("overloaded2",
sm.def("overloaded2", [](py::array_t<double>) { return "double"; }); [](const py::array_t<std::complex<double>> &) { return "double complex"; });
sm.def("overloaded2", [](py::array_t<std::complex<float>>) { return "float complex"; }); sm.def("overloaded2", [](const py::array_t<double> &) { return "double"; });
sm.def("overloaded2", [](py::array_t<float>) { return "float"; }); sm.def("overloaded2",
[](const py::array_t<std::complex<float>> &) { return "float complex"; });
sm.def("overloaded2", [](const py::array_t<float> &) { return "float"; });
// [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works. // [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works.
// Only accept the exact types: // Only accept the exact types:
sm.def("overloaded3", [](py::array_t<int>) { return "int"; }, py::arg{}.noconvert()); sm.def(
sm.def("overloaded3", [](py::array_t<double>) { return "double"; }, py::arg{}.noconvert()); "overloaded3", [](const py::array_t<int> &) { return "int"; }, py::arg{}.noconvert());
sm.def(
"overloaded3",
[](const py::array_t<double> &) { return "double"; },
py::arg{}.noconvert());
// Make sure we don't do unsafe coercion (e.g. float to int) when not using forcecast, but // Make sure we don't do unsafe coercion (e.g. float to int) when not using forcecast, but
// rather that float gets converted via the safe (conversion to double) overload: // rather that float gets converted via the safe (conversion to double) overload:
sm.def("overloaded4", [](py::array_t<long long, 0>) { return "long long"; }); sm.def("overloaded4", [](const py::array_t<long long, 0> &) { return "long long"; });
sm.def("overloaded4", [](py::array_t<double, 0>) { return "double"; }); sm.def("overloaded4", [](const py::array_t<double, 0> &) { return "double"; });
// But we do allow conversion to int if forcecast is enabled (but only if no overload matches // But we do allow conversion to int if forcecast is enabled (but only if no overload matches
// without conversion) // without conversion)
sm.def("overloaded5", [](py::array_t<unsigned int>) { return "unsigned int"; }); sm.def("overloaded5", [](const py::array_t<unsigned int> &) { return "unsigned int"; });
sm.def("overloaded5", [](py::array_t<double>) { return "double"; }); sm.def("overloaded5", [](const py::array_t<double> &) { return "double"; });
// test_greedy_string_overload // test_greedy_string_overload
// Issue 685: ndarray shouldn't go to std::string overload // Issue 685: ndarray shouldn't go to std::string overload
sm.def("issue685", [](std::string) { return "string"; }); sm.def("issue685", [](const std::string &) { return "string"; });
sm.def("issue685", [](py::array) { return "array"; }); sm.def("issue685", [](const py::array &) { return "array"; });
sm.def("issue685", [](py::object) { return "other"; }); sm.def("issue685", [](const py::object &) { return "other"; });
// test_array_unchecked_fixed_dims // test_array_unchecked_fixed_dims
sm.def("proxy_add2", [](py::array_t<double> a, double v) { sm.def("proxy_add2", [](py::array_t<double> a, double v) {
@ -306,7 +315,7 @@ TEST_SUBMODULE(numpy_array, sm) {
r(i, j, k) = start++; r(i, j, k) = start++;
return a; return a;
}); });
sm.def("proxy_squared_L2_norm", [](py::array_t<double> a) { sm.def("proxy_squared_L2_norm", [](const py::array_t<double> &a) {
auto r = a.unchecked<1>(); auto r = a.unchecked<1>();
double sumsq = 0; double sumsq = 0;
for (py::ssize_t i = 0; i < r.shape(0); i++) for (py::ssize_t i = 0; i < r.shape(0); i++)
@ -396,45 +405,58 @@ TEST_SUBMODULE(numpy_array, sm) {
return a; return a;
}); });
sm.def("index_using_ellipsis", [](py::array a) { sm.def("index_using_ellipsis",
return a[py::make_tuple(0, py::ellipsis(), 0)]; [](const py::array &a) { return a[py::make_tuple(0, py::ellipsis(), 0)]; });
});
// test_argument_conversions // test_argument_conversions
sm.def("accept_double", sm.def(
[](py::array_t<double, 0>) {}, "accept_double", [](const py::array_t<double, 0> &) {}, py::arg("a"));
sm.def(
"accept_double_forcecast",
[](const py::array_t<double, py::array::forcecast> &) {},
py::arg("a")); py::arg("a"));
sm.def("accept_double_forcecast", sm.def(
[](py::array_t<double, py::array::forcecast>) {}, "accept_double_c_style",
[](const py::array_t<double, py::array::c_style> &) {},
py::arg("a")); py::arg("a"));
sm.def("accept_double_c_style", sm.def(
[](py::array_t<double, py::array::c_style>) {}, "accept_double_c_style_forcecast",
[](const py::array_t<double, py::array::forcecast | py::array::c_style> &) {},
py::arg("a")); py::arg("a"));
sm.def("accept_double_c_style_forcecast", sm.def(
[](py::array_t<double, py::array::forcecast | py::array::c_style>) {}, "accept_double_f_style",
[](const py::array_t<double, py::array::f_style> &) {},
py::arg("a")); py::arg("a"));
sm.def("accept_double_f_style", sm.def(
[](py::array_t<double, py::array::f_style>) {}, "accept_double_f_style_forcecast",
[](const py::array_t<double, py::array::forcecast | py::array::f_style> &) {},
py::arg("a")); py::arg("a"));
sm.def("accept_double_f_style_forcecast", sm.def(
[](py::array_t<double, py::array::forcecast | py::array::f_style>) {}, "accept_double_noconvert", [](const py::array_t<double, 0> &) {}, "a"_a.noconvert());
py::arg("a")); sm.def(
sm.def("accept_double_noconvert", "accept_double_forcecast_noconvert",
[](py::array_t<double, 0>) {}, [](const py::array_t<double, py::array::forcecast> &) {},
"a"_a.noconvert()); "a"_a.noconvert());
sm.def("accept_double_forcecast_noconvert", sm.def(
[](py::array_t<double, py::array::forcecast>) {}, "accept_double_c_style_noconvert",
[](const py::array_t<double, py::array::c_style> &) {},
"a"_a.noconvert()); "a"_a.noconvert());
sm.def("accept_double_c_style_noconvert", sm.def(
[](py::array_t<double, py::array::c_style>) {}, "accept_double_c_style_forcecast_noconvert",
[](const py::array_t<double, py::array::forcecast | py::array::c_style> &) {},
"a"_a.noconvert()); "a"_a.noconvert());
sm.def("accept_double_c_style_forcecast_noconvert", sm.def(
[](py::array_t<double, py::array::forcecast | py::array::c_style>) {}, "accept_double_f_style_noconvert",
[](const py::array_t<double, py::array::f_style> &) {},
"a"_a.noconvert()); "a"_a.noconvert());
sm.def("accept_double_f_style_noconvert", sm.def(
[](py::array_t<double, py::array::f_style>) {}, "accept_double_f_style_forcecast_noconvert",
"a"_a.noconvert()); [](const py::array_t<double, py::array::forcecast | py::array::f_style> &) {},
sm.def("accept_double_f_style_forcecast_noconvert",
[](py::array_t<double, py::array::forcecast | py::array::f_style>) {},
"a"_a.noconvert()); "a"_a.noconvert());
// Check that types returns correct npy format descriptor
sm.def("test_fmt_desc_float", [](const py::array_t<float> &) {});
sm.def("test_fmt_desc_double", [](const py::array_t<double> &) {});
sm.def("test_fmt_desc_const_float", [](const py::array_t<const float> &) {});
sm.def("test_fmt_desc_const_double", [](const py::array_t<const double> &) {});
} }

View File

@ -482,6 +482,19 @@ def test_index_using_ellipsis():
assert a.shape == (6,) assert a.shape == (6,)
@pytest.mark.parametrize(
"test_func",
[
m.test_fmt_desc_float,
m.test_fmt_desc_double,
m.test_fmt_desc_const_float,
m.test_fmt_desc_const_double,
],
)
def test_format_descriptors_for_floating_point_types(test_func):
assert "numpy.ndarray[numpy.float" in test_func.__doc__
@pytest.mark.parametrize("forcecast", [False, True]) @pytest.mark.parametrize("forcecast", [False, True])
@pytest.mark.parametrize("contiguity", [None, "C", "F"]) @pytest.mark.parametrize("contiguity", [None, "C", "F"])
@pytest.mark.parametrize("noconvert", [False, True]) @pytest.mark.parametrize("noconvert", [False, True])

View File

@ -108,9 +108,11 @@ PYBIND11_PACKED(struct EnumStruct {
std::ostream& operator<<(std::ostream& os, const StringStruct& v) { std::ostream& operator<<(std::ostream& os, const StringStruct& v) {
os << "a='"; os << "a='";
for (size_t i = 0; i < 3 && v.a[i]; i++) os << v.a[i]; for (size_t i = 0; i < 3 && (v.a[i] != 0); i++)
os << v.a[i];
os << "',b='"; os << "',b='";
for (size_t i = 0; i < 3 && v.b[i]; i++) os << v.b[i]; for (size_t i = 0; i < 3 && (v.b[i] != 0); i++)
os << v.b[i];
return os << "'"; return os << "'";
} }
@ -266,10 +268,11 @@ TEST_SUBMODULE(numpy_dtypes, m) {
.def_readwrite("uint_", &SimpleStruct::uint_) .def_readwrite("uint_", &SimpleStruct::uint_)
.def_readwrite("float_", &SimpleStruct::float_) .def_readwrite("float_", &SimpleStruct::float_)
.def_readwrite("ldbl_", &SimpleStruct::ldbl_) .def_readwrite("ldbl_", &SimpleStruct::ldbl_)
.def("astuple", [](const SimpleStruct& self) { .def("astuple",
[](const SimpleStruct &self) {
return py::make_tuple(self.bool_, self.uint_, self.float_, self.ldbl_); return py::make_tuple(self.bool_, self.uint_, self.float_, self.ldbl_);
}) })
.def_static("fromtuple", [](const py::tuple tup) { .def_static("fromtuple", [](const py::tuple &tup) {
if (py::len(tup) != 4) { if (py::len(tup) != 4) {
throw py::cast_error("Invalid size"); throw py::cast_error("Invalid size");
} }

View File

@ -11,6 +11,8 @@
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include <pybind11/numpy.h> #include <pybind11/numpy.h>
#include <utility>
double my_func(int x, float y, double z) { double my_func(int x, float y, double z) {
py::print("my_func(x:int={}, y:float={:.0f}, z:float={:.0f})"_s.format(x, y, z)); py::print("my_func(x:int={}, y:float={:.0f}, z:float={:.0f})"_s.format(x, y, z));
return (float) x*y*z; return (float) x*y*z;
@ -25,11 +27,10 @@ TEST_SUBMODULE(numpy_vectorize, m) {
m.def("vectorized_func", py::vectorize(my_func)); m.def("vectorized_func", py::vectorize(my_func));
// Vectorize a lambda function with a capture object (e.g. to exclude some arguments from the vectorization) // Vectorize a lambda function with a capture object (e.g. to exclude some arguments from the vectorization)
m.def("vectorized_func2", m.def("vectorized_func2", [](py::array_t<int> x, py::array_t<float> y, float z) {
[](py::array_t<int> x, py::array_t<float> y, float z) { return py::vectorize([z](int x, float y) { return my_func(x, y, z); })(std::move(x),
return py::vectorize([z](int x, float y) { return my_func(x, y, z); })(x, y); std::move(y));
} });
);
// Vectorize a complex-valued function // Vectorize a complex-valued function
m.def("vectorized_func3", py::vectorize( m.def("vectorized_func3", py::vectorize(
@ -38,10 +39,14 @@ TEST_SUBMODULE(numpy_vectorize, m) {
// test_type_selection // test_type_selection
// NumPy function which only accepts specific data types // NumPy function which only accepts specific data types
m.def("selective_func", [](py::array_t<int, py::array::c_style>) { return "Int branch taken."; }); // A lot of these no lints could be replaced with const refs, and probably should at some point.
m.def("selective_func", [](py::array_t<float, py::array::c_style>) { return "Float branch taken."; }); m.def("selective_func",
m.def("selective_func", [](py::array_t<std::complex<float>, py::array::c_style>) { return "Complex float branch taken."; }); [](const py::array_t<int, py::array::c_style> &) { return "Int branch taken."; });
m.def("selective_func",
[](const py::array_t<float, py::array::c_style> &) { return "Float branch taken."; });
m.def("selective_func", [](const py::array_t<std::complex<float>, py::array::c_style> &) {
return "Complex float branch taken.";
});
// test_passthrough_arguments // test_passthrough_arguments
// Passthrough test: references and non-pod types should be automatically passed through (in the // Passthrough test: references and non-pod types should be automatically passed through (in the
@ -53,16 +58,21 @@ TEST_SUBMODULE(numpy_vectorize, m) {
py::class_<NonPODClass>(m, "NonPODClass") py::class_<NonPODClass>(m, "NonPODClass")
.def(py::init<int>()) .def(py::init<int>())
.def_readwrite("value", &NonPODClass::value); .def_readwrite("value", &NonPODClass::value);
m.def("vec_passthrough", py::vectorize( m.def("vec_passthrough",
[](double *a, double b, py::array_t<double> c, const int &d, int &e, NonPODClass f, const double g) { py::vectorize([](const double *a,
return *a + b + c.at(0) + d + e + f.value + g; double b,
} // Changing this broke things
)); // NOLINTNEXTLINE(performance-unnecessary-value-param)
py::array_t<double> c,
const int &d,
int &e,
NonPODClass f,
const double g) { return *a + b + c.at(0) + d + e + f.value + g; }));
// test_method_vectorization // test_method_vectorization
struct VectorizeTestClass { struct VectorizeTestClass {
VectorizeTestClass(int v) : value{v} {}; VectorizeTestClass(int v) : value{v} {};
float method(int x, float y) { return y + (float) (x + value); } float method(int x, float y) const { return y + (float) (x + value); }
int value = 0; int value = 0;
}; };
py::class_<VectorizeTestClass> vtc(m, "VectorizeTestClass"); py::class_<VectorizeTestClass> vtc(m, "VectorizeTestClass");
@ -78,14 +88,14 @@ TEST_SUBMODULE(numpy_vectorize, m) {
.value("f_trivial", py::detail::broadcast_trivial::f_trivial) .value("f_trivial", py::detail::broadcast_trivial::f_trivial)
.value("c_trivial", py::detail::broadcast_trivial::c_trivial) .value("c_trivial", py::detail::broadcast_trivial::c_trivial)
.value("non_trivial", py::detail::broadcast_trivial::non_trivial); .value("non_trivial", py::detail::broadcast_trivial::non_trivial);
m.def("vectorized_is_trivial", []( m.def("vectorized_is_trivial",
py::array_t<int, py::array::forcecast> arg1, [](const py::array_t<int, py::array::forcecast> &arg1,
py::array_t<float, py::array::forcecast> arg2, const py::array_t<float, py::array::forcecast> &arg2,
py::array_t<double, py::array::forcecast> arg3 const py::array_t<double, py::array::forcecast> &arg3) {
) { py::ssize_t ndim = 0;
py::ssize_t ndim;
std::vector<py::ssize_t> shape; std::vector<py::ssize_t> shape;
std::array<py::buffer_info, 3> buffers {{ arg1.request(), arg2.request(), arg3.request() }}; std::array<py::buffer_info, 3> buffers{
{arg1.request(), arg2.request(), arg3.request()}};
return py::detail::broadcast(buffers, ndim, shape); return py::detail::broadcast(buffers, ndim, shape);
}); });

View File

@ -44,7 +44,7 @@ TEST_SUBMODULE(opaque_types, m) {
m.def("print_opaque_list", [](const StringList &l) { m.def("print_opaque_list", [](const StringList &l) {
std::string ret = "Opaque list: ["; std::string ret = "Opaque list: [";
bool first = true; bool first = true;
for (auto entry : l) { for (const auto &entry : l) {
if (!first) if (!first)
ret += ", "; ret += ", ";
ret += entry; ret += entry;

View File

@ -16,9 +16,18 @@ class Vector2 {
public: public:
Vector2(float x, float y) : x(x), y(y) { print_created(this, toString()); } Vector2(float x, float y) : x(x), y(y) { print_created(this, toString()); }
Vector2(const Vector2 &v) : x(v.x), y(v.y) { print_copy_created(this); } Vector2(const Vector2 &v) : x(v.x), y(v.y) { print_copy_created(this); }
Vector2(Vector2 &&v) : x(v.x), y(v.y) { print_move_created(this); v.x = v.y = 0; } Vector2(Vector2 &&v) noexcept : x(v.x), y(v.y) {
print_move_created(this);
v.x = v.y = 0;
}
Vector2 &operator=(const Vector2 &v) { x = v.x; y = v.y; print_copy_assigned(this); return *this; } Vector2 &operator=(const Vector2 &v) { x = v.x; y = v.y; print_copy_assigned(this); return *this; }
Vector2 &operator=(Vector2 &&v) { x = v.x; y = v.y; v.x = v.y = 0; print_move_assigned(this); return *this; } Vector2 &operator=(Vector2 &&v) noexcept {
x = v.x;
y = v.y;
v.x = v.y = 0;
print_move_assigned(this);
return *this;
}
~Vector2() { print_destroyed(this); } ~Vector2() { print_destroyed(this); }
std::string toString() const { return "[" + std::to_string(x) + ", " + std::to_string(y) + "]"; } std::string toString() const { return "[" + std::to_string(x) + ", " + std::to_string(y) + "]"; }
@ -80,8 +89,8 @@ std::string abs(const Vector2&) {
return "abs(Vector2)"; return "abs(Vector2)";
} }
// MSVC warns about unknown pragmas, and warnings are errors. // MSVC & Intel warns about unknown pragmas, and warnings are errors.
#ifndef _MSC_VER #if !defined(_MSC_VER) && !defined(__INTEL_COMPILER)
#pragma GCC diagnostic push #pragma GCC diagnostic push
// clang 7.0.0 and Apple LLVM 10.0.1 introduce `-Wself-assign-overloaded` to // 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 `). // `-Wall`, which is used here for overloading (e.g. `py::self += py::self `).
@ -221,6 +230,6 @@ TEST_SUBMODULE(operators, m) {
.def(py::self == py::self); .def(py::self == py::self);
} }
#ifndef _MSC_VER #if !defined(_MSC_VER) && !defined(__INTEL_COMPILER)
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif

View File

@ -1,7 +1,9 @@
// clang-format off
/* /*
tests/test_pickling.cpp -- pickle support tests/test_pickling.cpp -- pickle support
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch> Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
Copyright (c) 2021 The Pybind Development Team.
All rights reserved. Use of this source code is governed by a All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
@ -9,6 +11,58 @@
#include "pybind11_tests.h" #include "pybind11_tests.h"
// clang-format on
#include <memory>
#include <stdexcept>
#include <utility>
namespace exercise_trampoline {
struct SimpleBase {
int num = 0;
virtual ~SimpleBase() = default;
// For compatibility with old clang versions:
SimpleBase() = default;
SimpleBase(const SimpleBase &) = default;
};
struct SimpleBaseTrampoline : SimpleBase {};
struct SimpleCppDerived : SimpleBase {};
void wrap(py::module m) {
py::class_<SimpleBase, SimpleBaseTrampoline>(m, "SimpleBase")
.def(py::init<>())
.def_readwrite("num", &SimpleBase::num)
.def(py::pickle(
[](const py::object &self) {
py::dict d;
if (py::hasattr(self, "__dict__"))
d = self.attr("__dict__");
return py::make_tuple(self.attr("num"), d);
},
[](const py::tuple &t) {
if (t.size() != 2)
throw std::runtime_error("Invalid state!");
auto cpp_state = std::unique_ptr<SimpleBase>(new SimpleBaseTrampoline);
cpp_state->num = t[0].cast<int>();
auto py_state = t[1].cast<py::dict>();
return std::make_pair(std::move(cpp_state), py_state);
}));
m.def("make_SimpleCppDerivedAsBase",
[]() { return std::unique_ptr<SimpleBase>(new SimpleCppDerived); });
m.def("check_dynamic_cast_SimpleCppDerived", [](const SimpleBase *base_ptr) {
return dynamic_cast<const SimpleCppDerived *>(base_ptr) != nullptr;
});
}
} // namespace exercise_trampoline
// clang-format off
TEST_SUBMODULE(pickling, m) { TEST_SUBMODULE(pickling, m) {
// test_roundtrip // test_roundtrip
class Pickleable { class Pickleable {
@ -46,8 +100,7 @@ TEST_SUBMODULE(pickling, m) {
return py::make_tuple(p.value(), p.extra1(), p.extra2()); return py::make_tuple(p.value(), p.extra1(), p.extra2());
}); });
ignoreOldStyleInitWarnings([&pyPickleable]() { ignoreOldStyleInitWarnings([&pyPickleable]() {
pyPickleable pyPickleable.def("__setstate__", [](Pickleable &p, const py::tuple &t) {
.def("__setstate__", [](Pickleable &p, py::tuple t) {
if (t.size() != 3) if (t.size() != 3)
throw std::runtime_error("Invalid state!"); throw std::runtime_error("Invalid state!");
/* Invoke the constructor (need to use in-place version) */ /* Invoke the constructor (need to use in-place version) */
@ -65,7 +118,7 @@ TEST_SUBMODULE(pickling, m) {
[](const PickleableNew &p) { [](const PickleableNew &p) {
return py::make_tuple(p.value(), p.extra1(), p.extra2()); return py::make_tuple(p.value(), p.extra1(), p.extra2());
}, },
[](py::tuple t) { [](const py::tuple &t) {
if (t.size() != 3) if (t.size() != 3)
throw std::runtime_error("Invalid state!"); throw std::runtime_error("Invalid state!");
auto p = PickleableNew(t[0].cast<std::string>()); auto p = PickleableNew(t[0].cast<std::string>());
@ -73,8 +126,7 @@ TEST_SUBMODULE(pickling, m) {
p.setExtra1(t[1].cast<int>()); p.setExtra1(t[1].cast<int>());
p.setExtra2(t[2].cast<int>()); p.setExtra2(t[2].cast<int>());
return p; return p;
} }));
));
#if !defined(PYPY_VERSION) #if !defined(PYPY_VERSION)
// test_roundtrip_with_dict // test_roundtrip_with_dict
@ -92,21 +144,19 @@ TEST_SUBMODULE(pickling, m) {
}; };
py::class_<PickleableWithDict> pyPickleableWithDict(m, "PickleableWithDict", py::dynamic_attr()); py::class_<PickleableWithDict> pyPickleableWithDict(m, "PickleableWithDict", py::dynamic_attr());
pyPickleableWithDict pyPickleableWithDict.def(py::init<std::string>())
.def(py::init<std::string>())
.def_readwrite("value", &PickleableWithDict::value) .def_readwrite("value", &PickleableWithDict::value)
.def_readwrite("extra", &PickleableWithDict::extra) .def_readwrite("extra", &PickleableWithDict::extra)
.def("__getstate__", [](py::object self) { .def("__getstate__", [](const py::object &self) {
/* Also include __dict__ in state */ /* Also include __dict__ in state */
return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__")); return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__"));
}); });
ignoreOldStyleInitWarnings([&pyPickleableWithDict]() { ignoreOldStyleInitWarnings([&pyPickleableWithDict]() {
pyPickleableWithDict pyPickleableWithDict.def("__setstate__", [](const py::object &self, const py::tuple &t) {
.def("__setstate__", [](py::object self, py::tuple t) {
if (t.size() != 3) if (t.size() != 3)
throw std::runtime_error("Invalid state!"); throw std::runtime_error("Invalid state!");
/* Cast and construct */ /* Cast and construct */
auto& p = self.cast<PickleableWithDict&>(); auto &p = self.cast<PickleableWithDict &>();
new (&p) PickleableWithDict(t[0].cast<std::string>()); new (&p) PickleableWithDict(t[0].cast<std::string>());
/* Assign C++ state */ /* Assign C++ state */
@ -120,7 +170,7 @@ TEST_SUBMODULE(pickling, m) {
py::class_<PickleableWithDictNew, PickleableWithDict>(m, "PickleableWithDictNew") py::class_<PickleableWithDictNew, PickleableWithDict>(m, "PickleableWithDictNew")
.def(py::init<std::string>()) .def(py::init<std::string>())
.def(py::pickle( .def(py::pickle(
[](py::object self) { [](const py::object &self) {
return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__")); return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__"));
}, },
[](const py::tuple &t) { [](const py::tuple &t) {
@ -132,7 +182,8 @@ TEST_SUBMODULE(pickling, m) {
auto py_state = t[2].cast<py::dict>(); auto py_state = t[2].cast<py::dict>();
return std::make_pair(cpp_state, py_state); return std::make_pair(cpp_state, py_state);
} }));
));
#endif #endif
exercise_trampoline::wrap(m);
} }

View File

@ -45,3 +45,39 @@ def test_enum_pickle():
data = pickle.dumps(e.EOne, 2) data = pickle.dumps(e.EOne, 2)
assert e.EOne == pickle.loads(data) assert e.EOne == pickle.loads(data)
#
# exercise_trampoline
#
class SimplePyDerived(m.SimpleBase):
pass
def test_roundtrip_simple_py_derived():
p = SimplePyDerived()
p.num = 202
p.stored_in_dict = 303
data = pickle.dumps(p, pickle.HIGHEST_PROTOCOL)
p2 = pickle.loads(data)
assert isinstance(p2, SimplePyDerived)
assert p2.num == 202
assert p2.stored_in_dict == 303
def test_roundtrip_simple_cpp_derived():
p = m.make_SimpleCppDerivedAsBase()
assert m.check_dynamic_cast_SimpleCppDerived(p)
p.num = 404
if not env.PYPY:
# To ensure that this unit test is not accidentally invalidated.
with pytest.raises(AttributeError):
# Mimics the `setstate` C++ implementation.
setattr(p, "__dict__", {}) # noqa: B010
data = pickle.dumps(p, pickle.HIGHEST_PROTOCOL)
p2 = pickle.loads(data)
assert isinstance(p2, m.SimpleBase)
assert p2.num == 404
# Issue #3062: pickleable base C++ classes can incur object slicing
# if derived typeid is not registered with pybind11
assert not m.check_dynamic_cast_SimpleCppDerived(p2)

View File

@ -7,6 +7,8 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include <utility>
#include "pybind11_tests.h" #include "pybind11_tests.h"
@ -27,16 +29,14 @@ TEST_SUBMODULE(pytypes, m) {
list.insert(2, "inserted-2"); list.insert(2, "inserted-2");
return list; return list;
}); });
m.def("print_list", [](py::list list) { m.def("print_list", [](const py::list &list) {
int index = 0; int index = 0;
for (auto item : list) for (auto item : list)
py::print("list item {}: {}"_s.format(index++, item)); py::print("list item {}: {}"_s.format(index++, item));
}); });
// test_none // test_none
m.def("get_none", []{return py::none();}); m.def("get_none", []{return py::none();});
m.def("print_none", [](py::none none) { m.def("print_none", [](const py::none &none) { py::print("none: {}"_s.format(none)); });
py::print("none: {}"_s.format(none));
});
// test_set // test_set
m.def("get_set", []() { m.def("get_set", []() {
@ -46,20 +46,17 @@ TEST_SUBMODULE(pytypes, m) {
set.add(std::string("key3")); set.add(std::string("key3"));
return set; return set;
}); });
m.def("print_set", [](py::set set) { m.def("print_set", [](const py::set &set) {
for (auto item : set) for (auto item : set)
py::print("key:", item); py::print("key:", item);
}); });
m.def("set_contains", [](py::set set, py::object key) { m.def("set_contains",
return set.contains(key); [](const py::set &set, const py::object &key) { return set.contains(key); });
}); m.def("set_contains", [](const py::set &set, const char *key) { return set.contains(key); });
m.def("set_contains", [](py::set set, const char* key) {
return set.contains(key);
});
// test_dict // test_dict
m.def("get_dict", []() { return py::dict("key"_a="value"); }); m.def("get_dict", []() { return py::dict("key"_a="value"); });
m.def("print_dict", [](py::dict dict) { m.def("print_dict", [](const py::dict &dict) {
for (auto item : dict) for (auto item : dict)
py::print("key: {}, value={}"_s.format(item.first, item.second)); py::print("key: {}, value={}"_s.format(item.first, item.second));
}); });
@ -68,12 +65,10 @@ TEST_SUBMODULE(pytypes, m) {
auto d2 = py::dict("z"_a=3, **d1); auto d2 = py::dict("z"_a=3, **d1);
return d2; return d2;
}); });
m.def("dict_contains", [](py::dict dict, py::object val) { m.def("dict_contains",
return dict.contains(val); [](const py::dict &dict, py::object val) { return dict.contains(val); });
}); m.def("dict_contains",
m.def("dict_contains", [](py::dict dict, const char* val) { [](const py::dict &dict, const char *val) { return dict.contains(val); });
return dict.contains(val);
});
// test_str // test_str
m.def("str_from_string", []() { return py::str(std::string("baz")); }); m.def("str_from_string", []() { return py::str(std::string("baz")); });
@ -81,6 +76,9 @@ TEST_SUBMODULE(pytypes, m) {
m.def("str_from_object", [](const py::object& obj) { return py::str(obj); }); m.def("str_from_object", [](const py::object& obj) { return py::str(obj); });
m.def("repr_from_object", [](const py::object& obj) { return py::repr(obj); }); m.def("repr_from_object", [](const py::object& obj) { return py::repr(obj); });
m.def("str_from_handle", [](py::handle h) { return py::str(h); }); m.def("str_from_handle", [](py::handle h) { return py::str(h); });
m.def("str_from_string_from_str", [](const py::str& obj) {
return py::str(static_cast<std::string>(obj));
});
m.def("str_format", []() { m.def("str_format", []() {
auto s1 = "{} + {} = {}"_s.format(1, 2, 3); auto s1 = "{} + {} = {}"_s.format(1, 2, 3);
@ -137,7 +135,7 @@ TEST_SUBMODULE(pytypes, m) {
}); });
// test_accessors // test_accessors
m.def("accessor_api", [](py::object o) { m.def("accessor_api", [](const py::object &o) {
auto d = py::dict(); auto d = py::dict();
d["basic_attr"] = o.attr("basic_attr"); d["basic_attr"] = o.attr("basic_attr");
@ -178,7 +176,7 @@ TEST_SUBMODULE(pytypes, m) {
return d; return d;
}); });
m.def("tuple_accessor", [](py::tuple existing_t) { m.def("tuple_accessor", [](const py::tuple &existing_t) {
try { try {
existing_t[0] = 1; existing_t[0] = 1;
} catch (const py::error_already_set &) { } catch (const py::error_already_set &) {
@ -226,7 +224,7 @@ TEST_SUBMODULE(pytypes, m) {
); );
}); });
m.def("converting_constructors", [](py::dict d) { m.def("converting_constructors", [](const py::dict &d) {
return py::dict( return py::dict(
"bytes"_a=py::bytes(d["bytes"]), "bytes"_a=py::bytes(d["bytes"]),
"bytearray"_a=py::bytearray(d["bytearray"]), "bytearray"_a=py::bytearray(d["bytearray"]),
@ -242,7 +240,7 @@ TEST_SUBMODULE(pytypes, m) {
); );
}); });
m.def("cast_functions", [](py::dict d) { m.def("cast_functions", [](const py::dict &d) {
// When converting between Python types, obj.cast<T>() should be the same as T(obj) // When converting between Python types, obj.cast<T>() should be the same as T(obj)
return py::dict( return py::dict(
"bytes"_a=d["bytes"].cast<py::bytes>(), "bytes"_a=d["bytes"].cast<py::bytes>(),
@ -259,19 +257,20 @@ TEST_SUBMODULE(pytypes, m) {
); );
}); });
m.def("convert_to_pybind11_str", [](py::object o) { return py::str(o); }); m.def("convert_to_pybind11_str", [](const py::object &o) { return py::str(o); });
m.def("nonconverting_constructor", [](std::string type, py::object value, bool move) -> py::object { m.def("nonconverting_constructor",
[](const std::string &type, py::object value, bool move) -> py::object {
if (type == "bytes") { if (type == "bytes") {
return move ? py::bytes(std::move(value)) : py::bytes(value); return move ? py::bytes(std::move(value)) : py::bytes(value);
} }
else if (type == "none") { if (type == "none") {
return move ? py::none(std::move(value)) : py::none(value); return move ? py::none(std::move(value)) : py::none(value);
} }
else if (type == "ellipsis") { if (type == "ellipsis") {
return move ? py::ellipsis(std::move(value)) : py::ellipsis(value); return move ? py::ellipsis(std::move(value)) : py::ellipsis(value);
} }
else if (type == "type") { if (type == "type") {
return move ? py::type(std::move(value)) : py::type(value); return move ? py::type(std::move(value)) : py::type(value);
} }
throw std::runtime_error("Invalid type"); throw std::runtime_error("Invalid type");
@ -333,9 +332,9 @@ TEST_SUBMODULE(pytypes, m) {
m.def("print_failure", []() { py::print(42, UnregisteredType()); }); m.def("print_failure", []() { py::print(42, UnregisteredType()); });
m.def("hash_function", [](py::object obj) { return py::hash(obj); }); m.def("hash_function", [](py::object obj) { return py::hash(std::move(obj)); });
m.def("test_number_protocol", [](py::object a, py::object b) { m.def("test_number_protocol", [](const py::object &a, const py::object &b) {
py::list l; py::list l;
l.append(a.equal(b)); l.append(a.equal(b));
l.append(a.not_equal(b)); l.append(a.not_equal(b));
@ -355,9 +354,7 @@ TEST_SUBMODULE(pytypes, m) {
return l; return l;
}); });
m.def("test_list_slicing", [](py::list a) { m.def("test_list_slicing", [](const py::list &a) { return a[py::slice(0, -1, 2)]; });
return a[py::slice(0, -1, 2)];
});
// See #2361 // See #2361
m.def("issue2361_str_implicit_copy_none", []() { m.def("issue2361_str_implicit_copy_none", []() {
@ -369,13 +366,10 @@ TEST_SUBMODULE(pytypes, m) {
return is_this_none; return is_this_none;
}); });
m.def("test_memoryview_object", [](py::buffer b) { m.def("test_memoryview_object", [](const py::buffer &b) { return py::memoryview(b); });
return py::memoryview(b);
});
m.def("test_memoryview_buffer_info", [](py::buffer b) { m.def("test_memoryview_buffer_info",
return py::memoryview(b.request()); [](const py::buffer &b) { return py::memoryview(b.request()); });
});
m.def("test_memoryview_from_buffer", [](bool is_unsigned) { m.def("test_memoryview_from_buffer", [](bool is_unsigned) {
static const int16_t si16[] = { 3, 1, 4, 1, 5 }; static const int16_t si16[] = { 3, 1, 4, 1, 5 };
@ -383,9 +377,7 @@ TEST_SUBMODULE(pytypes, m) {
if (is_unsigned) if (is_unsigned)
return py::memoryview::from_buffer( return py::memoryview::from_buffer(
ui16, { 4 }, { sizeof(uint16_t) }); ui16, { 4 }, { sizeof(uint16_t) });
else return py::memoryview::from_buffer(si16, {5}, {sizeof(int16_t)});
return py::memoryview::from_buffer(
si16, { 5 }, { sizeof(int16_t) });
}); });
m.def("test_memoryview_from_buffer_nativeformat", []() { m.def("test_memoryview_from_buffer_nativeformat", []() {
@ -425,20 +417,21 @@ TEST_SUBMODULE(pytypes, m) {
m.attr("PYBIND11_STR_LEGACY_PERMISSIVE") = true; m.attr("PYBIND11_STR_LEGACY_PERMISSIVE") = true;
#endif #endif
m.def("isinstance_pybind11_bytes", [](py::object o) { return py::isinstance<py::bytes>(o); }); m.def("isinstance_pybind11_bytes",
m.def("isinstance_pybind11_str", [](py::object o) { return py::isinstance<py::str>(o); }); [](py::object o) { return py::isinstance<py::bytes>(std::move(o)); });
m.def("isinstance_pybind11_str",
[](py::object o) { return py::isinstance<py::str>(std::move(o)); });
m.def("pass_to_pybind11_bytes", [](py::bytes b) { return py::len(b); }); m.def("pass_to_pybind11_bytes", [](py::bytes b) { return py::len(std::move(b)); });
m.def("pass_to_pybind11_str", [](py::str s) { return py::len(s); }); m.def("pass_to_pybind11_str", [](py::str s) { return py::len(std::move(s)); });
m.def("pass_to_std_string", [](std::string s) { return s.size(); }); m.def("pass_to_std_string", [](const std::string &s) { return s.size(); });
// test_weakref // test_weakref
m.def("weakref_from_handle", m.def("weakref_from_handle",
[](py::handle h) { return py::weakref(h); }); [](py::handle h) { return py::weakref(h); });
m.def("weakref_from_handle_and_function", m.def("weakref_from_handle_and_function",
[](py::handle h, py::function f) { return py::weakref(h, f); }); [](py::handle h, py::function f) { return py::weakref(h, std::move(f)); });
m.def("weakref_from_object", m.def("weakref_from_object", [](const py::object &o) { return py::weakref(o); });
[](py::object o) { return py::weakref(o); });
m.def("weakref_from_object_and_function", m.def("weakref_from_object_and_function",
[](py::object o, py::function f) { return py::weakref(o, f); }); [](py::object o, py::function f) { return py::weakref(std::move(o), std::move(f)); });
} }

View File

@ -65,7 +65,7 @@ def test_set(capture, doc):
""" """
) )
assert not m.set_contains(set([]), 42) assert not m.set_contains(set(), 42)
assert m.set_contains({42}, 42) assert m.set_contains({42}, 42)
assert m.set_contains({"foo"}, "foo") assert m.set_contains({"foo"}, "foo")
@ -133,6 +133,14 @@ def test_str(doc):
else: else:
assert m.str_from_handle(malformed_utf8) == "b'\\x80'" assert m.str_from_handle(malformed_utf8) == "b'\\x80'"
assert m.str_from_string_from_str("this is a str") == "this is a str"
ucs_surrogates_str = u"\udcc3"
if env.PY2:
assert u"\udcc3" == m.str_from_string_from_str(ucs_surrogates_str)
else:
with pytest.raises(UnicodeEncodeError):
m.str_from_string_from_str(ucs_surrogates_str)
def test_bytes(doc): def test_bytes(doc):
assert m.bytes_from_string().decode() == "foo" assert m.bytes_from_string().decode() == "foo"
@ -372,10 +380,10 @@ def test_print(capture):
with pytest.raises(RuntimeError) as excinfo: with pytest.raises(RuntimeError) as excinfo:
m.print_failure() m.print_failure()
assert str(excinfo.value) == "make_tuple(): unable to convert " + ( assert str(excinfo.value) == "Unable to convert call argument " + (
"argument of type 'UnregisteredType' to Python object" "'1' of type 'UnregisteredType' to Python object"
if debug_enabled if debug_enabled
else "arguments to Python object (compile in debug mode for details)" else "to Python object (compile in debug mode for details)"
) )
@ -448,7 +456,7 @@ def test_memoryview(method, args, fmt, expected_view):
view_as_list = list(view) view_as_list = list(view)
else: else:
# Using max to pick non-zero byte (big-endian vs little-endian). # Using max to pick non-zero byte (big-endian vs little-endian).
view_as_list = [max([ord(c) for c in s]) for s in view] view_as_list = [max(ord(c) for c in s) for s in view]
assert view_as_list == list(expected_view) assert view_as_list == list(expected_view)

View File

@ -14,6 +14,7 @@
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include <algorithm> #include <algorithm>
#include <utility>
template<typename T> template<typename T>
class NonZeroIterator { class NonZeroIterator {
@ -80,18 +81,17 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
int start,stop,step; int start,stop,step;
int size; int size;
}; };
py::class_<Sliceable>(m,"Sliceable") py::class_<Sliceable>(m, "Sliceable")
.def(py::init<int>()) .def(py::init<int>())
.def("__getitem__",[](const Sliceable &s, py::slice slice) { .def("__getitem__", [](const Sliceable &s, const py::slice &slice) {
py::ssize_t start, stop, step, slicelength; py::ssize_t start = 0, stop = 0, step = 0, slicelength = 0;
if (!slice.compute(s.size, &start, &stop, &step, &slicelength)) if (!slice.compute(s.size, &start, &stop, &step, &slicelength))
throw py::error_already_set(); throw py::error_already_set();
int istart = static_cast<int>(start); int istart = static_cast<int>(start);
int istop = static_cast<int>(stop); int istop = static_cast<int>(stop);
int istep = static_cast<int>(step); int istep = static_cast<int>(step);
return std::make_tuple(istart,istop,istep); return std::make_tuple(istart, istop, istep);
}) });
;
// test_sequence // test_sequence
class Sequence { class Sequence {
@ -111,7 +111,7 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
m_data = new float[m_size]; m_data = new float[m_size];
memcpy(m_data, s.m_data, sizeof(float)*m_size); memcpy(m_data, s.m_data, sizeof(float)*m_size);
} }
Sequence(Sequence &&s) : m_size(s.m_size), m_data(s.m_data) { Sequence(Sequence &&s) noexcept : m_size(s.m_size), m_data(s.m_data) {
print_move_created(this); print_move_created(this);
s.m_size = 0; s.m_size = 0;
s.m_data = nullptr; s.m_data = nullptr;
@ -130,7 +130,7 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
return *this; return *this;
} }
Sequence &operator=(Sequence &&s) { Sequence &operator=(Sequence &&s) noexcept {
if (&s != this) { if (&s != this) {
delete[] m_data; delete[] m_data;
m_size = s.m_size; m_size = s.m_size;
@ -179,41 +179,52 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
}; };
py::class_<Sequence>(m, "Sequence") py::class_<Sequence>(m, "Sequence")
.def(py::init<size_t>()) .def(py::init<size_t>())
.def(py::init<const std::vector<float>&>()) .def(py::init<const std::vector<float> &>())
/// Bare bones interface /// Bare bones interface
.def("__getitem__", [](const Sequence &s, size_t i) { .def("__getitem__",
if (i >= s.size()) throw py::index_error(); [](const Sequence &s, size_t i) {
if (i >= s.size())
throw py::index_error();
return s[i]; return s[i];
}) })
.def("__setitem__", [](Sequence &s, size_t i, float v) { .def("__setitem__",
if (i >= s.size()) throw py::index_error(); [](Sequence &s, size_t i, float v) {
if (i >= s.size())
throw py::index_error();
s[i] = v; s[i] = v;
}) })
.def("__len__", &Sequence::size) .def("__len__", &Sequence::size)
/// Optional sequence protocol operations /// Optional sequence protocol operations
.def("__iter__", [](const Sequence &s) { return py::make_iterator(s.begin(), s.end()); }, .def(
"__iter__",
[](const Sequence &s) { return py::make_iterator(s.begin(), s.end()); },
py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */) py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */)
.def("__contains__", [](const Sequence &s, float v) { return s.contains(v); }) .def("__contains__", [](const Sequence &s, float v) { return s.contains(v); })
.def("__reversed__", [](const Sequence &s) -> Sequence { return s.reversed(); }) .def("__reversed__", [](const Sequence &s) -> Sequence { return s.reversed(); })
/// Slicing protocol (optional) /// Slicing protocol (optional)
.def("__getitem__", [](const Sequence &s, py::slice slice) -> Sequence* { .def("__getitem__",
size_t start, stop, step, slicelength; [](const Sequence &s, const py::slice &slice) -> Sequence * {
size_t start = 0, stop = 0, step = 0, slicelength = 0;
if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) if (!slice.compute(s.size(), &start, &stop, &step, &slicelength))
throw py::error_already_set(); throw py::error_already_set();
auto *seq = new Sequence(slicelength); auto *seq = new Sequence(slicelength);
for (size_t i = 0; i < slicelength; ++i) { for (size_t i = 0; i < slicelength; ++i) {
(*seq)[i] = s[start]; start += step; (*seq)[i] = s[start];
start += step;
} }
return seq; return seq;
}) })
.def("__setitem__", [](Sequence &s, py::slice slice, const Sequence &value) { .def("__setitem__",
size_t start, stop, step, slicelength; [](Sequence &s, const py::slice &slice, const Sequence &value) {
size_t start = 0, stop = 0, step = 0, slicelength = 0;
if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) if (!slice.compute(s.size(), &start, &stop, &step, &slicelength))
throw py::error_already_set(); throw py::error_already_set();
if (slicelength != value.size()) if (slicelength != value.size())
throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); throw std::runtime_error(
"Left and right hand size of slice assignment have different sizes!");
for (size_t i = 0; i < slicelength; ++i) { for (size_t i = 0; i < slicelength; ++i) {
s[start] = value[i]; start += step; s[start] = value[i];
start += step;
} }
}) })
/// Comparisons /// Comparisons
@ -231,8 +242,8 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
StringMap(std::unordered_map<std::string, std::string> init) StringMap(std::unordered_map<std::string, std::string> init)
: map(std::move(init)) {} : map(std::move(init)) {}
void set(std::string key, std::string val) { map[key] = val; } void set(const std::string &key, std::string val) { map[key] = std::move(val); }
std::string get(std::string key) const { return map.at(key); } std::string get(const std::string &key) const { return map.at(key); }
size_t size() const { return map.size(); } size_t size() const { return map.size(); }
private: private:
std::unordered_map<std::string, std::string> map; std::unordered_map<std::string, std::string> map;
@ -243,19 +254,24 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
py::class_<StringMap>(m, "StringMap") py::class_<StringMap>(m, "StringMap")
.def(py::init<>()) .def(py::init<>())
.def(py::init<std::unordered_map<std::string, std::string>>()) .def(py::init<std::unordered_map<std::string, std::string>>())
.def("__getitem__", [](const StringMap &map, std::string key) { .def("__getitem__",
try { return map.get(key); } [](const StringMap &map, const std::string &key) {
catch (const std::out_of_range&) { try {
return map.get(key);
} catch (const std::out_of_range &) {
throw py::key_error("key '" + key + "' does not exist"); throw py::key_error("key '" + key + "' does not exist");
} }
}) })
.def("__setitem__", &StringMap::set) .def("__setitem__", &StringMap::set)
.def("__len__", &StringMap::size) .def("__len__", &StringMap::size)
.def("__iter__", [](const StringMap &map) { return py::make_key_iterator(map.begin(), map.end()); }, .def(
"__iter__",
[](const StringMap &map) { return py::make_key_iterator(map.begin(), map.end()); },
py::keep_alive<0, 1>()) py::keep_alive<0, 1>())
.def("items", [](const StringMap &map) { return py::make_iterator(map.begin(), map.end()); }, .def(
py::keep_alive<0, 1>()) "items",
; [](const StringMap &map) { return py::make_iterator(map.begin(), map.end()); },
py::keep_alive<0, 1>());
// test_generalized_iterators // test_generalized_iterators
class IntPairs { class IntPairs {
@ -304,7 +320,7 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
#endif #endif
// test_python_iterator_in_cpp // test_python_iterator_in_cpp
m.def("object_to_list", [](py::object o) { m.def("object_to_list", [](const py::object &o) {
auto l = py::list(); auto l = py::list();
for (auto item : o) { for (auto item : o) {
l.append(item); l.append(item);
@ -322,19 +338,19 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
}); });
// test_sequence_length: check that Python sequences can be converted to py::sequence. // test_sequence_length: check that Python sequences can be converted to py::sequence.
m.def("sequence_length", [](py::sequence seq) { return seq.size(); }); m.def("sequence_length", [](const py::sequence &seq) { return seq.size(); });
// Make sure that py::iterator works with std algorithms // Make sure that py::iterator works with std algorithms
m.def("count_none", [](py::object o) { m.def("count_none", [](const py::object &o) {
return std::count_if(o.begin(), o.end(), [](py::handle h) { return h.is_none(); }); return std::count_if(o.begin(), o.end(), [](py::handle h) { return h.is_none(); });
}); });
m.def("find_none", [](py::object o) { m.def("find_none", [](const py::object &o) {
auto it = std::find_if(o.begin(), o.end(), [](py::handle h) { return h.is_none(); }); auto it = std::find_if(o.begin(), o.end(), [](py::handle h) { return h.is_none(); });
return it->is_none(); return it->is_none();
}); });
m.def("count_nonzeros", [](py::dict d) { m.def("count_nonzeros", [](const py::dict &d) {
return std::count_if(d.begin(), d.end(), [](std::pair<py::handle, py::handle> p) { return std::count_if(d.begin(), d.end(), [](std::pair<py::handle, py::handle> p) {
return p.second.cast<int>() != 0; return p.second.cast<int>() != 0;
}); });

View File

@ -104,7 +104,7 @@ def test_sequence():
def test_sequence_length(): def test_sequence_length():
"""#2076: Exception raised by len(arg) should be propagated """ """#2076: Exception raised by len(arg) should be propagated"""
class BadLen(RuntimeError): class BadLen(RuntimeError):
pass pass
@ -187,7 +187,7 @@ def test_iterator_passthrough():
def test_iterator_rvp(): def test_iterator_rvp():
"""#388: Can't make iterators via make_iterator() with different r/v policies """ """#388: Can't make iterators via make_iterator() with different r/v policies"""
import pybind11_tests.sequences_and_iterators as m import pybind11_tests.sequences_and_iterators as m
assert list(m.make_iterator_1()) == [1, 2, 3] assert list(m.make_iterator_1()) == [1, 2, 3]

View File

@ -24,7 +24,7 @@ template <typename T> class huge_unique_ptr {
std::unique_ptr<T> ptr; std::unique_ptr<T> ptr;
uint64_t padding[10]; uint64_t padding[10];
public: public:
huge_unique_ptr(T *p) : ptr(p) {}; huge_unique_ptr(T *p) : ptr(p) {}
T *get() { return ptr.get(); } T *get() { return ptr.get(); }
}; };
@ -101,7 +101,7 @@ private:
// test_unique_nodelete // test_unique_nodelete
// Object with a private destructor // Object with a private destructor
class MyObject4; class MyObject4;
static std::unordered_set<MyObject4 *> myobject4_instances; std::unordered_set<MyObject4 *> myobject4_instances;
class MyObject4 { class MyObject4 {
public: public:
MyObject4(int value) : value{value} { MyObject4(int value) : value{value} {
@ -127,7 +127,7 @@ private:
// Object with std::unique_ptr<T, D> where D is not matching the base class // Object with std::unique_ptr<T, D> where D is not matching the base class
// Object with a protected destructor // Object with a protected destructor
class MyObject4a; class MyObject4a;
static std::unordered_set<MyObject4a *> myobject4a_instances; std::unordered_set<MyObject4a *> myobject4a_instances;
class MyObject4a { class MyObject4a {
public: public:
MyObject4a(int i) { MyObject4a(int i) {
@ -170,7 +170,7 @@ struct SharedPtrRef {
struct A { struct A {
A() { print_created(this); } A() { print_created(this); }
A(const A &) { print_copy_created(this); } A(const A &) { print_copy_created(this); }
A(A &&) { print_move_created(this); } A(A &&) noexcept { print_move_created(this); }
~A() { print_destroyed(this); } ~A() { print_destroyed(this); }
}; };
@ -183,7 +183,7 @@ struct SharedFromThisRef {
struct B : std::enable_shared_from_this<B> { struct B : std::enable_shared_from_this<B> {
B() { print_created(this); } B() { print_created(this); }
B(const B &) : std::enable_shared_from_this<B>() { print_copy_created(this); } B(const B &) : std::enable_shared_from_this<B>() { print_copy_created(this); }
B(B &&) : std::enable_shared_from_this<B>() { print_move_created(this); } B(B &&) noexcept : std::enable_shared_from_this<B>() { print_move_created(this); }
~B() { print_destroyed(this); } ~B() { print_destroyed(this); }
}; };
@ -209,7 +209,9 @@ struct C {
struct TypeForHolderWithAddressOf { struct TypeForHolderWithAddressOf {
TypeForHolderWithAddressOf() { print_created(this); } TypeForHolderWithAddressOf() { print_created(this); }
TypeForHolderWithAddressOf(const TypeForHolderWithAddressOf &) { print_copy_created(this); } TypeForHolderWithAddressOf(const TypeForHolderWithAddressOf &) { print_copy_created(this); }
TypeForHolderWithAddressOf(TypeForHolderWithAddressOf &&) { print_move_created(this); } TypeForHolderWithAddressOf(TypeForHolderWithAddressOf &&) noexcept {
print_move_created(this);
}
~TypeForHolderWithAddressOf() { print_destroyed(this); } ~TypeForHolderWithAddressOf() { print_destroyed(this); }
std::string toString() const { std::string toString() const {
return "TypeForHolderWithAddressOf[" + std::to_string(value) + "]"; return "TypeForHolderWithAddressOf[" + std::to_string(value) + "]";
@ -240,12 +242,12 @@ struct ElementBase {
struct ElementA : ElementBase { struct ElementA : ElementBase {
ElementA(int v) : v(v) { } ElementA(int v) : v(v) { }
int value() { return v; } int value() const { return v; }
int v; int v;
}; };
struct ElementList { struct ElementList {
void add(std::shared_ptr<ElementBase> e) { l.push_back(e); } void add(const std::shared_ptr<ElementBase> &e) { l.push_back(e); }
std::vector<std::shared_ptr<ElementBase>> l; std::vector<std::shared_ptr<ElementBase>> l;
}; };
@ -308,6 +310,7 @@ TEST_SUBMODULE(smart_ptr, m) {
m.def("make_myobject2_1", []() { return new MyObject2(6); }); m.def("make_myobject2_1", []() { return new MyObject2(6); });
m.def("make_myobject2_2", []() { return std::make_shared<MyObject2>(7); }); m.def("make_myobject2_2", []() { return std::make_shared<MyObject2>(7); });
m.def("print_myobject2_1", [](const MyObject2 *obj) { py::print(obj->toString()); }); m.def("print_myobject2_1", [](const MyObject2 *obj) { py::print(obj->toString()); });
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("print_myobject2_2", [](std::shared_ptr<MyObject2> obj) { py::print(obj->toString()); }); m.def("print_myobject2_2", [](std::shared_ptr<MyObject2> obj) { py::print(obj->toString()); });
m.def("print_myobject2_3", [](const std::shared_ptr<MyObject2> &obj) { py::print(obj->toString()); }); m.def("print_myobject2_3", [](const std::shared_ptr<MyObject2> &obj) { py::print(obj->toString()); });
m.def("print_myobject2_4", [](const std::shared_ptr<MyObject2> *obj) { py::print((*obj)->toString()); }); m.def("print_myobject2_4", [](const std::shared_ptr<MyObject2> *obj) { py::print((*obj)->toString()); });
@ -317,6 +320,7 @@ TEST_SUBMODULE(smart_ptr, m) {
m.def("make_myobject3_1", []() { return new MyObject3(8); }); m.def("make_myobject3_1", []() { return new MyObject3(8); });
m.def("make_myobject3_2", []() { return std::make_shared<MyObject3>(9); }); m.def("make_myobject3_2", []() { return std::make_shared<MyObject3>(9); });
m.def("print_myobject3_1", [](const MyObject3 *obj) { py::print(obj->toString()); }); m.def("print_myobject3_1", [](const MyObject3 *obj) { py::print(obj->toString()); });
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("print_myobject3_2", [](std::shared_ptr<MyObject3> obj) { py::print(obj->toString()); }); m.def("print_myobject3_2", [](std::shared_ptr<MyObject3> obj) { py::print(obj->toString()); });
m.def("print_myobject3_3", [](const std::shared_ptr<MyObject3> &obj) { py::print(obj->toString()); }); m.def("print_myobject3_3", [](const std::shared_ptr<MyObject3> &obj) { py::print(obj->toString()); });
m.def("print_myobject3_4", [](const std::shared_ptr<MyObject3> *obj) { py::print((*obj)->toString()); }); m.def("print_myobject3_4", [](const std::shared_ptr<MyObject3> *obj) { py::print((*obj)->toString()); });
@ -358,12 +362,15 @@ TEST_SUBMODULE(smart_ptr, m) {
py::class_<SharedPtrRef, std::unique_ptr<SharedPtrRef>>(m, "SharedPtrRef") py::class_<SharedPtrRef, std::unique_ptr<SharedPtrRef>>(m, "SharedPtrRef")
.def(py::init<>()) .def(py::init<>())
.def_readonly("ref", &SharedPtrRef::value) .def_readonly("ref", &SharedPtrRef::value)
.def_property_readonly("copy", [](const SharedPtrRef &s) { return s.value; }, .def_property_readonly(
py::return_value_policy::copy) "copy", [](const SharedPtrRef &s) { return s.value; }, py::return_value_policy::copy)
.def_readonly("holder_ref", &SharedPtrRef::shared) .def_readonly("holder_ref", &SharedPtrRef::shared)
.def_property_readonly("holder_copy", [](const SharedPtrRef &s) { return s.shared; }, .def_property_readonly(
"holder_copy",
[](const SharedPtrRef &s) { return s.shared; },
py::return_value_policy::copy) py::return_value_policy::copy)
.def("set_ref", [](SharedPtrRef &, const A &) { return true; }) .def("set_ref", [](SharedPtrRef &, const A &) { return true; })
// NOLINTNEXTLINE(performance-unnecessary-value-param)
.def("set_holder", [](SharedPtrRef &, std::shared_ptr<A>) { return true; }); .def("set_holder", [](SharedPtrRef &, std::shared_ptr<A>) { return true; });
// test_shared_ptr_from_this_and_references // test_shared_ptr_from_this_and_references
@ -372,13 +379,19 @@ TEST_SUBMODULE(smart_ptr, m) {
py::class_<SharedFromThisRef, std::unique_ptr<SharedFromThisRef>>(m, "SharedFromThisRef") py::class_<SharedFromThisRef, std::unique_ptr<SharedFromThisRef>>(m, "SharedFromThisRef")
.def(py::init<>()) .def(py::init<>())
.def_readonly("bad_wp", &SharedFromThisRef::value) .def_readonly("bad_wp", &SharedFromThisRef::value)
.def_property_readonly("ref", [](const SharedFromThisRef &s) -> const B & { return *s.shared; }) .def_property_readonly("ref",
.def_property_readonly("copy", [](const SharedFromThisRef &s) { return s.value; }, [](const SharedFromThisRef &s) -> const B & { return *s.shared; })
.def_property_readonly(
"copy",
[](const SharedFromThisRef &s) { return s.value; },
py::return_value_policy::copy) py::return_value_policy::copy)
.def_readonly("holder_ref", &SharedFromThisRef::shared) .def_readonly("holder_ref", &SharedFromThisRef::shared)
.def_property_readonly("holder_copy", [](const SharedFromThisRef &s) { return s.shared; }, .def_property_readonly(
"holder_copy",
[](const SharedFromThisRef &s) { return s.shared; },
py::return_value_policy::copy) py::return_value_policy::copy)
.def("set_ref", [](SharedFromThisRef &, const B &) { return true; }) .def("set_ref", [](SharedFromThisRef &, const B &) { return true; })
// NOLINTNEXTLINE(performance-unnecessary-value-param)
.def("set_holder", [](SharedFromThisRef &, std::shared_ptr<B>) { return true; }); .def("set_holder", [](SharedFromThisRef &, std::shared_ptr<B>) { return true; });
// Issue #865: shared_from_this doesn't work with virtual inheritance // Issue #865: shared_from_this doesn't work with virtual inheritance
@ -396,10 +409,14 @@ TEST_SUBMODULE(smart_ptr, m) {
py::class_<TypeForHolderWithAddressOf, HolderWithAddressOf>(m, "TypeForHolderWithAddressOf") py::class_<TypeForHolderWithAddressOf, HolderWithAddressOf>(m, "TypeForHolderWithAddressOf")
.def_static("make", []() { return HolderWithAddressOf(new TypeForHolderWithAddressOf); }) .def_static("make", []() { return HolderWithAddressOf(new TypeForHolderWithAddressOf); })
.def("get", [](const HolderWithAddressOf &self) { return self.get(); }) .def("get", [](const HolderWithAddressOf &self) { return self.get(); })
.def("print_object_1", [](const TypeForHolderWithAddressOf *obj) { py::print(obj->toString()); }) .def("print_object_1",
[](const TypeForHolderWithAddressOf *obj) { py::print(obj->toString()); })
// NOLINTNEXTLINE(performance-unnecessary-value-param)
.def("print_object_2", [](HolderWithAddressOf obj) { py::print(obj.get()->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_3",
.def("print_object_4", [](const HolderWithAddressOf *obj) { py::print((*obj).get()->toString()); }); [](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 // test_move_only_holder_with_addressof_operator
using MoveOnlyHolderWithAddressOf = unique_ptr_with_addressof_operator<TypeForMoveOnlyHolderWithAddressOf>; using MoveOnlyHolderWithAddressOf = unique_ptr_with_addressof_operator<TypeForMoveOnlyHolderWithAddressOf>;
@ -411,6 +428,7 @@ TEST_SUBMODULE(smart_ptr, m) {
// test_smart_ptr_from_default // test_smart_ptr_from_default
py::class_<HeldByDefaultHolder, std::unique_ptr<HeldByDefaultHolder>>(m, "HeldByDefaultHolder") py::class_<HeldByDefaultHolder, std::unique_ptr<HeldByDefaultHolder>>(m, "HeldByDefaultHolder")
.def(py::init<>()) .def(py::init<>())
// NOLINTNEXTLINE(performance-unnecessary-value-param)
.def_static("load_shared_ptr", [](std::shared_ptr<HeldByDefaultHolder>) {}); .def_static("load_shared_ptr", [](std::shared_ptr<HeldByDefaultHolder>) {});
// test_shared_ptr_gc // test_shared_ptr_gc

View File

@ -11,6 +11,11 @@
#include "constructor_stats.h" #include "constructor_stats.h"
#include <pybind11/stl.h> #include <pybind11/stl.h>
#ifndef PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL
#define PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL
#endif
#include <pybind11/stl/filesystem.h>
#include <vector> #include <vector>
#include <string> #include <string>
@ -97,7 +102,7 @@ TEST_SUBMODULE(stl, m) {
// test_set // test_set
m.def("cast_set", []() { return std::set<std::string>{"key1", "key2"}; }); m.def("cast_set", []() { return std::set<std::string>{"key1", "key2"}; });
m.def("load_set", [](const std::set<std::string> &set) { m.def("load_set", [](const std::set<std::string> &set) {
return set.count("key1") && set.count("key2") && set.count("key3"); return (set.count("key1") != 0u) && (set.count("key2") != 0u) && (set.count("key3") != 0u);
}); });
// test_recursive_casting // test_recursive_casting
@ -191,9 +196,7 @@ TEST_SUBMODULE(stl, m) {
m.def("double_or_zero", [](const opt_int& x) -> int { m.def("double_or_zero", [](const opt_int& x) -> int {
return x.value_or(0) * 2; return x.value_or(0) * 2;
}); });
m.def("half_or_none", [](int x) -> opt_int { m.def("half_or_none", [](int x) -> opt_int { return x != 0 ? opt_int(x / 2) : opt_int(); });
return x ? opt_int(x / 2) : opt_int();
});
m.def("test_nullopt", [](opt_int x) { m.def("test_nullopt", [](opt_int x) {
return x.value_or(42); return x.value_or(42);
}, py::arg_v("x", std::nullopt, "None")); }, py::arg_v("x", std::nullopt, "None"));
@ -202,7 +205,7 @@ TEST_SUBMODULE(stl, m) {
}, py::arg_v("x", std::nullopt, "None")); }, py::arg_v("x", std::nullopt, "None"));
m.def("nodefer_none_optional", [](std::optional<int>) { return true; }); m.def("nodefer_none_optional", [](std::optional<int>) { return true; });
m.def("nodefer_none_optional", [](py::none) { return false; }); m.def("nodefer_none_optional", [](const py::none &) { return false; });
using opt_holder = OptionalHolder<std::optional, MoveOutDetector>; using opt_holder = OptionalHolder<std::optional, MoveOutDetector>;
py::class_<opt_holder>(m, "OptionalHolder", "Class with optional member") py::class_<opt_holder>(m, "OptionalHolder", "Class with optional member")
@ -237,6 +240,12 @@ TEST_SUBMODULE(stl, m) {
.def("member_initialized", &opt_exp_holder::member_initialized); .def("member_initialized", &opt_exp_holder::member_initialized);
#endif #endif
#ifdef PYBIND11_HAS_FILESYSTEM
// test_fs_path
m.attr("has_filesystem") = true;
m.def("parent_path", [](const std::filesystem::path& p) { return p.parent_path(); });
#endif
#ifdef PYBIND11_HAS_VARIANT #ifdef PYBIND11_HAS_VARIANT
static_assert(std::is_same<py::detail::variant_caster_visitor::result_type, py::handle>::value, static_assert(std::is_same<py::detail::variant_caster_visitor::result_type, py::handle>::value,
"visitor::result_type is required by boost::variant in C++11 mode"); "visitor::result_type is required by boost::variant in C++11 mode");
@ -245,13 +254,13 @@ TEST_SUBMODULE(stl, m) {
using result_type = const char *; using result_type = const char *;
result_type operator()(int) { return "int"; } result_type operator()(int) { return "int"; }
result_type operator()(std::string) { return "std::string"; } result_type operator()(const std::string &) { return "std::string"; }
result_type operator()(double) { return "double"; } result_type operator()(double) { return "double"; }
result_type operator()(std::nullptr_t) { return "std::nullptr_t"; } result_type operator()(std::nullptr_t) { return "std::nullptr_t"; }
}; };
// test_variant // test_variant
m.def("load_variant", [](variant<int, std::string, double, std::nullptr_t> v) { m.def("load_variant", [](const variant<int, std::string, double, std::nullptr_t> &v) {
return py::detail::visit_helper<variant>::call(visitor(), v); return py::detail::visit_helper<variant>::call(visitor(), v);
}); });
m.def("load_variant_2pass", [](variant<double, int> v) { m.def("load_variant_2pass", [](variant<double, int> v) {
@ -287,9 +296,11 @@ TEST_SUBMODULE(stl, m) {
m.def("stl_pass_by_pointer", [](std::vector<int>* v) { return *v; }, "v"_a=nullptr); m.def("stl_pass_by_pointer", [](std::vector<int>* v) { return *v; }, "v"_a=nullptr);
// #1258: pybind11/stl.h converts string to vector<string> // #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",
m.def("func_with_string_or_vector_string_arg_overload", [](std::list<std::string>) { return 2; }); [](const std::vector<std::string> &) { return 1; });
m.def("func_with_string_or_vector_string_arg_overload", [](std::string) { return 3; }); m.def("func_with_string_or_vector_string_arg_overload",
[](const std::list<std::string> &) { return 2; });
m.def("func_with_string_or_vector_string_arg_overload", [](const std::string &) { return 3; });
class Placeholder { class Placeholder {
public: public:
@ -321,4 +332,10 @@ TEST_SUBMODULE(stl, m) {
py::class_<Issue1561Outer>(m, "Issue1561Outer") py::class_<Issue1561Outer>(m, "Issue1561Outer")
.def(py::init<>()) .def(py::init<>())
.def_readwrite("list", &Issue1561Outer::list); .def_readwrite("list", &Issue1561Outer::list);
m.def(
"return_vector_bool_raw_ptr",
[]() { return new std::vector<bool>(4513); },
// Without explicitly specifying `take_ownership`, this function leaks.
py::return_value_policy::take_ownership);
} }

View File

@ -162,6 +162,25 @@ def test_exp_optional():
assert holder.member_initialized() assert holder.member_initialized()
@pytest.mark.skipif(not hasattr(m, "has_filesystem"), reason="no <filesystem>")
def test_fs_path():
from pathlib import Path
class PseudoStrPath:
def __fspath__(self):
return "foo/bar"
class PseudoBytesPath:
def __fspath__(self):
return b"foo/bar"
assert m.parent_path(Path("foo/bar")) == Path("foo")
assert m.parent_path("foo/bar") == Path("foo")
assert m.parent_path(b"foo/bar") == Path("foo")
assert m.parent_path(PseudoStrPath()) == Path("foo")
assert m.parent_path(PseudoBytesPath()) == Path("foo")
@pytest.mark.skipif(not hasattr(m, "load_variant"), reason="no <variant>") @pytest.mark.skipif(not hasattr(m, "load_variant"), reason="no <variant>")
def test_variant(doc): def test_variant(doc):
assert m.load_variant(1) == "int" assert m.load_variant(1) == "int"
@ -259,8 +278,15 @@ def test_array_cast_sequence():
def test_issue_1561(): def test_issue_1561():
""" check fix for issue #1561 """ """check fix for issue #1561"""
bar = m.Issue1561Outer() bar = m.Issue1561Outer()
bar.list = [m.Issue1561Inner("bar")] bar.list = [m.Issue1561Inner("bar")]
bar.list bar.list
assert bar.list[0].data == "bar" assert bar.list[0].data == "bar"
def test_return_vector_bool_raw_ptr():
# Add `while True:` for manual leak checking.
v = m.return_vector_bool_raw_ptr()
assert isinstance(v, list)
assert len(v) == 4513

View File

@ -125,5 +125,7 @@ TEST_SUBMODULE(stl_binders, m) {
PYBIND11_NUMPY_DTYPE(VStruct, w, x, y, z); PYBIND11_NUMPY_DTYPE(VStruct, w, x, y, z);
py::class_<VStruct>(m, "VStruct").def_readwrite("x", &VStruct::x); py::class_<VStruct>(m, "VStruct").def_readwrite("x", &VStruct::x);
py::bind_vector<std::vector<VStruct>>(m, "VectorStruct", py::buffer_protocol()); py::bind_vector<std::vector<VStruct>>(m, "VectorStruct", py::buffer_protocol());
m.def("get_vectorstruct", [] {return std::vector<VStruct> {{0, 5, 3.0, 1}, {1, 30, -1e4, 0}};}); m.def("get_vectorstruct", [] {
return std::vector<VStruct>{{false, 5, 3.0, true}, {true, 30, -1e4, false}};
});
} }

View File

@ -17,7 +17,10 @@ class ExampleVirt {
public: public:
ExampleVirt(int state) : state(state) { print_created(this, state); } ExampleVirt(int state) : state(state) { print_created(this, state); }
ExampleVirt(const ExampleVirt &e) : state(e.state) { print_copy_created(this); } 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(ExampleVirt &&e) noexcept : state(e.state) {
print_move_created(this);
e.state = 0;
}
virtual ~ExampleVirt() { print_destroyed(this); } virtual ~ExampleVirt() { print_destroyed(this); }
virtual int run(int value) { virtual int run(int value) {
@ -100,13 +103,18 @@ public:
class NonCopyable { class NonCopyable {
public: public:
NonCopyable(int a, int b) : value{new int(a*b)} { print_created(this, a, b); } NonCopyable(int a, int b) : value{new int(a*b)} { print_created(this, a, b); }
NonCopyable(NonCopyable &&o) { value = std::move(o.value); print_move_created(this); } NonCopyable(NonCopyable &&o) noexcept {
value = std::move(o.value);
print_move_created(this);
}
NonCopyable(const NonCopyable &) = delete; NonCopyable(const NonCopyable &) = delete;
NonCopyable() = delete; NonCopyable() = delete;
void operator=(const NonCopyable &) = delete; void operator=(const NonCopyable &) = delete;
void operator=(NonCopyable &&) = delete; void operator=(NonCopyable &&) = delete;
std::string get_value() const { std::string get_value() const {
if (value) return std::to_string(*value); else return "(null)"; if (value)
return std::to_string(*value);
return "(null)";
} }
~NonCopyable() { print_destroyed(this); } ~NonCopyable() { print_destroyed(this); }
@ -120,7 +128,10 @@ class Movable {
public: public:
Movable(int a, int b) : value{a+b} { print_created(this, a, b); } Movable(int a, int b) : value{a+b} { print_created(this, a, b); }
Movable(const Movable &m) { value = m.value; print_copy_created(this); } Movable(const Movable &m) { value = m.value; print_copy_created(this); }
Movable(Movable &&m) { value = std::move(m.value); print_move_created(this); } Movable(Movable &&m) noexcept {
value = m.value;
print_move_created(this);
}
std::string get_value() const { return std::to_string(value); } std::string get_value() const { return std::to_string(value); }
~Movable() { print_destroyed(this); } ~Movable() { print_destroyed(this); }
private: private:

View File

@ -2,7 +2,7 @@
# #
# On updating a dependency, to get a list of "default" leaks in e.g. NumPy, run # On updating a dependency, to get a list of "default" leaks in e.g. NumPy, run
# `PYTHONMALLOC=malloc valgrind --leak-check=full --show-leak-kinds=definite,indirect python3.9-dbg -c "import numpy"` # `PYTHONMALLOC=malloc valgrind --leak-check=full --show-leak-kinds=definite,indirect python3.9-dbg -c "import numpy"`
# To use theses suppression files, add e.g. `--suppressions=valgrind-numpy-scipy.supp` # To use these suppression files, add e.g. `--suppressions=valgrind-numpy-scipy.supp`
{ {
Leaks when importing NumPy Leaks when importing NumPy
@ -111,7 +111,7 @@
fun:_Znwm fun:_Znwm
fun:PyInit_pypocketfft fun:PyInit_pypocketfft
fun:_PyImport_LoadDynamicModuleWithSpec fun:_PyImport_LoadDynamicModuleWithSpec
fun:_imp_create_dynamic_impl.constprop.3 fun:_imp_create_dynamic_impl*
fun:_imp_create_dynamic fun:_imp_create_dynamic
fun:cfunction_vectorcall_FASTCALL fun:cfunction_vectorcall_FASTCALL
fun:PyVectorcall_Call fun:PyVectorcall_Call

View File

@ -190,19 +190,6 @@ if(CMAKE_HOST_WIN32)
set(PYTHON_LIBRARY "${_PYTHON_ROOT}/libs/python${PYTHON_LIBRARY_SUFFIX}.lib") set(PYTHON_LIBRARY "${_PYTHON_ROOT}/libs/python${PYTHON_LIBRARY_SUFFIX}.lib")
endif() endif()
if(DEFINED VCPKG_TOOLCHAIN)
unset(PYTHON_LIBRARY)
find_library(
PYTHON_LIBRARY
NAMES "python${PYTHON_LIBRARY_SUFFIX}"
NO_SYSTEM_ENVIRONMENT_PATH)
find_library(PYTHON_DEBUG_LIBRARY
NAMES python${PYTHON_LIBRARY_SUFFIX}_d
NO_SYSTEM_ENVIRONMENT_PATH
)
endif()
# if we are in MSYS & MINGW, and we didn't find windows python lib, look for system python lib # if we are in MSYS & MINGW, and we didn't find windows python lib, look for system python lib
if(DEFINED ENV{MSYSTEM} if(DEFINED ENV{MSYSTEM}
AND MINGW AND MINGW
@ -246,7 +233,7 @@ else()
endif() endif()
endif() endif()
mark_as_advanced(PYTHON_DEBUG_LIBRARY PYTHON_LIBRARY PYTHON_INCLUDE_DIR) mark_as_advanced(PYTHON_LIBRARY PYTHON_INCLUDE_DIR)
# We use PYTHON_INCLUDE_DIR, PYTHON_LIBRARY and PYTHON_DEBUG_LIBRARY for the # We use PYTHON_INCLUDE_DIR, PYTHON_LIBRARY and PYTHON_DEBUG_LIBRARY for the
# cache entries because they are meant to specify the location of a single # cache entries because they are meant to specify the location of a single
@ -259,10 +246,6 @@ if(NOT PYTHON_DEBUG_LIBRARY)
endif() endif()
set(PYTHON_DEBUG_LIBRARIES "${PYTHON_DEBUG_LIBRARY}") set(PYTHON_DEBUG_LIBRARIES "${PYTHON_DEBUG_LIBRARY}")
set(PYTHON_LIBRARY_DEBUG "${PYTHON_DEBUG_LIBRARY}")
set(PYTHON_LIBRARY_RELEASE "${PYTHON_LIBRARY}")
select_library_configurations(PYTHON)
find_package_message(PYTHON "Found PythonLibs: ${PYTHON_LIBRARY}" find_package_message(PYTHON "Found PythonLibs: ${PYTHON_LIBRARY}"
"${PYTHON_EXECUTABLE}${PYTHON_VERSION_STRING}") "${PYTHON_EXECUTABLE}${PYTHON_VERSION_STRING}")

View File

@ -16,11 +16,11 @@ check_style_errors=0
IFS=$'\n' IFS=$'\n'
found="$(grep '\<\(if\|for\|while\|catch\)(\|){' $@ -rn --color=always)" found="$(grep '\<\(if\|for\|while\|catch\)(\|){' "$@" -rn --color=always)"
if [ -n "$found" ]; then if [ -n "$found" ]; then
echo -e '\033[31;01mError: found the following coding style problems:\033[0m' echo -e '\033[31;01mError: found the following coding style problems:\033[0m'
check_style_errors=1 check_style_errors=1
echo "$found" | sed -e 's/^/ /' echo "${found//^/ /}"
fi fi
found="$(awk ' found="$(awk '
@ -34,7 +34,7 @@ last && /^\s*{/ {
last="" last=""
} }
{ last = /(if|for|while|catch|switch)\s*\(.*\)\s*$/ ? $0 : "" } { last = /(if|for|while|catch|switch)\s*\(.*\)\s*$/ ? $0 : "" }
' $(find include -type f) $@)" ' "$(find include -type f)" "$@")"
if [ -n "$found" ]; then if [ -n "$found" ]; then
check_style_errors=1 check_style_errors=1
echo -e '\033[31;01mError: braces should occur on the same line as the if/while/.. statement. Found issues in the following files:\033[0m' echo -e '\033[31;01mError: braces should occur on the same line as the if/while/.. statement. Found issues in the following files:\033[0m'

View File

@ -27,7 +27,10 @@ print()
api = ghapi.all.GhApi(owner="pybind", repo="pybind11") api = ghapi.all.GhApi(owner="pybind", repo="pybind11")
issues = api.issues.list_for_repo(labels="needs changelog", state="closed") issues_pages = ghapi.page.paged(
api.issues.list_for_repo, labels="needs changelog", state="closed"
)
issues = (issue for page in issues_pages for issue in page)
missing = [] missing = []
for issue in issues: for issue in issues:
@ -41,7 +44,7 @@ for issue in issues:
msg += f"\n `#{issue.number} <{issue.html_url}>`_" msg += f"\n `#{issue.number} <{issue.html_url}>`_"
print(Syntax(msg, "rst", theme="ansi_light")) print(Syntax(msg, "rst", theme="ansi_light", word_wrap=True))
print() print()
else: else:

View File

@ -302,13 +302,18 @@ function(_pybind11_return_if_cxx_and_linker_flags_work result cxxflags linkerfla
endfunction() endfunction()
function(_pybind11_generate_lto target prefer_thin_lto) function(_pybind11_generate_lto target prefer_thin_lto)
if(MINGW)
message(STATUS "${target} disabled (problems with undefined symbols for MinGW for now)")
return()
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
set(cxx_append "") set(cxx_append "")
set(linker_append "") set(linker_append "")
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT APPLE) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT APPLE)
# Clang Gold plugin does not support -Os; append -O3 to MinSizeRel builds to override it # Clang Gold plugin does not support -Os; append -O3 to MinSizeRel builds to override it
set(linker_append ";$<$<CONFIG:MinSizeRel>:-O3>") set(linker_append ";$<$<CONFIG:MinSizeRel>:-O3>")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND NOT MINGW)
set(cxx_append ";-fno-fat-lto-objects") set(cxx_append ";-fno-fat-lto-objects")
endif() endif()

View File

@ -139,7 +139,7 @@ This module defines the following commands to assist with creating Python module
pybind11_add_module(<target> pybind11_add_module(<target>
[STATIC|SHARED|MODULE] [STATIC|SHARED|MODULE]
[THIN_LTO] [OPT_SIZE] [NO_EXTRAS] [WITHOUT_SOBAI] [THIN_LTO] [OPT_SIZE] [NO_EXTRAS] [WITHOUT_SOABI]
<files>... <files>...
) )
@ -201,7 +201,8 @@ Using ``find_package`` with version info is not recommended except for release v
@PACKAGE_INIT@ @PACKAGE_INIT@
# Location of pybind11/pybind11.h # Location of pybind11/pybind11.h
set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_INCLUDEDIR@") # This will be relative unless explicitly set as absolute
set(pybind11_INCLUDE_DIR "@pybind11_INCLUDEDIR@")
set(pybind11_LIBRARY "") set(pybind11_LIBRARY "")
set(pybind11_DEFINITIONS USING_pybind11) set(pybind11_DEFINITIONS USING_pybind11)

Some files were not shown because too many files have changed in this diff Show More