ext: Update pybind11 to v2.8.1
Change-Id: Ia1c7081377f53fd470addf35526f8b28a949a7b0 Signed-off-by: Jason Lowe-Power <jason@lowepower.com> Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/52523 Maintainer: Bobby R. Bruce <bbruce@ucdavis.edu> Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Gabe Black <gabe.black@gmail.com>
This commit is contained in:
committed by
Jason Lowe-Power
parent
ba5f68db3d
commit
1e8aeee698
@@ -7,13 +7,13 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.4)
|
||||
|
||||
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
|
||||
# The `cmake_minimum_required(VERSION 3.4...3.22)` syntax does not work with
|
||||
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
||||
# the behavior using the following workaround:
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.18)
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.22)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.18)
|
||||
cmake_policy(VERSION 3.22)
|
||||
endif()
|
||||
|
||||
# Extract project version from source
|
||||
@@ -89,6 +89,9 @@ endif()
|
||||
option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT})
|
||||
option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT})
|
||||
option(PYBIND11_NOPYTHON "Disable search for Python" OFF)
|
||||
set(PYBIND11_INTERNALS_VERSION
|
||||
""
|
||||
CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.")
|
||||
|
||||
cmake_dependent_option(
|
||||
USE_PYTHON_INCLUDE_DIR
|
||||
@@ -105,6 +108,7 @@ set(PYBIND11_HEADERS
|
||||
include/pybind11/detail/descr.h
|
||||
include/pybind11/detail/init.h
|
||||
include/pybind11/detail/internals.h
|
||||
include/pybind11/detail/type_caster_base.h
|
||||
include/pybind11/detail/typeid.h
|
||||
include/pybind11/attr.h
|
||||
include/pybind11/buffer_info.h
|
||||
@@ -116,6 +120,7 @@ set(PYBIND11_HEADERS
|
||||
include/pybind11/eigen.h
|
||||
include/pybind11/embed.h
|
||||
include/pybind11/eval.h
|
||||
include/pybind11/gil.h
|
||||
include/pybind11/iostream.h
|
||||
include/pybind11/functional.h
|
||||
include/pybind11/numpy.h
|
||||
@@ -123,7 +128,8 @@ set(PYBIND11_HEADERS
|
||||
include/pybind11/pybind11.h
|
||||
include/pybind11/pytypes.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
|
||||
if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12)
|
||||
@@ -180,6 +186,10 @@ if(NOT TARGET pybind11_headers)
|
||||
|
||||
target_compile_features(pybind11_headers INTERFACE cxx_inheriting_constructors cxx_user_literals
|
||||
cxx_right_angle_brackets)
|
||||
if(NOT "${PYBIND11_INTERNALS_VERSION}" STREQUAL "")
|
||||
target_compile_definitions(
|
||||
pybind11_headers INTERFACE "PYBIND11_INTERNALS_VERSION=${PYBIND11_INTERNALS_VERSION}")
|
||||
endif()
|
||||
else()
|
||||
# It is invalid to install a target twice, too.
|
||||
set(PYBIND11_INSTALL OFF)
|
||||
@@ -200,6 +210,12 @@ if(PYBIND11_INSTALL)
|
||||
"${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME}"
|
||||
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(
|
||||
tools/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR})
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
**pybind11 — Seamless operability between C++11 and Python**
|
||||
|
||||
|Latest Documentation Status| |Stable Documentation Status| |Gitter chat| |CI| |Build status|
|
||||
|Latest Documentation Status| |Stable Documentation Status| |Gitter chat| |GitHub Discussions| |CI| |Build status|
|
||||
|
||||
|Repology| |PyPI package| |Conda-forge| |Python Versions|
|
||||
|
||||
@@ -13,19 +13,6 @@
|
||||
|
||||
.. start
|
||||
|
||||
.. warning::
|
||||
|
||||
Combining older versions of pybind11 (< 2.6.0) with Python 3.9.0 will
|
||||
trigger undefined behavior that typically manifests as crashes during
|
||||
interpreter shutdown (but could also destroy your data. **You have been
|
||||
warned.**)
|
||||
|
||||
We recommend that you update to the latest patch release of Python (3.9.1),
|
||||
which includes a `fix <https://github.com/python/cpython/pull/22670>`_
|
||||
that resolves this problem. If you do use Python 3.9.0, please update to
|
||||
the latest version of pybind11 (2.6.0 or newer), which includes a temporary
|
||||
workaround specifically when Python 3.9.0 is detected at runtime.
|
||||
|
||||
|
||||
**pybind11** is a lightweight header-only library that exposes C++ types
|
||||
in Python and vice versa, mainly to create Python bindings of existing
|
||||
@@ -110,7 +97,7 @@ goodies:
|
||||
transparently applied to all entries of one or more NumPy array
|
||||
arguments.
|
||||
|
||||
- Python’s slice-based access and assignment operations can be
|
||||
- Python's slice-based access and assignment operations can be
|
||||
supported with just a few lines of code.
|
||||
|
||||
- Everything is contained in just a few header files; there is no need
|
||||
@@ -147,9 +134,9 @@ About
|
||||
This project was created by `Wenzel
|
||||
Jakob <http://rgl.epfl.ch/people/wjakob>`_. Significant features and/or
|
||||
improvements to the code were contributed by Jonas Adler, Lori A. Burns,
|
||||
Sylvain Corlay, Eric Cousineau, Ralf Grosse-Kunstleve, Trent Houliston, Axel
|
||||
Sylvain Corlay, Eric Cousineau, Aaron Gokaslan, Ralf Grosse-Kunstleve, Trent Houliston, Axel
|
||||
Huebl, @hulucc, Yannick Jadoul, Sergey Lyskov Johan Mabille, Tomasz Miąsko,
|
||||
Dean Moldovan, Ben Pritchard, Jason Rhinelander, Boris Schäling, Pim
|
||||
Dean Moldovan, Ben Pritchard, Jason Rhinelander, Boris Schäling, Pim
|
||||
Schellart, Henry Schreiner, Ivan Smirnov, Boris Staletic, and Patrick Stewart.
|
||||
|
||||
We thank Google for a generous financial contribution to the continuous
|
||||
@@ -189,3 +176,5 @@ to the terms and conditions of this license.
|
||||
:target: https://repology.org/project/python:pybind11/versions
|
||||
.. |Python Versions| image:: https://img.shields.io/pypi/pyversions/pybind11.svg
|
||||
:target: https://pypi.org/project/pybind11/
|
||||
.. |GitHub Discussions| image:: https://img.shields.io/static/v1?label=Discussions&message=Ask&color=blue&logo=github
|
||||
:target: https://github.com/pybind/pybind11/discussions
|
||||
|
||||
@@ -26,7 +26,9 @@ The following Python snippet demonstrates the intended usage from the Python sid
|
||||
def __int__(self):
|
||||
return 123
|
||||
|
||||
|
||||
from example import print
|
||||
|
||||
print(A())
|
||||
|
||||
To register the necessary conversion routines, it is necessary to add an
|
||||
|
||||
@@ -112,7 +112,7 @@ example:
|
||||
.. code-block:: python
|
||||
|
||||
a = MyClass()
|
||||
m = a.get_matrix() # flags.writeable = True, flags.owndata = False
|
||||
m = a.get_matrix() # flags.writeable = True, flags.owndata = False
|
||||
v = a.view_matrix() # flags.writeable = False, flags.owndata = False
|
||||
c = a.copy_matrix() # flags.writeable = True, flags.owndata = True
|
||||
# m[5,6] and v[5,6] refer to the same element, c[5,6] does not.
|
||||
@@ -203,7 +203,7 @@ adding the ``order='F'`` option when creating an array:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
myarray = np.array(source, order='F')
|
||||
myarray = np.array(source, order="F")
|
||||
|
||||
Such an object will be passable to a bound function accepting an
|
||||
``Eigen::Ref<MatrixXd>`` (or similar column-major Eigen type).
|
||||
|
||||
@@ -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::filesystem::path<T>`` | STL path (C++17) [#]_ | :file:`pybind11/stl.h` |
|
||||
+------------------------------------+---------------------------+-------------------------------+
|
||||
| ``std::function<...>`` | STL polymorphic function | :file:`pybind11/functional.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` |
|
||||
+------------------------------------+---------------------------+-------------------------------+
|
||||
|
||||
.. [#] ``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).
|
||||
|
||||
@@ -36,13 +36,13 @@ everywhere <http://utf8everywhere.org/>`_.
|
||||
}
|
||||
);
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> utf8_test('🎂')
|
||||
>>> utf8_test("🎂")
|
||||
utf-8 is icing on the cake.
|
||||
🎂
|
||||
|
||||
>>> utf8_charptr('🍕')
|
||||
>>> utf8_charptr("🍕")
|
||||
My favorite food is
|
||||
🍕
|
||||
|
||||
@@ -80,7 +80,7 @@ raise a ``UnicodeDecodeError``.
|
||||
}
|
||||
);
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> isinstance(example.std_string_return(), str)
|
||||
True
|
||||
@@ -114,7 +114,7 @@ conversion has the same overhead as implicit conversion.
|
||||
}
|
||||
);
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> str_output()
|
||||
'Send your résumé to Alice in HR'
|
||||
@@ -143,7 +143,7 @@ returned to Python as ``bytes``, then one can return the data as a
|
||||
}
|
||||
);
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> example.return_bytes()
|
||||
b'\xba\xd0\xba\xd0'
|
||||
@@ -160,7 +160,7 @@ encoding, but cannot convert ``std::string`` back to ``bytes`` implicitly.
|
||||
}
|
||||
);
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> isinstance(example.asymmetry(b"have some bytes"), str)
|
||||
True
|
||||
@@ -229,16 +229,16 @@ character.
|
||||
m.def("pass_char", [](char c) { return c; });
|
||||
m.def("pass_wchar", [](wchar_t w) { return w; });
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> example.pass_char('A')
|
||||
>>> example.pass_char("A")
|
||||
'A'
|
||||
|
||||
While C++ will cast integers to character types (``char c = 0x65;``), pybind11
|
||||
does not convert Python integers to characters implicitly. The Python function
|
||||
``chr()`` can be used to convert integers to characters.
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> example.pass_char(0x65)
|
||||
TypeError
|
||||
@@ -259,17 +259,17 @@ a combining acute accent). The combining character will be lost if the
|
||||
two-character sequence is passed as an argument, even though it renders as a
|
||||
single grapheme.
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> example.pass_wchar('é')
|
||||
>>> example.pass_wchar("é")
|
||||
'é'
|
||||
|
||||
>>> combining_e_acute = 'e' + '\u0301'
|
||||
>>> combining_e_acute = "e" + "\u0301"
|
||||
|
||||
>>> combining_e_acute
|
||||
'é'
|
||||
|
||||
>>> combining_e_acute == 'é'
|
||||
>>> combining_e_acute == "é"
|
||||
False
|
||||
|
||||
>>> example.pass_wchar(combining_e_acute)
|
||||
@@ -278,9 +278,9 @@ single grapheme.
|
||||
Normalizing combining characters before passing the character literal to C++
|
||||
may resolve *some* of these issues:
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> example.pass_wchar(unicodedata.normalize('NFC', combining_e_acute))
|
||||
>>> example.pass_wchar(unicodedata.normalize("NFC", combining_e_acute))
|
||||
'é'
|
||||
|
||||
In some languages (Thai for example), there are `graphemes that cannot be
|
||||
|
||||
@@ -136,7 +136,7 @@ a virtual method call.
|
||||
u'woof! woof! woof! '
|
||||
>>> class Cat(Animal):
|
||||
... def go(self, n_times):
|
||||
... return "meow! " * n_times
|
||||
... return "meow! " * n_times
|
||||
...
|
||||
>>> c = Cat()
|
||||
>>> call_go(c)
|
||||
@@ -159,8 +159,9 @@ Here is an example:
|
||||
|
||||
class Dachshund(Dog):
|
||||
def __init__(self, name):
|
||||
Dog.__init__(self) # Without this, a TypeError is raised.
|
||||
Dog.__init__(self) # Without this, a TypeError is raised.
|
||||
self.name = name
|
||||
|
||||
def bark(self):
|
||||
return "yap!"
|
||||
|
||||
@@ -259,7 +260,7 @@ override the ``name()`` method):
|
||||
|
||||
.. 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
|
||||
function that does not take any arguments. For functions that take
|
||||
a nonzero number of arguments, the trailing comma must be omitted.
|
||||
@@ -804,7 +805,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
|
||||
a value, pointer or holder type. See :ref:`custom_constructors` for details.
|
||||
|
||||
@@ -1153,6 +1154,7 @@ error:
|
||||
|
||||
>>> class PyFinalChild(IsFinal):
|
||||
... pass
|
||||
...
|
||||
TypeError: type 'IsFinal' is not an acceptable base type
|
||||
|
||||
.. note:: This attribute is currently ignored on PyPy
|
||||
@@ -1247,7 +1249,7 @@ Accessing the type object
|
||||
|
||||
You can get the type object from a C++ class that has already been registered using:
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: cpp
|
||||
|
||||
py::type T_py = py::type::of<T>();
|
||||
|
||||
@@ -1259,3 +1261,37 @@ object, just like ``type(ob)`` in Python.
|
||||
Other types, like ``py::type::of<int>()``, do not work, see :ref:`type-conversions`.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
|
||||
Custom type setup
|
||||
=================
|
||||
|
||||
For advanced use cases, such as enabling garbage collection support, you may
|
||||
wish to directly manipulate the ``PyHeapTypeObject`` corresponding to a
|
||||
``py::class_`` definition.
|
||||
|
||||
You can do that using ``py::custom_type_setup``:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
struct OwnsPythonObjects {
|
||||
py::object value = py::none();
|
||||
};
|
||||
py::class_<OwnsPythonObjects> cls(
|
||||
m, "OwnsPythonObjects", py::custom_type_setup([](PyHeapTypeObject *heap_type) {
|
||||
auto *type = &heap_type->ht_type;
|
||||
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
|
||||
type->tp_traverse = [](PyObject *self_base, visitproc visit, void *arg) {
|
||||
auto &self = py::cast<OwnsPythonObjects&>(py::handle(self_base));
|
||||
Py_VISIT(self.value.ptr());
|
||||
return 0;
|
||||
};
|
||||
type->tp_clear = [](PyObject *self_base) {
|
||||
auto &self = py::cast<OwnsPythonObjects&>(py::handle(self_base));
|
||||
self.value = py::none();
|
||||
return 0;
|
||||
};
|
||||
}));
|
||||
cls.def(py::init<>());
|
||||
cls.def_readwrite("value", &OwnsPythonObjects::value);
|
||||
|
||||
.. versionadded:: 2.8
|
||||
|
||||
@@ -40,15 +40,15 @@ The essential structure of the ``main.cpp`` file looks like this:
|
||||
}
|
||||
|
||||
The interpreter must be initialized before using any Python API, which includes
|
||||
all the functions and classes in pybind11. The RAII guard class `scoped_interpreter`
|
||||
all the functions and classes in pybind11. The RAII guard class ``scoped_interpreter``
|
||||
takes care of the interpreter lifetime. After the guard is destroyed, the interpreter
|
||||
shuts down and clears its memory. No Python functions can be called after this.
|
||||
|
||||
Executing Python code
|
||||
=====================
|
||||
|
||||
There are a few different ways to run Python code. One option is to use `eval`,
|
||||
`exec` or `eval_file`, as explained in :ref:`eval`. Here is a quick example in
|
||||
There are a few different ways to run Python code. One option is to use ``eval``,
|
||||
``exec`` or ``eval_file``, as explained in :ref:`eval`. Here is a quick example in
|
||||
the context of an executable with an embedded interpreter:
|
||||
|
||||
.. code-block:: cpp
|
||||
@@ -108,7 +108,7 @@ The two approaches can also be combined:
|
||||
Importing modules
|
||||
=================
|
||||
|
||||
Python modules can be imported using `module_::import()`:
|
||||
Python modules can be imported using ``module_::import()``:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
@@ -122,6 +122,7 @@ embedding the interpreter. This makes it easy to import local Python files:
|
||||
|
||||
"""calc.py located in the working directory"""
|
||||
|
||||
|
||||
def add(i, j):
|
||||
return i + j
|
||||
|
||||
@@ -133,7 +134,7 @@ embedding the interpreter. This makes it easy to import local Python files:
|
||||
int n = result.cast<int>();
|
||||
assert(n == 3);
|
||||
|
||||
Modules can be reloaded using `module_::reload()` if the source is modified e.g.
|
||||
Modules can be reloaded using ``module_::reload()`` if the source is modified e.g.
|
||||
by an external process. This can be useful in scenarios where the application
|
||||
imports a user defined data processing script which needs to be updated after
|
||||
changes by the user. Note that this function does not reload modules recursively.
|
||||
@@ -143,7 +144,7 @@ changes by the user. Note that this function does not reload modules recursively
|
||||
Adding embedded modules
|
||||
=======================
|
||||
|
||||
Embedded binary modules can be added using the `PYBIND11_EMBEDDED_MODULE` macro.
|
||||
Embedded binary modules can be added using the ``PYBIND11_EMBEDDED_MODULE`` macro.
|
||||
Note that the definition must be placed at global scope. They can be imported
|
||||
like any other module.
|
||||
|
||||
@@ -169,7 +170,7 @@ like any other module.
|
||||
|
||||
Unlike extension modules where only a single binary module can be created, on
|
||||
the embedded side an unlimited number of modules can be added using multiple
|
||||
`PYBIND11_EMBEDDED_MODULE` definitions (as long as they have unique names).
|
||||
``PYBIND11_EMBEDDED_MODULE`` definitions (as long as they have unique names).
|
||||
|
||||
These modules are added to Python's list of builtins, so they can also be
|
||||
imported in pure Python files loaded by the interpreter. Everything interacts
|
||||
@@ -215,9 +216,9 @@ naturally:
|
||||
Interpreter lifetime
|
||||
====================
|
||||
|
||||
The Python interpreter shuts down when `scoped_interpreter` is destroyed. After
|
||||
The Python interpreter shuts down when ``scoped_interpreter`` is destroyed. After
|
||||
this, creating a new instance will restart the interpreter. Alternatively, the
|
||||
`initialize_interpreter` / `finalize_interpreter` pair of functions can be used
|
||||
``initialize_interpreter`` / ``finalize_interpreter`` pair of functions can be used
|
||||
to directly set the state at any time.
|
||||
|
||||
Modules created with pybind11 can be safely re-initialized after the interpreter
|
||||
@@ -229,8 +230,8 @@ global data. All the details can be found in the CPython documentation.
|
||||
|
||||
.. warning::
|
||||
|
||||
Creating two concurrent `scoped_interpreter` guards is a fatal error. So is
|
||||
calling `initialize_interpreter` for a second time after the interpreter
|
||||
Creating two concurrent ``scoped_interpreter`` guards is a fatal error. So is
|
||||
calling ``initialize_interpreter`` for a second time after the interpreter
|
||||
has already been initialized.
|
||||
|
||||
Do not use the raw CPython API functions ``Py_Initialize`` and
|
||||
@@ -241,7 +242,7 @@ global data. All the details can be found in the CPython documentation.
|
||||
Sub-interpreter support
|
||||
=======================
|
||||
|
||||
Creating multiple copies of `scoped_interpreter` is not possible because it
|
||||
Creating multiple copies of ``scoped_interpreter`` is not possible because it
|
||||
represents the main Python interpreter. Sub-interpreters are something different
|
||||
and they do permit the existence of multiple interpreters. This is an advanced
|
||||
feature of the CPython API and should be handled with care. pybind11 does not
|
||||
@@ -257,5 +258,5 @@ We'll just mention a couple of caveats the sub-interpreters support in pybind11:
|
||||
2. Managing multiple threads, multiple interpreters and the GIL can be
|
||||
challenging and there are several caveats here, even within the pure
|
||||
CPython API (please refer to the Python docs for details). As for
|
||||
pybind11, keep in mind that `gil_scoped_release` and `gil_scoped_acquire`
|
||||
pybind11, keep in mind that ``gil_scoped_release`` and ``gil_scoped_acquire``
|
||||
do not take sub-interpreters into account.
|
||||
|
||||
@@ -56,7 +56,9 @@ at its exception handler.
|
||||
+--------------------------------------+--------------------------------------+
|
||||
| :class:`pybind11::buffer_error` | ``BufferError`` |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
| :class:`pybind11::import_error` | ``import_error`` |
|
||||
| :class:`pybind11::import_error` | ``ImportError`` |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
| :class:`pybind11::attribute_error` | ``AttributeError`` |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
| Any other exception | ``RuntimeError`` |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
@@ -75,9 +77,10 @@ Registering custom translators
|
||||
|
||||
If the default exception conversion policy described above is insufficient,
|
||||
pybind11 also provides support for registering custom exception translators.
|
||||
To register a simple exception conversion that translates a C++ exception into
|
||||
a new Python exception using the C++ exception's ``what()`` method, a helper
|
||||
function is available:
|
||||
Similar to pybind11 classes, exception translators can be local to the module
|
||||
they are defined in or global to the entire python session. To register a simple
|
||||
exception conversion that translates a C++ exception into a new Python exception
|
||||
using the C++ exception's ``what()`` method, a helper function is available:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
@@ -87,29 +90,39 @@ This call creates a Python exception class with the name ``PyExp`` in the given
|
||||
module and automatically converts any encountered exceptions of type ``CppExp``
|
||||
into Python exceptions of type ``PyExp``.
|
||||
|
||||
A matching function is available for registering a local exception translator:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
py::register_local_exception<CppExp>(module, "PyExp");
|
||||
|
||||
|
||||
It is possible to specify base class for the exception using the third
|
||||
parameter, a `handle`:
|
||||
parameter, a ``handle``:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
py::register_exception<CppExp>(module, "PyExp", PyExc_RuntimeError);
|
||||
py::register_local_exception<CppExp>(module, "PyExp", PyExc_RuntimeError);
|
||||
|
||||
Then `PyExp` can be caught both as `PyExp` and `RuntimeError`.
|
||||
Then ``PyExp`` can be caught both as ``PyExp`` and ``RuntimeError``.
|
||||
|
||||
The class objects of the built-in Python exceptions are listed in the Python
|
||||
documentation on `Standard Exceptions <https://docs.python.org/3/c-api/exceptions.html#standard-exceptions>`_.
|
||||
The default base class is `PyExc_Exception`.
|
||||
The default base class is ``PyExc_Exception``.
|
||||
|
||||
When more advanced exception translation is needed, the function
|
||||
``py::register_exception_translator(translator)`` can be used to register
|
||||
When more advanced exception translation is needed, the functions
|
||||
``py::register_exception_translator(translator)`` and
|
||||
``py::register_local_exception_translator(translator)`` can be used to register
|
||||
functions that can translate arbitrary exception types (and which may include
|
||||
additional logic to do so). The function takes a stateless callable (e.g. a
|
||||
additional logic to do so). The functions takes a stateless callable (e.g. a
|
||||
function pointer or a lambda function without captured variables) with the call
|
||||
signature ``void(std::exception_ptr)``.
|
||||
|
||||
When a C++ exception is thrown, the registered exception translators are tried
|
||||
in reverse order of registration (i.e. the last registered translator gets the
|
||||
first shot at handling the exception).
|
||||
first shot at handling the exception). All local translators will be tried
|
||||
before a global translator is tried.
|
||||
|
||||
Inside the translator, ``std::rethrow_exception`` should be used within
|
||||
a try block to re-throw the exception. One or more catch clauses to catch
|
||||
@@ -164,6 +177,57 @@ section.
|
||||
may be explicitly (re-)thrown to delegate it to the other,
|
||||
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>`_.
|
||||
|
||||
|
||||
Local vs Global Exception Translators
|
||||
=====================================
|
||||
|
||||
When a global exception translator is registered, it will be applied across all
|
||||
modules in the reverse order of registration. This can create behavior where the
|
||||
order of module import influences how exceptions are translated.
|
||||
|
||||
If module1 has the following translator:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
py::register_exception_translator([](std::exception_ptr p) {
|
||||
try {
|
||||
if (p) std::rethrow_exception(p);
|
||||
} catch (const std::invalid_argument &e) {
|
||||
PyErr_SetString("module1 handled this")
|
||||
}
|
||||
}
|
||||
|
||||
and module2 has the following similar translator:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
py::register_exception_translator([](std::exception_ptr p) {
|
||||
try {
|
||||
if (p) std::rethrow_exception(p);
|
||||
} catch (const std::invalid_argument &e) {
|
||||
PyErr_SetString("module2 handled this")
|
||||
}
|
||||
}
|
||||
|
||||
then which translator handles the invalid_argument will be determined by the
|
||||
order that module1 and module2 are imported. Since exception translators are
|
||||
applied in the reverse order of registration, which ever module was imported
|
||||
last will "win" and that translator will be applied.
|
||||
|
||||
If there are multiple pybind11 modules that share exception types (either
|
||||
standard built-in or custom) loaded into a single python instance and
|
||||
consistent error handling behavior is needed, then local translators should be
|
||||
used.
|
||||
|
||||
Changing the previous example to use ``register_local_exception_translator``
|
||||
would mean that when invalid_argument is thrown in the module2 code, the
|
||||
module2 translator will always handle it, while in module1, the module1
|
||||
translator will do the same.
|
||||
|
||||
.. _handling_python_exceptions_cpp:
|
||||
|
||||
Handling exceptions from Python in C++
|
||||
@@ -261,6 +325,34 @@ Alternately, to ignore the error, call `PyErr_Clear
|
||||
Any Python error must be thrown or cleared, or Python/pybind11 will be left in
|
||||
an invalid state.
|
||||
|
||||
Chaining exceptions ('raise from')
|
||||
==================================
|
||||
|
||||
In Python 3.3 a mechanism for indicating that exceptions were caused by other
|
||||
exceptions was introduced:
|
||||
|
||||
.. code-block:: py
|
||||
|
||||
try:
|
||||
print(1 / 0)
|
||||
except Exception as exc:
|
||||
raise RuntimeError("could not divide by zero") from exc
|
||||
|
||||
To do a similar thing in pybind11, you can use the ``py::raise_from`` function. It
|
||||
sets the current python error indicator, so to continue propagating the exception
|
||||
you should ``throw py::error_already_set()`` (Python 3 only).
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
try {
|
||||
py::eval("print(1 / 0"));
|
||||
} catch (py::error_already_set &e) {
|
||||
py::raise_from(e, PyExc_RuntimeError, "could not divide by zero");
|
||||
throw py::error_already_set();
|
||||
}
|
||||
|
||||
.. versionadded:: 2.8
|
||||
|
||||
.. _unraisable_exceptions:
|
||||
|
||||
Handling unraisable exceptions
|
||||
|
||||
@@ -50,7 +50,7 @@ implied transfer of ownership, i.e.:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
m.def("get_data", &get_data, return_value_policy::reference);
|
||||
m.def("get_data", &get_data, py::return_value_policy::reference);
|
||||
|
||||
On the other hand, this is not the right policy for many other situations,
|
||||
where ignoring ownership could lead to resource leaks.
|
||||
@@ -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 |
|
||||
| | 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 |
|
||||
| | pointer. Otherwise, it uses :enum:`return_value_policy::move` or |
|
||||
| | :enum:`return_value_policy::copy` for rvalue and lvalue references, |
|
||||
| | 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 |
|
||||
| | return value is a pointer. This is the default conversion policy for |
|
||||
| | 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:
|
||||
@@ -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
|
||||
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
|
||||
operation ties the lifetime of the newly added element to the underlying
|
||||
container:
|
||||
@@ -228,7 +232,7 @@ is equivalent to the following pseudocode:
|
||||
});
|
||||
|
||||
The only requirement is that ``T`` is default-constructible, but otherwise any
|
||||
scope guard will work. This is very useful in combination with `gil_scoped_release`.
|
||||
scope guard will work. This is very useful in combination with ``gil_scoped_release``.
|
||||
See :ref:`gil`.
|
||||
|
||||
Multiple guards can also be specified as ``py::call_guard<T1, T2, T3...>``. The
|
||||
@@ -251,7 +255,7 @@ For instance, the following statement iterates over a Python ``dict``:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
void print_dict(py::dict dict) {
|
||||
void print_dict(const py::dict& dict) {
|
||||
/* Easily interact with Python types */
|
||||
for (auto item : dict)
|
||||
std::cout << "key=" << std::string(py::str(item.first)) << ", "
|
||||
@@ -268,7 +272,7 @@ And used in Python as usual:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> print_dict({'foo': 123, 'bar': 'hello'})
|
||||
>>> print_dict({"foo": 123, "bar": "hello"})
|
||||
key=foo, value=123
|
||||
key=bar, value=hello
|
||||
|
||||
@@ -289,7 +293,7 @@ Such functions can also be created using pybind11:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
void generic(py::args args, py::kwargs kwargs) {
|
||||
void generic(py::args args, const py::kwargs& kwargs) {
|
||||
/// .. do something with args
|
||||
if (kwargs)
|
||||
/// .. do something with kwargs
|
||||
@@ -373,10 +377,11 @@ argument in a function definition:
|
||||
def f(a, *, b): # a can be positional or via keyword; b must be via keyword
|
||||
pass
|
||||
|
||||
|
||||
f(a=1, b=2) # good
|
||||
f(b=2, a=1) # good
|
||||
f(1, b=2) # good
|
||||
f(1, 2) # TypeError: f() takes 1 positional argument but 2 were given
|
||||
f(1, b=2) # good
|
||||
f(1, 2) # TypeError: f() takes 1 positional argument but 2 were given
|
||||
|
||||
Pybind11 provides a ``py::kw_only`` object that allows you to implement
|
||||
the same behaviour by specifying the object between positional and keyword-only
|
||||
|
||||
@@ -84,7 +84,7 @@ could be realized as follows (important changes highlighted):
|
||||
});
|
||||
}
|
||||
|
||||
The ``call_go`` wrapper can also be simplified using the `call_guard` policy
|
||||
The ``call_go`` wrapper can also be simplified using the ``call_guard`` policy
|
||||
(see :ref:`call_policies`) which yields the same result:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
@@ -171,6 +171,31 @@ template parameter, and it ensures that non-conforming arguments are converted
|
||||
into an array satisfying the specified requirements instead of trying the next
|
||||
function overload.
|
||||
|
||||
There are several methods on arrays; the methods listed below under references
|
||||
work, as well as the following functions based on the NumPy API:
|
||||
|
||||
- ``.dtype()`` returns the type of the contained values.
|
||||
|
||||
- ``.strides()`` returns a pointer to the strides of the array (optionally pass
|
||||
an integer axis to get a number).
|
||||
|
||||
- ``.flags()`` returns the flag settings. ``.writable()`` and ``.owndata()``
|
||||
are directly available.
|
||||
|
||||
- ``.offset_at()`` returns the offset (optionally pass indices).
|
||||
|
||||
- ``.squeeze()`` returns a view with length-1 axes removed.
|
||||
|
||||
- ``.view(dtype)`` returns a view of the array with a different dtype.
|
||||
|
||||
- ``.reshape({i, j, ...})`` returns a view of the array with a different shape.
|
||||
``.resize({...})`` is also available.
|
||||
|
||||
- ``.index_at(i, j, ...)`` gets the count from the beginning to a given index.
|
||||
|
||||
|
||||
There are also several methods for getting references (described below).
|
||||
|
||||
Structured types
|
||||
================
|
||||
|
||||
@@ -233,8 +258,8 @@ by the compiler. The result is returned as a NumPy array of type
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> x = np.array([[1, 3],[5, 7]])
|
||||
>>> y = np.array([[2, 4],[6, 8]])
|
||||
>>> x = np.array([[1, 3], [5, 7]])
|
||||
>>> y = np.array([[2, 4], [6, 8]])
|
||||
>>> z = 3
|
||||
>>> result = vectorized_func(x, y, z)
|
||||
|
||||
@@ -345,21 +370,21 @@ The returned proxy object supports some of the same methods as ``py::array`` so
|
||||
that it can be used as a drop-in replacement for some existing, index-checked
|
||||
uses of ``py::array``:
|
||||
|
||||
- ``r.ndim()`` returns the number of dimensions
|
||||
- ``.ndim()`` returns the number of dimensions
|
||||
|
||||
- ``r.data(1, 2, ...)`` and ``r.mutable_data(1, 2, ...)``` returns a pointer to
|
||||
- ``.data(1, 2, ...)`` and ``r.mutable_data(1, 2, ...)``` returns a pointer to
|
||||
the ``const T`` or ``T`` data, respectively, at the given indices. The
|
||||
latter is only available to proxies obtained via ``a.mutable_unchecked()``.
|
||||
|
||||
- ``itemsize()`` returns the size of an item in bytes, i.e. ``sizeof(T)``.
|
||||
- ``.itemsize()`` returns the size of an item in bytes, i.e. ``sizeof(T)``.
|
||||
|
||||
- ``ndim()`` returns the number of dimensions.
|
||||
- ``.ndim()`` returns the number of dimensions.
|
||||
|
||||
- ``shape(n)`` returns the size of dimension ``n``
|
||||
- ``.shape(n)`` returns the size of dimension ``n``
|
||||
|
||||
- ``size()`` returns the total number of elements (i.e. the product of the shapes).
|
||||
- ``.size()`` returns the total number of elements (i.e. the product of the shapes).
|
||||
|
||||
- ``nbytes()`` returns the number of bytes used by the referenced elements
|
||||
- ``.nbytes()`` returns the number of bytes used by the referenced elements
|
||||
(i.e. ``itemsize()`` times ``size()``).
|
||||
|
||||
.. seealso::
|
||||
@@ -378,7 +403,7 @@ In Python 2, the syntactic sugar ``...`` is not available, but the singleton
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
a = # a NumPy array
|
||||
a = ... # a NumPy array
|
||||
b = a[0, ..., 0]
|
||||
|
||||
The function ``py::ellipsis()`` function can be used to perform the same
|
||||
@@ -390,7 +415,7 @@ operation on the C++ side:
|
||||
py::array b = a[py::make_tuple(0, py::ellipsis(), 0)];
|
||||
|
||||
.. versionchanged:: 2.6
|
||||
``py::ellipsis()`` is now also avaliable in Python 2.
|
||||
``py::ellipsis()`` is now also available in Python 2.
|
||||
|
||||
Memory view
|
||||
===========
|
||||
|
||||
@@ -20,6 +20,40 @@ Available types include :class:`handle`, :class:`object`, :class:`bool_`,
|
||||
Be sure to review the :ref:`pytypes_gotchas` before using this heavily in
|
||||
your C++ API.
|
||||
|
||||
.. _instantiating_compound_types:
|
||||
|
||||
Instantiating compound Python types from C++
|
||||
============================================
|
||||
|
||||
Dictionaries can be initialized in the :class:`dict` constructor:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
using namespace pybind11::literals; // to bring in the `_a` literal
|
||||
py::dict d("spam"_a=py::none(), "eggs"_a=42);
|
||||
|
||||
A tuple of python objects can be instantiated using :func:`py::make_tuple`:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
py::tuple tup = py::make_tuple(42, py::none(), "spam");
|
||||
|
||||
Each element is converted to a supported Python type.
|
||||
|
||||
A `simple namespace`_ can be instantiated using
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
using namespace pybind11::literals; // to bring in the `_a` literal
|
||||
py::object SimpleNamespace = py::module_::import("types").attr("SimpleNamespace");
|
||||
py::object ns = SimpleNamespace("spam"_a=py::none(), "eggs"_a=42);
|
||||
|
||||
Attributes on a namespace can be modified with the :func:`py::delattr`,
|
||||
:func:`py::getattr`, and :func:`py::setattr` functions. Simple namespaces can
|
||||
be useful as lightweight stand-ins for class instances.
|
||||
|
||||
.. _simple namespace: https://docs.python.org/3/library/types.html#types.SimpleNamespace
|
||||
|
||||
.. _casting_back_and_forth:
|
||||
|
||||
Casting back and forth
|
||||
@@ -30,7 +64,7 @@ types to Python, which can be done using :func:`py::cast`:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
MyClass *cls = ..;
|
||||
MyClass *cls = ...;
|
||||
py::object obj = py::cast(cls);
|
||||
|
||||
The reverse direction uses the following syntax:
|
||||
@@ -132,6 +166,7 @@ Keyword arguments are also supported. In Python, there is the usual call syntax:
|
||||
def f(number, say, to):
|
||||
... # function code
|
||||
|
||||
|
||||
f(1234, say="hello", to=some_instance) # keyword call in Python
|
||||
|
||||
In C++, the same call can be made using:
|
||||
|
||||
@@ -28,7 +28,7 @@ Capturing standard output from ostream
|
||||
|
||||
Often, a library will use the streams ``std::cout`` and ``std::cerr`` to print,
|
||||
but this does not play well with Python's standard ``sys.stdout`` and ``sys.stderr``
|
||||
redirection. Replacing a library's printing with `py::print <print>` may not
|
||||
redirection. Replacing a library's printing with ``py::print <print>`` may not
|
||||
be feasible. This can be fixed using a guard around the library function that
|
||||
redirects output to the corresponding Python streams:
|
||||
|
||||
@@ -47,15 +47,26 @@ redirects output to the corresponding Python streams:
|
||||
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
|
||||
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
|
||||
the Python output, are optional, and default to standard output if not given. An
|
||||
extra type, `py::scoped_estream_redirect <scoped_estream_redirect>`, is identical
|
||||
extra type, ``py::scoped_estream_redirect <scoped_estream_redirect>``, is identical
|
||||
except for defaulting to ``std::cerr`` and ``sys.stderr``; this can be useful with
|
||||
`py::call_guard`, which allows multiple items, but uses the default constructor:
|
||||
``py::call_guard``, which allows multiple items, but uses the default constructor:
|
||||
|
||||
.. code-block:: py
|
||||
.. code-block:: cpp
|
||||
|
||||
// Alternative: Call single function using call guard
|
||||
m.def("noisy_func", &call_noisy_function,
|
||||
@@ -63,7 +74,7 @@ except for defaulting to ``std::cerr`` and ``sys.stderr``; this can be useful wi
|
||||
py::scoped_estream_redirect>());
|
||||
|
||||
The redirection can also be done in Python with the addition of a context
|
||||
manager, using the `py::add_ostream_redirect() <add_ostream_redirect>` function:
|
||||
manager, using the ``py::add_ostream_redirect() <add_ostream_redirect>`` function:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
@@ -92,7 +103,7 @@ arguments to disable one of the streams if needed.
|
||||
Evaluating Python expressions from strings and files
|
||||
====================================================
|
||||
|
||||
pybind11 provides the `eval`, `exec` and `eval_file` functions to evaluate
|
||||
pybind11 provides the ``eval``, ``exec`` and ``eval_file`` functions to evaluate
|
||||
Python expressions and statements. The following example illustrates how they
|
||||
can be used.
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ segmentation fault).
|
||||
.. code-block:: python
|
||||
|
||||
from example import Parent
|
||||
|
||||
print(Parent().get_child())
|
||||
|
||||
The problem is that ``Parent::get_child()`` returns a pointer to an instance of
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import random
|
||||
import os
|
||||
import time
|
||||
import datetime as dt
|
||||
import os
|
||||
import random
|
||||
|
||||
nfns = 4 # Functions per class
|
||||
nargs = 4 # Arguments per function
|
||||
|
||||
@@ -7,6 +7,423 @@ Starting with version 1.8.0, pybind11 releases use a `semantic versioning
|
||||
<http://semver.org>`_ policy.
|
||||
|
||||
|
||||
IN DEVELOPMENT
|
||||
--------------
|
||||
|
||||
v2.8.1 (Oct 27, 2021)
|
||||
---------------------
|
||||
|
||||
Changes and additions:
|
||||
|
||||
* The simple namespace creation shortcut added in 2.8.0 was deprecated due to
|
||||
usage of CPython internal API, and will be removed soon. Use
|
||||
``py::module_::import("types").attr("SimpleNamespace")``.
|
||||
`#3374 <https://github.com/pybinyyd/pybind11/pull/3374>`_
|
||||
|
||||
* Add C++ Exception type to throw and catch ``AttributeError``. Useful for
|
||||
defining custom ``__setattr__`` and ``__getattr__`` methods.
|
||||
`#3387 <https://github.com/pybind/pybind11/pull/3387>`_
|
||||
|
||||
Fixes:
|
||||
|
||||
* Fixed the potential for dangling references when using properties with
|
||||
``std::optional`` types.
|
||||
`#3376 <https://github.com/pybind/pybind11/pull/3376>`_
|
||||
|
||||
* Modernize usage of ``PyCodeObject`` on Python 3.9+ (moving toward support for
|
||||
Python 3.11a1)
|
||||
`#3368 <https://github.com/pybind/pybind11/pull/3368>`_
|
||||
|
||||
* A long-standing bug in ``eigen.h`` was fixed (originally PR #3343). The bug
|
||||
was unmasked by newly added ``static_assert``'s in the Eigen 3.4.0 release.
|
||||
`#3352 <https://github.com/pybind/pybind11/pull/3352>`_
|
||||
|
||||
* Support multiple raw inclusion of CMake helper files (Conan.io does this for
|
||||
multi-config generators).
|
||||
`#3420 <https://github.com/pybind/pybind11/pull/3420>`_
|
||||
|
||||
* Fix harmless warning on upcoming CMake 3.22.
|
||||
`#3368 <https://github.com/pybind/pybind11/pull/3368>`_
|
||||
|
||||
* Fix 2.8.0 regression with MSVC 2017 + C++17 mode + Python 3.
|
||||
`#3407 <https://github.com/pybind/pybind11/pull/3407>`_
|
||||
|
||||
* Fix 2.8.0 regression that caused undefined behavior (typically
|
||||
segfaults) in ``make_key_iterator``/``make_value_iterator`` if dereferencing
|
||||
the iterator returned a temporary value instead of a reference.
|
||||
`#3348 <https://github.com/pybind/pybind11/pull/3348>`_
|
||||
|
||||
|
||||
v2.8.0 (Oct 4, 2021)
|
||||
--------------------
|
||||
|
||||
New features:
|
||||
|
||||
* Added ``py::raise_from`` to enable chaining exceptions.
|
||||
`#3215 <https://github.com/pybind/pybind11/pull/3215>`_
|
||||
|
||||
* 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/pybinyyd/pybind11/pull/2650>`_
|
||||
|
||||
* Add ``make_simple_namespace`` function for instantiating Python
|
||||
``SimpleNamespace`` objects. **Deprecated in 2.8.1.**
|
||||
`#2840 <https://github.com/pybind/pybind11/pull/2840>`_
|
||||
|
||||
* ``pybind11::scoped_interpreter`` and ``initialize_interpreter`` have new
|
||||
arguments to allow ``sys.argv`` initialization.
|
||||
`#2341 <https://github.com/pybind/pybind11/pull/2341>`_
|
||||
|
||||
* Allow Python builtins to be used as callbacks in CPython.
|
||||
`#1413 <https://github.com/pybind/pybind11/pull/1413>`_
|
||||
|
||||
* Added ``view`` to view arrays with a different datatype.
|
||||
`#987 <https://github.com/pybind/pybind11/pull/987>`_
|
||||
|
||||
* Implemented ``reshape`` on arrays.
|
||||
`#984 <https://github.com/pybind/pybind11/pull/984>`_
|
||||
|
||||
* Enable defining custom ``__new__`` methods on classes by fixing bug
|
||||
preventing overriding methods if they have non-pybind11 siblings.
|
||||
`#3265 <https://github.com/pybind/pybind11/pull/3265>`_
|
||||
|
||||
* Add ``make_value_iterator()``, and fix ``make_key_iterator()`` to return
|
||||
references instead of copies.
|
||||
`#3293 <https://github.com/pybind/pybind11/pull/3293>`_
|
||||
|
||||
* Improve the classes generated by ``bind_map``: `#3310 <https://github.com/pybind/pybind11/pull/3310>`_
|
||||
|
||||
* Change ``.items`` from an iterator to a dictionary view.
|
||||
* Add ``.keys`` and ``.values`` (both dictionary views).
|
||||
* Allow ``__contains__`` to take any object.
|
||||
|
||||
* ``pybind11::custom_type_setup`` was added, for customizing the
|
||||
``PyHeapTypeObject`` corresponding to a class, which may be useful for
|
||||
enabling garbage collection support, among other things.
|
||||
`#3287 <https://github.com/pybind/pybind11/pull/3287>`_
|
||||
|
||||
|
||||
Changes:
|
||||
|
||||
* Set ``__file__`` constant when running ``eval_file`` in an embedded interpreter.
|
||||
`#3233 <https://github.com/pybind/pybind11/pull/3233>`_
|
||||
|
||||
* Python objects and (C++17) ``std::optional`` now accepted in ``py::slice``
|
||||
constructor.
|
||||
`#1101 <https://github.com/pybind/pybind11/pull/1101>`_
|
||||
|
||||
* The pybind11 proxy types ``str``, ``bytes``, ``bytearray``, ``tuple``,
|
||||
``list`` now consistently support passing ``ssize_t`` values for sizes and
|
||||
indexes. Previously, only ``size_t`` was accepted in several interfaces.
|
||||
`#3219 <https://github.com/pybind/pybind11/pull/3219>`_
|
||||
|
||||
* Avoid evaluating ``PYBIND11_TLS_REPLACE_VALUE`` arguments more than once.
|
||||
`#3290 <https://github.com/pybind/pybind11/pull/3290>`_
|
||||
|
||||
Fixes:
|
||||
|
||||
* Bug fix: enum value's ``__int__`` returning non-int when underlying type is
|
||||
bool or of char type.
|
||||
`#1334 <https://github.com/pybind/pybind11/pull/1334>`_
|
||||
|
||||
* Fixes bug in setting error state in Capsule's pointer methods.
|
||||
`#3261 <https://github.com/pybind/pybind11/pull/3261>`_
|
||||
|
||||
* A long-standing memory leak in ``py::cpp_function::initialize`` was fixed.
|
||||
`#3229 <https://github.com/pybind/pybind11/pull/3229>`_
|
||||
|
||||
* Fixes thread safety for some ``pybind11::type_caster`` which require lifetime
|
||||
extension, such as for ``std::string_view``.
|
||||
`#3237 <https://github.com/pybind/pybind11/pull/3237>`_
|
||||
|
||||
* Restore compatibility with gcc 4.8.4 as distributed by ubuntu-trusty, linuxmint-17.
|
||||
`#3270 <https://github.com/pybind/pybind11/pull/3270>`_
|
||||
|
||||
|
||||
Build system improvements:
|
||||
|
||||
* Fix regression in CMake Python package config: improper use of absolute path.
|
||||
`#3144 <https://github.com/pybind/pybind11/pull/3144>`_
|
||||
|
||||
* Cached Python version information could become stale when CMake was re-run
|
||||
with a different Python version. The build system now detects this and
|
||||
updates this information.
|
||||
`#3299 <https://github.com/pybind/pybind11/pull/3299>`_
|
||||
|
||||
* Specified UTF8-encoding in setup.py calls of open().
|
||||
`#3137 <https://github.com/pybind/pybind11/pull/3137>`_
|
||||
|
||||
* Fix a harmless warning from CMake 3.21 with the classic Python discovery.
|
||||
`#3220 <https://github.com/pybind/pybind11/pull/3220>`_
|
||||
|
||||
* Eigen repo and version can now be specified as cmake options.
|
||||
`#3324 <https://github.com/pybind/pybind11/pull/3324>`_
|
||||
|
||||
|
||||
Backend and tidying up:
|
||||
|
||||
* Reduced thread-local storage required for keeping alive temporary data for
|
||||
type conversion to one key per ABI version, rather than one key per extension
|
||||
module. This makes the total thread-local storage required by pybind11 2
|
||||
keys per ABI version.
|
||||
`#3275 <https://github.com/pybind/pybind11/pull/3275>`_
|
||||
|
||||
* Optimize NumPy array construction with additional moves.
|
||||
`#3183 <https://github.com/pybind/pybind11/pull/3183>`_
|
||||
|
||||
* Conversion to ``std::string`` and ``std::string_view`` now avoids making an
|
||||
extra copy of the data on Python >= 3.3.
|
||||
`#3257 <https://github.com/pybind/pybind11/pull/3257>`_
|
||||
|
||||
* Remove const modifier from certain C++ methods on Python collections
|
||||
(``list``, ``set``, ``dict``) such as (``clear()``, ``append()``,
|
||||
``insert()``, etc...) and annotated them with ``py-non-const``.
|
||||
|
||||
* Enable readability ``clang-tidy-const-return`` and remove useless consts.
|
||||
`#3254 <https://github.com/pybind/pybind11/pull/3254>`_
|
||||
`#3194 <https://github.com/pybind/pybind11/pull/3194>`_
|
||||
|
||||
* The clang-tidy ``google-explicit-constructor`` option was enabled.
|
||||
`#3250 <https://github.com/pybind/pybind11/pull/3250>`_
|
||||
|
||||
* Mark a pytype move constructor as noexcept (perf).
|
||||
`#3236 <https://github.com/pybind/pybind11/pull/3236>`_
|
||||
|
||||
* Enable clang-tidy check to guard against inheritance slicing.
|
||||
`#3210 <https://github.com/pybind/pybind11/pull/3210>`_
|
||||
|
||||
* Legacy warning suppression pragma were removed from eigen.h. On Unix
|
||||
platforms, please use -isystem for Eigen include directories, to suppress
|
||||
compiler warnings originating from Eigen headers. Note that CMake does this
|
||||
by default. No adjustments are needed for Windows.
|
||||
`#3198 <https://github.com/pybind/pybind11/pull/3198>`_
|
||||
|
||||
* Format pybind11 with isort consistent ordering of imports
|
||||
`#3195 <https://github.com/pybind/pybind11/pull/3195>`_
|
||||
|
||||
* The warnings-suppression "pragma clamp" at the top/bottom of pybind11 was
|
||||
removed, clearing the path to refactoring and IWYU cleanup.
|
||||
`#3186 <https://github.com/pybind/pybind11/pull/3186>`_
|
||||
|
||||
* Enable most bugprone checks in clang-tidy and fix the found potential bugs
|
||||
and poor coding styles.
|
||||
`#3166 <https://github.com/pybind/pybind11/pull/3166>`_
|
||||
|
||||
* 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>`_
|
||||
|
||||
|
||||
|
||||
|
||||
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`` could also hold ``bytes``, which is probably surprising, was
|
||||
never documented, and can mask bugs (e.g. accidental use of ``py::str``
|
||||
instead of ``py::bytes``).
|
||||
`#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)
|
||||
---------------------
|
||||
|
||||
@@ -86,7 +503,7 @@ Bug fixes:
|
||||
* Fix ``py::gil_scoped_acquire`` assert with CPython 3.9 debug build.
|
||||
`#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>`_
|
||||
|
||||
Warning fixes:
|
||||
@@ -495,7 +912,7 @@ v2.4.0 (Sep 19, 2019)
|
||||
`#1888 <https://github.com/pybind/pybind11/pull/1888>`_.
|
||||
|
||||
* ``py::details::overload_cast_impl`` is available in C++11 mode, can be used
|
||||
like ``overload_cast`` with an additional set of parantheses.
|
||||
like ``overload_cast`` with an additional set of parentheses.
|
||||
`#1581 <https://github.com/pybind/pybind11/pull/1581>`_.
|
||||
|
||||
* Fixed ``get_include()`` on Conda.
|
||||
@@ -817,6 +1234,7 @@ v2.2.0 (August 31, 2017)
|
||||
|
||||
from cpp_module import CppBase1, CppBase2
|
||||
|
||||
|
||||
class PyDerived(CppBase1, CppBase2):
|
||||
def __init__(self):
|
||||
CppBase1.__init__(self) # C++ bases must be initialized explicitly
|
||||
@@ -1029,7 +1447,7 @@ v2.2.0 (August 31, 2017)
|
||||
* Intel C++ compiler compatibility fixes.
|
||||
`#937 <https://github.com/pybind/pybind11/pull/937>`_.
|
||||
|
||||
* Fixed implicit conversion of `py::enum_` to integer types on Python 2.7.
|
||||
* Fixed implicit conversion of ``py::enum_`` to integer types on Python 2.7.
|
||||
`#821 <https://github.com/pybind/pybind11/pull/821>`_.
|
||||
|
||||
* Added ``py::hash`` to fetch the hash value of Python objects, and
|
||||
|
||||
@@ -44,12 +44,12 @@ interactive Python session demonstrating this example is shown below:
|
||||
|
||||
% python
|
||||
>>> import example
|
||||
>>> p = example.Pet('Molly')
|
||||
>>> p = example.Pet("Molly")
|
||||
>>> print(p)
|
||||
<example.Pet object at 0x10cd98060>
|
||||
>>> p.getName()
|
||||
u'Molly'
|
||||
>>> p.setName('Charly')
|
||||
>>> p.setName("Charly")
|
||||
>>> p.getName()
|
||||
u'Charly'
|
||||
|
||||
@@ -122,10 +122,10 @@ This makes it possible to write
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> p = example.Pet('Molly')
|
||||
>>> p = example.Pet("Molly")
|
||||
>>> p.name
|
||||
u'Molly'
|
||||
>>> p.name = 'Charly'
|
||||
>>> p.name = "Charly"
|
||||
>>> p.name
|
||||
u'Charly'
|
||||
|
||||
@@ -174,10 +174,10 @@ Native Python classes can pick up new attributes dynamically:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> class Pet:
|
||||
... name = 'Molly'
|
||||
... name = "Molly"
|
||||
...
|
||||
>>> p = Pet()
|
||||
>>> p.name = 'Charly' # overwrite existing
|
||||
>>> p.name = "Charly" # overwrite existing
|
||||
>>> p.age = 2 # dynamically add a new attribute
|
||||
|
||||
By default, classes exported from C++ do not support this and the only writable
|
||||
@@ -195,7 +195,7 @@ Trying to set any other attribute results in an error:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> p = example.Pet()
|
||||
>>> p.name = 'Charly' # OK, attribute defined in C++
|
||||
>>> p.name = "Charly" # OK, attribute defined in C++
|
||||
>>> p.age = 2 # fail
|
||||
AttributeError: 'Pet' object has no attribute 'age'
|
||||
|
||||
@@ -213,7 +213,7 @@ Now everything works as expected:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> p = example.Pet()
|
||||
>>> p.name = 'Charly' # OK, overwrite value in C++
|
||||
>>> p.name = "Charly" # OK, overwrite value in C++
|
||||
>>> p.age = 2 # OK, dynamically add a new attribute
|
||||
>>> p.__dict__ # just like a native Python class
|
||||
{'age': 2}
|
||||
@@ -280,7 +280,7 @@ expose fields and methods of both types:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> p = example.Dog('Molly')
|
||||
>>> p = example.Dog("Molly")
|
||||
>>> p.name
|
||||
u'Molly'
|
||||
>>> p.bark()
|
||||
@@ -446,8 +446,7 @@ you can use ``py::detail::overload_cast_impl`` with an additional set of parenth
|
||||
Enumerations and internal types
|
||||
===============================
|
||||
|
||||
Let's now suppose that the example class contains an internal enumeration type,
|
||||
e.g.:
|
||||
Let's now suppose that the example class contains internal types like enumerations, e.g.:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
@@ -457,10 +456,15 @@ e.g.:
|
||||
Cat
|
||||
};
|
||||
|
||||
struct Attributes {
|
||||
float age = 0;
|
||||
};
|
||||
|
||||
Pet(const std::string &name, Kind type) : name(name), type(type) { }
|
||||
|
||||
std::string name;
|
||||
Kind type;
|
||||
Attributes attr;
|
||||
};
|
||||
|
||||
The binding code for this example looks as follows:
|
||||
@@ -471,22 +475,28 @@ The binding code for this example looks as follows:
|
||||
|
||||
pet.def(py::init<const std::string &, Pet::Kind>())
|
||||
.def_readwrite("name", &Pet::name)
|
||||
.def_readwrite("type", &Pet::type);
|
||||
.def_readwrite("type", &Pet::type)
|
||||
.def_readwrite("attr", &Pet::attr);
|
||||
|
||||
py::enum_<Pet::Kind>(pet, "Kind")
|
||||
.value("Dog", Pet::Kind::Dog)
|
||||
.value("Cat", Pet::Kind::Cat)
|
||||
.export_values();
|
||||
|
||||
To ensure that the ``Kind`` type is created within the scope of ``Pet``, the
|
||||
``pet`` :class:`class_` instance must be supplied to the :class:`enum_`.
|
||||
py::class_<Pet::Attributes> attributes(pet, "Attributes")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("age", &Pet::Attributes::age);
|
||||
|
||||
|
||||
To ensure that the nested types ``Kind`` and ``Attributes`` are created within the scope of ``Pet``, the
|
||||
``pet`` :class:`class_` instance must be supplied to the :class:`enum_` and :class:`class_`
|
||||
constructor. The :func:`enum_::export_values` function exports the enum entries
|
||||
into the parent scope, which should be skipped for newer C++11-style strongly
|
||||
typed enums.
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> p = Pet('Lucy', Pet.Cat)
|
||||
>>> p = Pet("Lucy", Pet.Cat)
|
||||
>>> p.type
|
||||
Kind.Cat
|
||||
>>> int(p.type)
|
||||
@@ -508,7 +518,7 @@ The ``name`` property returns the name of the enum value as a unicode string.
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> p = Pet( "Lucy", Pet.Cat )
|
||||
>>> p = Pet("Lucy", Pet.Cat)
|
||||
>>> pet_type = p.type
|
||||
>>> pet_type
|
||||
Pet.Cat
|
||||
|
||||
@@ -42,10 +42,7 @@ An example of a ``setup.py`` using pybind11's helpers:
|
||||
),
|
||||
]
|
||||
|
||||
setup(
|
||||
...,
|
||||
ext_modules=ext_modules
|
||||
)
|
||||
setup(..., ext_modules=ext_modules)
|
||||
|
||||
If you want to do an automatic search for the highest supported C++ standard,
|
||||
that is supported via a ``build_ext`` command override; it will only affect
|
||||
@@ -64,11 +61,20 @@ that is supported via a ``build_ext`` command override; it will only affect
|
||||
),
|
||||
]
|
||||
|
||||
setup(
|
||||
...,
|
||||
cmdclass={"build_ext": build_ext},
|
||||
ext_modules=ext_modules
|
||||
)
|
||||
setup(..., cmdclass={"build_ext": build_ext}, 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
|
||||
for NumPy's parallel compilation distutils tool is included. Use it like this:
|
||||
@@ -93,14 +99,14 @@ to a memory dependent number.
|
||||
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
|
||||
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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pybind11.setup_helpers import ParallelCompile, naive_recompile
|
||||
|
||||
SmartCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install()
|
||||
ParallelCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install()
|
||||
|
||||
|
||||
If you have a more complex build, you can implement a smarter function and pass
|
||||
@@ -149,7 +155,7 @@ Your ``pyproject.toml`` file will likely look something like this:
|
||||
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
|
||||
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/
|
||||
.. _cibuildwheel: https://cibuildwheel.readthedocs.io
|
||||
@@ -334,7 +340,7 @@ standard explicitly with
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14 CACHE STRING "C++ version selection") # or 11, 14, 17, 20
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON) # optional, ensure standard is supported
|
||||
set(CMAKE_CXX_EXTENSIONS OFF) # optional, keep compiler extensionsn off
|
||||
set(CMAKE_CXX_EXTENSIONS OFF) # optional, keep compiler extensions off
|
||||
|
||||
The variables can also be set when calling CMake from the command line using
|
||||
the ``-D<variable>=<value>`` flag. You can also manually set ``CXX_STANDARD``
|
||||
@@ -411,7 +417,7 @@ existing targets instead:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
cmake_minumum_required(VERSION 3.15...3.19)
|
||||
cmake_minimum_required(VERSION 3.15...3.19)
|
||||
project(example LANGUAGES CXX)
|
||||
|
||||
find_package(Python COMPONENTS Interpreter Development REQUIRED)
|
||||
@@ -516,7 +522,7 @@ Instead of setting properties, you can set ``CMAKE_*`` variables to initialize t
|
||||
compiler flags are provided to ensure high quality code generation. In
|
||||
contrast to the ``pybind11_add_module()`` command, the CMake interface
|
||||
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.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
|
||||
@@ -13,12 +13,11 @@
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
DIR = Path(__file__).parent.resolve()
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Frequently asked questions
|
||||
===========================================================
|
||||
|
||||
1. Make sure that the name specified in PYBIND11_MODULE is identical to the
|
||||
filename of the extension library (without 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
|
||||
version of Python (for instance, the extension library was compiled against
|
||||
@@ -54,7 +54,7 @@ provided by the caller -- in fact, it does nothing at all.
|
||||
.. code-block:: python
|
||||
|
||||
def increment(i):
|
||||
i += 1 # nope..
|
||||
i += 1 # nope..
|
||||
|
||||
pybind11 is also affected by such language-level conventions, which means that
|
||||
binding ``increment`` or ``increment_ptr`` will also create Python functions
|
||||
@@ -169,8 +169,8 @@ can be changed, but even if it isn't it is not always enough to guarantee
|
||||
complete independence of the symbols involved when not using
|
||||
``-fvisibility=hidden``.
|
||||
|
||||
Additionally, ``-fvisiblity=hidden`` can deliver considerably binary size
|
||||
savings. (See the following section for more details).
|
||||
Additionally, ``-fvisibility=hidden`` can deliver considerably binary size
|
||||
savings. (See the following section for more details.)
|
||||
|
||||
|
||||
.. _`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
|
||||
*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
|
||||
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
|
||||
|
||||
@@ -57,16 +57,16 @@ clean, well written patch would likely be accepted to solve them.
|
||||
Python 3.9.0 warning
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Combining older versions of pybind11 (< 2.6.0) with Python on 3.9.0 will
|
||||
trigger undefined behavior that typically manifests as crashes during
|
||||
Combining older versions of pybind11 (< 2.6.0) with Python on exactly 3.9.0
|
||||
will trigger undefined behavior that typically manifests as crashes during
|
||||
interpreter shutdown (but could also destroy your data. **You have been
|
||||
warned**).
|
||||
|
||||
This issue has been
|
||||
`fixed in Python <https://github.com/python/cpython/pull/22670>`_. As a
|
||||
mitigation until 3.9.1 is released and commonly used, pybind11 (2.6.0 or newer)
|
||||
includes a temporary workaround specifically when Python 3.9.0 is detected at
|
||||
runtime, leaking about 50 bytes of memory when a callback function is garbage
|
||||
collected. For reference; the pybind11 test suite has about 2,000 such
|
||||
callbacks, but only 49 are garbage collected before the end-of-process. Wheels
|
||||
built with Python 3.9.0 will correctly avoid the leak when run in Python 3.9.1.
|
||||
This issue was `fixed in Python <https://github.com/python/cpython/pull/22670>`_.
|
||||
As a mitigation for this bug, pybind11 2.6.0 or newer includes a workaround
|
||||
specifically when Python 3.9.0 is detected at runtime, leaking about 50 bytes
|
||||
of memory when a callback function is garbage collected. For reference, the
|
||||
pybind11 test suite has about 2,000 such callbacks, but only 49 are garbage
|
||||
collected before the end-of-process. Wheels (even if built with Python 3.9.0)
|
||||
will correctly avoid the leak when run in Python 3.9.1, and this does not
|
||||
affect other 3.X versions.
|
||||
|
||||
@@ -52,6 +52,20 @@ Convenience classes for specific Python types
|
||||
.. doxygengroup:: pytypes
|
||||
:members:
|
||||
|
||||
Convenience functions converting to Python types
|
||||
================================================
|
||||
|
||||
.. doxygenfunction:: make_tuple(Args&&...)
|
||||
|
||||
.. doxygenfunction:: make_iterator(Iterator, Sentinel, Extra &&...)
|
||||
.. doxygenfunction:: make_iterator(Type &, Extra&&...)
|
||||
|
||||
.. doxygenfunction:: make_key_iterator(Iterator, Sentinel, Extra &&...)
|
||||
.. doxygenfunction:: make_key_iterator(Type &, Extra&&...)
|
||||
|
||||
.. doxygenfunction:: make_value_iterator(Iterator, Sentinel, Extra &&...)
|
||||
.. doxygenfunction:: make_value_iterator(Type &, Extra&&...)
|
||||
|
||||
.. _extras:
|
||||
|
||||
Passing extra arguments to ``def`` or ``class_``
|
||||
@@ -110,7 +124,6 @@ Exceptions
|
||||
.. doxygenclass:: builtin_exception
|
||||
:members:
|
||||
|
||||
|
||||
Literals
|
||||
========
|
||||
|
||||
|
||||
@@ -15,31 +15,34 @@ For example:
|
||||
|
||||
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
|
||||
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:
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- Update the version number
|
||||
- Update ``PYBIND11_VERSION_MAJOR`` etc. in
|
||||
``include/pybind11/detail/common.h``. PATCH should be a simple integer.
|
||||
- Update ``pybind11/_version.py`` (match above)
|
||||
- Ensure that all the information in ``setup.cfg`` is up-to-date, like
|
||||
supported Python versions.
|
||||
- Add release date in ``docs/changelog.rst``.
|
||||
- Check to make sure
|
||||
`needs-changelog <https://github.com/pybind/pybind11/pulls?q=is%3Apr+is%3Aclosed+label%3A%22needs+changelog%22>`_
|
||||
issues are entered in the changelog (clear the label when done).
|
||||
- ``git add`` and ``git commit``, ``git push``. **Ensure CI passes**. (If it
|
||||
fails due to a known flake issue, either ignore or restart CI.)
|
||||
- Update ``PYBIND11_VERSION_MAJOR`` etc. in
|
||||
``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)
|
||||
- Run ``nox -s tests_packaging`` to ensure this was done correctly.
|
||||
- Ensure that all the information in ``setup.cfg`` is up-to-date, like
|
||||
supported Python versions.
|
||||
- Add release date in ``docs/changelog.rst``.
|
||||
- Check to make sure
|
||||
`needs-changelog <https://github.com/pybind/pybind11/pulls?q=is%3Apr+is%3Aclosed+label%3A%22needs+changelog%22>`_
|
||||
issues are entered in the changelog (clear the label when done).
|
||||
- ``git add`` and ``git commit``, ``git push``. **Ensure CI passes**. (If it
|
||||
fails due to a known flake issue, either ignore or restart CI.)
|
||||
- Add a release branch if this is a new minor version, or update the existing release branch if it is a patch version
|
||||
- New branch: ``git checkout -b vX.Y``, ``git push -u origin vX.Y``
|
||||
- Update branch: ``git checkout vX.Y``, ``git merge <release branch>``, ``git push``
|
||||
- New branch: ``git checkout -b vX.Y``, ``git push -u origin vX.Y``
|
||||
- Update branch: ``git checkout vX.Y``, ``git merge <release branch>``, ``git push``
|
||||
- Update tags (optional; if you skip this, the GitHub release makes a
|
||||
non-annotated tag for you)
|
||||
- ``git tag -a vX.Y.Z -m 'vX.Y.Z release'``.
|
||||
- ``git push --tags``.
|
||||
non-annotated tag for you)
|
||||
- ``git tag -a vX.Y.Z -m 'vX.Y.Z release'``.
|
||||
- ``git push --tags``.
|
||||
- Update stable
|
||||
- ``git checkout stable``
|
||||
- ``git merge master``
|
||||
@@ -47,29 +50,32 @@ To release a new version of pybind11:
|
||||
- Make a GitHub release (this shows up in the UI, sends new release
|
||||
notifications to users watching releases, and also uploads PyPI packages).
|
||||
(Note: if you do not use an existing tag, this creates a new lightweight tag
|
||||
for you, so you could skip the above step).
|
||||
- GUI method: click "Create a new release" on the far right, fill in the tag
|
||||
name (if you didn't tag above, it will be made here), fill in a release
|
||||
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
|
||||
this is a beta/RC. You can get partway there with
|
||||
``cat docs/changelog.rst | pandsoc -f rst -t markdown``.
|
||||
- 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``.
|
||||
for you, so you could skip the above step.)
|
||||
|
||||
- GUI method: Under `releases <https://github.com/pybind/pybind11/releases>`_
|
||||
click "Draft a new release" on the far right, fill in the tag name
|
||||
(if you didn't tag above, it will be made here), fill in a release name
|
||||
like "Version X.Y.Z", and copy-and-paste the markdown-formatted (!) changelog
|
||||
into the description (usually ``cat docs/changelog.rst | pandoc -f rst -t gfm``).
|
||||
Check "pre-release" if this is a beta/RC.
|
||||
|
||||
- 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``.
|
||||
|
||||
- Get back to work
|
||||
- Make sure you are on master, not somewhere else: ``git checkout master``
|
||||
- Update version macros in ``include/pybind11/detail/common.h`` (set PATCH to
|
||||
``0.dev1`` and increment MINOR).
|
||||
- Update ``_version.py`` to match
|
||||
- Add a spot for in-development updates in ``docs/changelog.rst``.
|
||||
- ``git add``, ``git commit``, ``git push``
|
||||
- Make sure you are on master, not somewhere else: ``git checkout master``
|
||||
- Update version macros in ``include/pybind11/detail/common.h`` (set PATCH to
|
||||
``0.dev1`` and increment MINOR).
|
||||
- Update ``_version.py`` to match
|
||||
- Run ``nox -s tests_packaging`` to ensure this was done correctly.
|
||||
- Add a spot for in-development updates in ``docs/changelog.rst``.
|
||||
- ``git add``, ``git commit``, ``git push``
|
||||
|
||||
If a version branch is updated, remember to set PATCH to ``1.dev1``.
|
||||
|
||||
If you'd like to bump homebrew, run:
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: console
|
||||
|
||||
brew bump-formula-pr --url https://github.com/pybind/pybind11/archive/vX.Y.Z.tar.gz
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
breathe==4.25.1
|
||||
commonmark==0.9.1
|
||||
recommonmark==0.7.1
|
||||
breathe==4.26.1
|
||||
# docutils 0.17 breaks HTML tags & RTD theme
|
||||
# https://github.com/sphinx-doc/sphinx/issues/9001
|
||||
docutils==0.16
|
||||
sphinx==3.3.1
|
||||
sphinx_rtd_theme==0.5.0
|
||||
sphinxcontrib-moderncmakedomain==3.17
|
||||
|
||||
@@ -8,6 +8,42 @@ to a new version. But it goes into more detail. This includes things like
|
||||
deprecated APIs and their replacements, build system changes, general code
|
||||
modernization and other useful information.
|
||||
|
||||
.. _upgrade-guide-2.9:
|
||||
|
||||
v2.9
|
||||
====
|
||||
|
||||
* Any usage of the recently added ``py::make_simple_namespace`` should be
|
||||
converted to using ``py::module_::import("types").attr("SimpleNamespace")``
|
||||
instead.
|
||||
|
||||
|
||||
.. _upgrade-guide-2.7:
|
||||
|
||||
v2.7
|
||||
====
|
||||
|
||||
*Before* v2.7, ``py::str`` can hold ``PyUnicodeObject`` or ``PyBytesObject``,
|
||||
and ``py::isinstance<str>()`` is ``true`` for both ``py::str`` and
|
||||
``py::bytes``. Starting with v2.7, ``py::str`` exclusively holds
|
||||
``PyUnicodeObject`` (`#2409 <https://github.com/pybind/pybind11/pull/2409>`_),
|
||||
and ``py::isinstance<str>()`` is ``true`` only for ``py::str``. To help in
|
||||
the transition of user code, the ``PYBIND11_STR_LEGACY_PERMISSIVE`` macro
|
||||
is provided as an escape hatch to go back to the legacy behavior. This macro
|
||||
will be removed in future releases. Two types of required fixes are expected
|
||||
to be common:
|
||||
|
||||
* Accidental use of ``py::str`` instead of ``py::bytes``, masked by the legacy
|
||||
behavior. These are probably very easy to fix, by changing from
|
||||
``py::str`` to ``py::bytes``.
|
||||
|
||||
* Reliance on py::isinstance<str>(obj) being ``true`` for
|
||||
``py::bytes``. This is likely to be easy to fix in most cases by adding
|
||||
``|| py::isinstance<bytes>(obj)``, but a fix may be more involved, e.g. if
|
||||
``py::isinstance<T>`` appears in a template. Such situations will require
|
||||
careful review and custom fixes.
|
||||
|
||||
|
||||
.. _upgrade-guide-2.6:
|
||||
|
||||
v2.6
|
||||
@@ -256,7 +292,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
|
||||
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
|
||||
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
|
||||
were previously relying on default visibility, i.e. if your Python module was
|
||||
|
||||
@@ -12,13 +12,17 @@
|
||||
|
||||
#include "cast.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
/// \addtogroup annotations
|
||||
/// @{
|
||||
|
||||
/// Annotation for methods
|
||||
struct is_method { handle class_; is_method(const handle &c) : class_(c) { } };
|
||||
struct is_method { handle class_;
|
||||
explicit is_method(const handle &c) : class_(c) {}
|
||||
};
|
||||
|
||||
/// Annotation for operators
|
||||
struct is_operator { };
|
||||
@@ -27,16 +31,24 @@ struct is_operator { };
|
||||
struct is_final { };
|
||||
|
||||
/// Annotation for parent scope
|
||||
struct scope { handle value; scope(const handle &s) : value(s) { } };
|
||||
struct scope { handle value;
|
||||
explicit scope(const handle &s) : value(s) {}
|
||||
};
|
||||
|
||||
/// Annotation for documentation
|
||||
struct doc { const char *value; doc(const char *value) : value(value) { } };
|
||||
struct doc { const char *value;
|
||||
explicit doc(const char *value) : value(value) {}
|
||||
};
|
||||
|
||||
/// Annotation for function names
|
||||
struct name { const char *value; name(const char *value) : value(value) { } };
|
||||
struct name { const char *value;
|
||||
explicit name(const char *value) : value(value) {}
|
||||
};
|
||||
|
||||
/// Annotation indicating that a function is an overload associated with a given "sibling"
|
||||
struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } };
|
||||
struct sibling { handle value;
|
||||
explicit sibling(const handle &value) : value(value.ptr()) {}
|
||||
};
|
||||
|
||||
/// Annotation indicating that a class derives from another given type
|
||||
template <typename T> struct base {
|
||||
@@ -62,14 +74,34 @@ struct metaclass {
|
||||
handle value;
|
||||
|
||||
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
|
||||
explicit metaclass(handle value) : value(value) { }
|
||||
};
|
||||
|
||||
/// Specifies a custom callback with signature `void (PyHeapTypeObject*)` that
|
||||
/// may be used to customize the Python type.
|
||||
///
|
||||
/// The callback is invoked immediately before `PyType_Ready`.
|
||||
///
|
||||
/// Note: This is an advanced interface, and uses of it may require changes to
|
||||
/// work with later versions of pybind11. You may wish to consult the
|
||||
/// implementation of `make_new_python_type` in `detail/classes.h` to understand
|
||||
/// the context in which the callback will be run.
|
||||
struct custom_type_setup {
|
||||
using callback = std::function<void(PyHeapTypeObject *heap_type)>;
|
||||
|
||||
explicit custom_type_setup(callback value) : value(std::move(value)) {}
|
||||
|
||||
callback value;
|
||||
};
|
||||
|
||||
/// Annotation that marks a class as local to the module:
|
||||
struct module_local { const bool value; constexpr module_local(bool v = true) : value(v) { } };
|
||||
struct module_local { const bool value;
|
||||
constexpr explicit module_local(bool v = true) : value(v) {}
|
||||
};
|
||||
|
||||
/// Annotation to mark enums as an arithmetic type
|
||||
struct arithmetic { };
|
||||
@@ -123,7 +155,7 @@ enum op_id : int;
|
||||
enum op_type : int;
|
||||
struct undefined_t;
|
||||
template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t> struct op_;
|
||||
inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret);
|
||||
void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret);
|
||||
|
||||
/// Internal data structure which holds metadata about a keyword argument
|
||||
struct argument_record {
|
||||
@@ -259,6 +291,9 @@ struct type_record {
|
||||
/// Custom metaclass (optional)
|
||||
handle metaclass;
|
||||
|
||||
/// Custom type setup.
|
||||
custom_type_setup::callback custom_type_setup_callback;
|
||||
|
||||
/// Multiple inheritance marker
|
||||
bool multiple_inheritance : 1;
|
||||
|
||||
@@ -377,7 +412,7 @@ template <> struct process_attribute<is_new_style_constructor> : process_attribu
|
||||
};
|
||||
|
||||
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");
|
||||
++r->nargs_kw_only;
|
||||
}
|
||||
@@ -463,6 +498,13 @@ struct process_attribute<dynamic_attr> : process_attribute_default<dynamic_attr>
|
||||
static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct process_attribute<custom_type_setup> {
|
||||
static void init(const custom_type_setup &value, type_record *r) {
|
||||
r->custom_type_setup_callback = value.value;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct process_attribute<is_final> : process_attribute_default<is_final> {
|
||||
static void init(const is_final &, type_record *r) { r->is_final = true; }
|
||||
@@ -515,20 +557,31 @@ template <size_t Nurse, size_t Patient> struct process_attribute<keep_alive<Nurs
|
||||
/// Recursively iterate over variadic template arguments
|
||||
template <typename... Args> struct process_attributes {
|
||||
static void init(const Args&... args, function_record *r) {
|
||||
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::init(args, r), 0) ... };
|
||||
ignore_unused(unused);
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r);
|
||||
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r);
|
||||
using expander = int[];
|
||||
(void) expander{
|
||||
0, ((void) process_attribute<typename std::decay<Args>::type>::init(args, r), 0)...};
|
||||
}
|
||||
static void init(const Args&... args, type_record *r) {
|
||||
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::init(args, r), 0) ... };
|
||||
ignore_unused(unused);
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r);
|
||||
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r);
|
||||
using expander = int[];
|
||||
(void) expander{0,
|
||||
(process_attribute<typename std::decay<Args>::type>::init(args, r), 0)...};
|
||||
}
|
||||
static void precall(function_call &call) {
|
||||
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::precall(call), 0) ... };
|
||||
ignore_unused(unused);
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call);
|
||||
using expander = int[];
|
||||
(void) expander{0,
|
||||
(process_attribute<typename std::decay<Args>::type>::precall(call), 0)...};
|
||||
}
|
||||
static void postcall(function_call &call, handle fn_ret) {
|
||||
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::postcall(call, fn_ret), 0) ... };
|
||||
ignore_unused(unused);
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call, fn_ret);
|
||||
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(fn_ret);
|
||||
using expander = int[];
|
||||
(void) expander{
|
||||
0, (process_attribute<typename std::decay<Args>::type>::postcall(call, fn_ret), 0)...};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -544,6 +597,7 @@ template <typename... Extra,
|
||||
size_t named = constexpr_sum(std::is_base_of<arg, Extra>::value...),
|
||||
size_t self = constexpr_sum(std::is_same<is_method, Extra>::value...)>
|
||||
constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, has_kwargs);
|
||||
return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ struct buffer_info {
|
||||
view->strides
|
||||
? std::vector<ssize_t>(view->strides, view->strides + view->ndim)
|
||||
: detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize),
|
||||
view->readonly) {
|
||||
(view->readonly != 0)) {
|
||||
this->m_view = view;
|
||||
this->ownview = ownview;
|
||||
}
|
||||
@@ -91,11 +91,9 @@ struct buffer_info {
|
||||
buffer_info(const buffer_info &) = delete;
|
||||
buffer_info& operator=(const buffer_info &) = delete;
|
||||
|
||||
buffer_info(buffer_info &&other) {
|
||||
(*this) = std::move(other);
|
||||
}
|
||||
buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); }
|
||||
|
||||
buffer_info& operator=(buffer_info &&rhs) {
|
||||
buffer_info &operator=(buffer_info &&rhs) noexcept {
|
||||
ptr = rhs.ptr;
|
||||
itemsize = rhs.itemsize;
|
||||
size = rhs.size;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,9 +11,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "pybind11.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <datetime.h>
|
||||
|
||||
// Backport the PyDateTime_DELTA functions from Python3.3 if required
|
||||
@@ -35,7 +40,7 @@ public:
|
||||
using rep = typename type::rep;
|
||||
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) {
|
||||
using namespace std::chrono;
|
||||
@@ -53,11 +58,11 @@ public:
|
||||
return true;
|
||||
}
|
||||
// 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()))));
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If this is a duration just return it back
|
||||
@@ -95,6 +100,22 @@ public:
|
||||
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
|
||||
template <typename Duration> class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> {
|
||||
public:
|
||||
@@ -161,10 +182,11 @@ public:
|
||||
// > If std::time_t has lower precision, it is implementation-defined whether the value is rounded or truncated.
|
||||
// (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));
|
||||
// this function uses static memory so it's best to copy it out asap just in case
|
||||
// otherwise other code that is using localtime may break this (not just python code)
|
||||
std::tm localtime = *std::localtime(&tt);
|
||||
|
||||
std::tm localtime;
|
||||
std::tm *localtime_ptr = localtime_thread_safe(&tt, &localtime);
|
||||
if (!localtime_ptr)
|
||||
throw cast_error("Unable to represent system_clock in local time");
|
||||
return PyDateTime_FromDateAndTime(localtime.tm_year + 1900,
|
||||
localtime.tm_mon + 1,
|
||||
localtime.tm_mday,
|
||||
|
||||
@@ -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`
|
||||
// 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment
|
||||
const auto static_prop = (PyObject *) get_internals().static_property_type;
|
||||
const auto call_descr_set = descr && value && PyObject_IsInstance(descr, static_prop)
|
||||
&& !PyObject_IsInstance(value, static_prop);
|
||||
const auto call_descr_set = (descr != nullptr) && (value != nullptr)
|
||||
&& (PyObject_IsInstance(descr, static_prop) != 0)
|
||||
&& (PyObject_IsInstance(value, static_prop) == 0);
|
||||
if (call_descr_set) {
|
||||
// Call `static_property.__set__()` instead of replacing the `static_property`.
|
||||
#if !defined(PYPY_VERSION)
|
||||
@@ -162,9 +163,7 @@ extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name
|
||||
Py_INCREF(descr);
|
||||
return descr;
|
||||
}
|
||||
else {
|
||||
return PyType_Type.tp_getattro(obj, name);
|
||||
}
|
||||
return PyType_Type.tp_getattro(obj, name);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -211,7 +210,7 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
|
||||
internals.direct_conversions.erase(tindex);
|
||||
|
||||
if (tinfo->module_local)
|
||||
registered_local_types_cpp().erase(tindex);
|
||||
get_local_internals().registered_types_cpp.erase(tindex);
|
||||
else
|
||||
internals.registered_types_cpp.erase(tindex);
|
||||
internals.registered_types_py.erase(tinfo->type);
|
||||
@@ -329,7 +328,7 @@ inline bool deregister_instance(instance *self, void *valptr, const type_info *t
|
||||
inline PyObject *make_new_instance(PyTypeObject *type) {
|
||||
#if defined(PYPY_VERSION)
|
||||
// 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));
|
||||
if (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;
|
||||
for (auto s : info->shape)
|
||||
view->len *= s;
|
||||
view->readonly = info->readonly;
|
||||
view->readonly = static_cast<int>(info->readonly);
|
||||
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
|
||||
view->format = const_cast<char *>(info->format.c_str());
|
||||
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
|
||||
@@ -684,11 +683,13 @@ inline PyObject* make_new_python_type(const type_record &rec) {
|
||||
if (rec.buffer_protocol)
|
||||
enable_buffer_protocol(heap_type);
|
||||
|
||||
if (rec.custom_type_setup_callback)
|
||||
rec.custom_type_setup_callback(heap_type);
|
||||
|
||||
if (PyType_Ready(type) < 0)
|
||||
pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!");
|
||||
|
||||
assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)
|
||||
: !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
|
||||
assert(!rec.dynamic_attr || PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
|
||||
|
||||
/* Register type with the parent scope */
|
||||
if (rec.scope)
|
||||
|
||||
@@ -10,8 +10,12 @@
|
||||
#pragma once
|
||||
|
||||
#define PYBIND11_VERSION_MAJOR 2
|
||||
#define PYBIND11_VERSION_MINOR 6
|
||||
#define PYBIND11_VERSION_PATCH 2
|
||||
#define PYBIND11_VERSION_MINOR 8
|
||||
#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 0x02080100
|
||||
|
||||
#define PYBIND11_NAMESPACE_BEGIN(name) namespace name {
|
||||
#define PYBIND11_NAMESPACE_END(name) }
|
||||
@@ -52,6 +56,9 @@
|
||||
# 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.
|
||||
# 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__)
|
||||
# if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3)
|
||||
# error pybind11 requires clang 3.3 or newer
|
||||
@@ -82,13 +89,43 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define PYBIND11_NOINLINE __declspec(noinline)
|
||||
#else
|
||||
# define PYBIND11_NOINLINE __attribute__ ((noinline))
|
||||
#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(PYBIND11_CPP14)
|
||||
// For CUDA, GCC7, GCC8:
|
||||
// PYBIND11_NOINLINE_FORCED is incompatible with `-Wattributes -Werror`.
|
||||
// When defining PYBIND11_NOINLINE_FORCED, it is best to also use `-Wno-attributes`.
|
||||
// However, the measured shared-library size saving when using noinline are only
|
||||
// 1.7% for CUDA, -0.2% for GCC7, and 0.0% for GCC8 (using -DCMAKE_BUILD_TYPE=MinSizeRel,
|
||||
// the default under pybind11/tests).
|
||||
#if !defined(PYBIND11_NOINLINE_FORCED) && \
|
||||
(defined(__CUDACC__) || (defined(__GNUC__) && (__GNUC__ == 7 || __GNUC__ == 8)))
|
||||
# define PYBIND11_NOINLINE_DISABLED
|
||||
#endif
|
||||
|
||||
// The PYBIND11_NOINLINE macro is for function DEFINITIONS.
|
||||
// In contrast, FORWARD DECLARATIONS should never use this macro:
|
||||
// https://stackoverflow.com/questions/9317473/forward-declaration-of-inline-functions
|
||||
#if defined(PYBIND11_NOINLINE_DISABLED) // Option for maximum portability and experimentation.
|
||||
# define PYBIND11_NOINLINE inline
|
||||
#elif defined(_MSC_VER)
|
||||
# define PYBIND11_NOINLINE __declspec(noinline) inline
|
||||
#else
|
||||
# define PYBIND11_NOINLINE __attribute__ ((noinline)) inline
|
||||
#endif
|
||||
|
||||
#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)]]
|
||||
#else
|
||||
# define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason)))
|
||||
@@ -114,13 +151,38 @@
|
||||
# define HAVE_ROUND 1
|
||||
# endif
|
||||
# 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
|
||||
|
||||
#ifdef __has_include
|
||||
// std::optional (but including it in c++14 mode isn't allowed)
|
||||
# if defined(PYBIND11_CPP17) && __has_include(<optional>)
|
||||
# define PYBIND11_HAS_OPTIONAL 1
|
||||
# endif
|
||||
// std::experimental::optional (but not allowed in c++11 mode)
|
||||
# if defined(PYBIND11_CPP14) && (__has_include(<experimental/optional>) && \
|
||||
!__has_include(<optional>))
|
||||
# define PYBIND11_HAS_EXP_OPTIONAL 1
|
||||
# endif
|
||||
// std::variant
|
||||
# if defined(PYBIND11_CPP17) && __has_include(<variant>)
|
||||
# define PYBIND11_HAS_VARIANT 1
|
||||
# endif
|
||||
#elif defined(_MSC_VER) && defined(PYBIND11_CPP17)
|
||||
# define PYBIND11_HAS_OPTIONAL 1
|
||||
# define PYBIND11_HAS_VARIANT 1
|
||||
#endif
|
||||
|
||||
#include <Python.h>
|
||||
#include <frameobject.h>
|
||||
#include <pythread.h>
|
||||
@@ -162,6 +224,24 @@
|
||||
#include <memory>
|
||||
#include <typeindex>
|
||||
#include <type_traits>
|
||||
#if defined(__has_include)
|
||||
# if __has_include(<version>)
|
||||
# include <version>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// #define PYBIND11_STR_LEGACY_PERMISSIVE
|
||||
// If DEFINED, pybind11::str can hold PyUnicodeObject or PyBytesObject
|
||||
// (probably surprising and never documented, but this was the
|
||||
// legacy behavior until and including v2.6.x). As a side-effect,
|
||||
// pybind11::isinstance<str>() is true for both pybind11::str and
|
||||
// pybind11::bytes.
|
||||
// If UNDEFINED, pybind11::str can only hold PyUnicodeObject, and
|
||||
// pybind11::isinstance<str>() is true only for pybind11::str.
|
||||
// However, for Python 2 only (!), the pybind11::str caster
|
||||
// implicitly decodes bytes to PyUnicodeObject. This is to ease
|
||||
// the transition from the legacy behavior to the non-permissive
|
||||
// behavior.
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions
|
||||
#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr)
|
||||
@@ -175,8 +255,8 @@
|
||||
#define PYBIND11_BYTES_SIZE PyBytes_Size
|
||||
#define PYBIND11_LONG_CHECK(o) PyLong_Check(o)
|
||||
#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o)
|
||||
#define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) o)
|
||||
#define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) o)
|
||||
#define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) (o))
|
||||
#define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) (o))
|
||||
#define PYBIND11_BYTES_NAME "bytes"
|
||||
#define PYBIND11_STRING_NAME "str"
|
||||
#define PYBIND11_SLICE_OBJECT PyObject
|
||||
@@ -254,6 +334,19 @@ extern "C" {
|
||||
} \
|
||||
}
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
|
||||
#define PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
catch (pybind11::error_already_set &e) { \
|
||||
pybind11::raise_from(e, PyExc_ImportError, "initialization failed"); \
|
||||
return nullptr; \
|
||||
} catch (const std::exception &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
} \
|
||||
|
||||
#else
|
||||
|
||||
#define PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
catch (pybind11::error_already_set &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
@@ -263,6 +356,8 @@ extern "C" {
|
||||
return nullptr; \
|
||||
} \
|
||||
|
||||
#endif
|
||||
|
||||
/** \rst
|
||||
***Deprecated in favor of PYBIND11_MODULE***
|
||||
|
||||
@@ -311,30 +406,35 @@ extern "C" {
|
||||
});
|
||||
}
|
||||
\endrst */
|
||||
#define PYBIND11_MODULE(name, variable) \
|
||||
static ::pybind11::module_::module_def \
|
||||
PYBIND11_CONCAT(pybind11_module_def_, name) PYBIND11_MAYBE_UNUSED; \
|
||||
PYBIND11_MAYBE_UNUSED \
|
||||
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
|
||||
PYBIND11_PLUGIN_IMPL(name) { \
|
||||
PYBIND11_CHECK_PYTHON_VERSION \
|
||||
PYBIND11_ENSURE_INTERNALS_READY \
|
||||
auto m = ::pybind11::module_::create_extension_module( \
|
||||
PYBIND11_TOSTRING(name), nullptr, \
|
||||
&PYBIND11_CONCAT(pybind11_module_def_, name)); \
|
||||
try { \
|
||||
PYBIND11_CONCAT(pybind11_init_, name)(m); \
|
||||
return m.ptr(); \
|
||||
} PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
} \
|
||||
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &variable)
|
||||
|
||||
#define PYBIND11_MODULE(name, variable) \
|
||||
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name) \
|
||||
PYBIND11_MAYBE_UNUSED; \
|
||||
PYBIND11_MAYBE_UNUSED \
|
||||
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
|
||||
PYBIND11_PLUGIN_IMPL(name) { \
|
||||
PYBIND11_CHECK_PYTHON_VERSION \
|
||||
PYBIND11_ENSURE_INTERNALS_READY \
|
||||
auto m = ::pybind11::module_::create_extension_module( \
|
||||
PYBIND11_TOSTRING(name), nullptr, &PYBIND11_CONCAT(pybind11_module_def_, name)); \
|
||||
try { \
|
||||
PYBIND11_CONCAT(pybind11_init_, name)(m); \
|
||||
return m.ptr(); \
|
||||
} \
|
||||
PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
} \
|
||||
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ & (variable))
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
using ssize_t = Py_ssize_t;
|
||||
using size_t = std::size_t;
|
||||
|
||||
template <typename IntType>
|
||||
inline ssize_t ssize_t_cast(const IntType &val) {
|
||||
static_assert(sizeof(IntType) <= sizeof(ssize_t), "Implicit narrowing is not permitted.");
|
||||
return static_cast<ssize_t>(val);
|
||||
}
|
||||
|
||||
/// Approach used to cast a previously unknown C++ instance into a Python object
|
||||
enum class return_value_policy : uint8_t {
|
||||
/** This is the default return value policy, which falls back to the policy
|
||||
@@ -690,9 +790,6 @@ using function_signature_t = conditional_t<
|
||||
template <typename T> using is_lambda = satisfies_none_of<remove_reference_t<T>,
|
||||
std::is_function, std::is_pointer, std::is_member_pointer>;
|
||||
|
||||
/// Ignore that a variable is unused in compiler warnings
|
||||
inline void ignore_unused(const int *) { }
|
||||
|
||||
// [workaround(intel)] Internal error on fold expression
|
||||
/// Apply a function over each element of a parameter pack
|
||||
#if defined(__cpp_fold_expressions) && !defined(__INTEL_COMPILER)
|
||||
@@ -705,16 +802,23 @@ using expand_side_effects = bool[];
|
||||
|
||||
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
|
||||
class builtin_exception : public std::runtime_error {
|
||||
class PYBIND11_EXPORT_EXCEPTION builtin_exception : public std::runtime_error {
|
||||
public:
|
||||
using std::runtime_error::runtime_error;
|
||||
/// Set the error using the Python C API
|
||||
virtual void set_error() const = 0;
|
||||
};
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#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; \
|
||||
name() : name("") { } \
|
||||
void set_error() const override { PyErr_SetString(type, what()); } \
|
||||
@@ -727,11 +831,12 @@ PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(buffer_error, PyExc_BufferError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(import_error, PyExc_ImportError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(attribute_error, PyExc_AttributeError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error
|
||||
PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally
|
||||
|
||||
[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const char *reason) { throw std::runtime_error(reason); }
|
||||
[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); }
|
||||
[[noreturn]] PYBIND11_NOINLINE void pybind11_fail(const char *reason) { throw std::runtime_error(reason); }
|
||||
[[noreturn]] PYBIND11_NOINLINE void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); }
|
||||
|
||||
template <typename T, typename SFINAE = void> struct format_descriptor { };
|
||||
|
||||
@@ -776,7 +881,8 @@ struct nodelete { template <typename T> void operator()(T*) { } };
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
template <typename... Args>
|
||||
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>
|
||||
constexpr auto operator()(Return (*pf)(Args...)) const noexcept
|
||||
@@ -832,6 +938,7 @@ public:
|
||||
|
||||
// Implicit conversion constructor from any arbitrary container type with values convertible to T
|
||||
template <typename Container, typename = enable_if_t<std::is_convertible<decltype(*std::begin(std::declval<const Container &>())), T>::value>>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
any_container(const Container &c) : any_container(std::begin(c), std::end(c)) { }
|
||||
|
||||
// initializer_list's aren't deducible, so don't get matched by the above template; we need this
|
||||
@@ -840,9 +947,11 @@ public:
|
||||
any_container(const std::initializer_list<TIn> &c) : any_container(c.begin(), c.end()) { }
|
||||
|
||||
// Avoid copying if given an rvalue vector of the correct type.
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
any_container(std::vector<T> &&v) : v(std::move(v)) { }
|
||||
|
||||
// Moves the vector out of an rvalue any_container
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator std::vector<T> &&() && { return std::move(v); }
|
||||
|
||||
// Dereferencing obtains a reference to the underlying vector
|
||||
@@ -857,5 +966,57 @@ public:
|
||||
// Forward-declaration; see detail/class.h
|
||||
std::string get_fully_qualified_tp_name(PyTypeObject*);
|
||||
|
||||
template <typename T>
|
||||
inline static std::shared_ptr<T> try_get_shared_from_this(std::enable_shared_from_this<T> *holder_value_ptr) {
|
||||
// Pre C++17, this code path exploits undefined behavior, but is known to work on many platforms.
|
||||
// Use at your own risk!
|
||||
// See also https://en.cppreference.com/w/cpp/memory/enable_shared_from_this, and in particular
|
||||
// the `std::shared_ptr<Good> gp1 = not_so_good.getptr();` and `try`-`catch` parts of the example.
|
||||
#if defined(__cpp_lib_enable_shared_from_this) && (!defined(_MSC_VER) || _MSC_VER >= 1912)
|
||||
return holder_value_ptr->weak_from_this().lock();
|
||||
#else
|
||||
try {
|
||||
return holder_value_ptr->shared_from_this();
|
||||
}
|
||||
catch (const std::bad_weak_ptr &) {
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// For silencing "unused" compiler warnings in special situations.
|
||||
template <typename... Args>
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER < 1920 // MSVC 2017
|
||||
constexpr
|
||||
#endif
|
||||
inline void silence_unused_warnings(Args &&...) {}
|
||||
|
||||
// MSVC warning C4100: Unreferenced formal parameter
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1916
|
||||
# define PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(...) \
|
||||
detail::silence_unused_warnings(__VA_ARGS__)
|
||||
#else
|
||||
# define PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(...)
|
||||
#endif
|
||||
|
||||
// GCC -Wunused-but-set-parameter All GCC versions (as of July 2021).
|
||||
#if defined(__GNUG__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
|
||||
# define PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(...) \
|
||||
detail::silence_unused_warnings(__VA_ARGS__)
|
||||
#else
|
||||
# define PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(...)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) // All versions (as of July 2021).
|
||||
|
||||
// warning C4127: Conditional expression is constant
|
||||
constexpr inline bool silence_msvc_c4127(bool cond) { return cond; }
|
||||
|
||||
# define PYBIND11_SILENCE_MSVC_C4127(...) ::pybind11::detail::silence_msvc_c4127(__VA_ARGS__)
|
||||
|
||||
#else
|
||||
# define PYBIND11_SILENCE_MSVC_C4127(...) __VA_ARGS__
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
@@ -23,15 +23,17 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
/* Concatenate type signatures at compile time */
|
||||
template <size_t N, typename... Ts>
|
||||
struct descr {
|
||||
char text[N + 1];
|
||||
char text[N + 1]{'\0'};
|
||||
|
||||
constexpr descr() : text{'\0'} { }
|
||||
constexpr descr() = default;
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence<N>()) { }
|
||||
|
||||
template <size_t... Is>
|
||||
constexpr descr(char const (&s)[N+1], index_sequence<Is...>) : text{s[Is]..., '\0'} { }
|
||||
|
||||
template <typename... Chars>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr descr(char c, Chars... cs) : text{c, static_cast<char>(cs)..., '\0'} { }
|
||||
|
||||
static constexpr std::array<const std::type_info *, sizeof...(Ts) + 1> types() {
|
||||
@@ -42,6 +44,7 @@ struct descr {
|
||||
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2, size_t... Is1, size_t... Is2>
|
||||
constexpr descr<N1 + N2, Ts1..., Ts2...> plus_impl(const descr<N1, Ts1...> &a, const descr<N2, Ts2...> &b,
|
||||
index_sequence<Is1...>, index_sequence<Is2...>) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(b);
|
||||
return {a.text[Is1]..., b.text[Is2]...};
|
||||
}
|
||||
|
||||
@@ -74,7 +77,8 @@ constexpr enable_if_t<B, T1> _(const T1 &d, const T2 &) { return d; }
|
||||
template <bool B, typename T1, typename T2>
|
||||
constexpr enable_if_t<!B, T2> _(const T1 &, const T2 &d) { return d; }
|
||||
|
||||
template <size_t Size> auto constexpr _() -> decltype(int_to_str<Size / 10, Size % 10>::digits) {
|
||||
template <size_t Size>
|
||||
auto constexpr _() -> remove_cv_t<decltype(int_to_str<Size / 10, Size % 10>::digits)> {
|
||||
return int_to_str<Size / 10, Size % 10>::digits;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
}
|
||||
|
||||
template <typename> using cast_op_type = value_and_holder &;
|
||||
operator value_and_holder &() { return *value; }
|
||||
explicit operator value_and_holder &() { return *value; }
|
||||
static constexpr auto name = _<value_and_holder>();
|
||||
|
||||
private:
|
||||
@@ -94,8 +94,9 @@ void construct(...) {
|
||||
// construct an Alias from the returned base instance.
|
||||
template <typename Class>
|
||||
void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||
no_nullptr(ptr);
|
||||
if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) {
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias && !is_alias<Class>(ptr)) {
|
||||
// We're going to try to construct an alias by moving the cpp type. Whether or not
|
||||
// that succeeds, we still need to destroy the original cpp pointer (either the
|
||||
// moved away leftover, if the alias construction works, or the value itself if we
|
||||
@@ -131,10 +132,11 @@ void construct(value_and_holder &v_h, Alias<Class> *alias_ptr, bool) {
|
||||
// derived type (through those holder's implicit conversion from derived class holder constructors).
|
||||
template <typename Class>
|
||||
void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||
auto *ptr = holder_helper<Holder<Class>>::get(holder);
|
||||
no_nullptr(ptr);
|
||||
// If we need an alias, check that the held pointer is actually an alias instance
|
||||
if (Class::has_alias && need_alias && !is_alias<Class>(ptr))
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias && !is_alias<Class>(ptr))
|
||||
throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance "
|
||||
"is not an alias instance");
|
||||
|
||||
@@ -148,9 +150,10 @@ void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
|
||||
// need it, we simply move-construct the cpp value into a new instance.
|
||||
template <typename Class>
|
||||
void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||
static_assert(std::is_move_constructible<Cpp<Class>>::value,
|
||||
"pybind11::init() return-by-value factory function requires a movable class");
|
||||
if (Class::has_alias && need_alias)
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias)
|
||||
construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result));
|
||||
else
|
||||
v_h.value_ptr() = new Cpp<Class>(std::move(result));
|
||||
@@ -219,7 +222,8 @@ template <typename Func, typename Return, typename... Args>
|
||||
struct factory<Func, void_type (*)(), Return(Args...)> {
|
||||
remove_reference_t<Func> class_factory;
|
||||
|
||||
factory(Func &&f) : class_factory(std::forward<Func>(f)) { }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
factory(Func &&f) : class_factory(std::forward<Func>(f)) {}
|
||||
|
||||
// The given class either has no alias or has no separate alias factory;
|
||||
// this always constructs the class itself. If the class is registered with an alias
|
||||
@@ -293,7 +297,13 @@ template <typename Class, typename T, typename O,
|
||||
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) {
|
||||
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)
|
||||
|
||||
@@ -11,8 +11,30 @@
|
||||
|
||||
#include "../pytypes.h"
|
||||
|
||||
/// Tracks the `internals` and `type_info` ABI version independent of the main library version.
|
||||
///
|
||||
/// Some portions of the code use an ABI that is conditional depending on this
|
||||
/// version number. That allows ABI-breaking changes to be "pre-implemented".
|
||||
/// Once the default version number is incremented, the conditional logic that
|
||||
/// no longer applies can be removed. Additionally, users that need not
|
||||
/// maintain ABI compatibility can increase the version number in order to take
|
||||
/// advantage of any functionality/efficiency improvements that depend on the
|
||||
/// newer ABI.
|
||||
///
|
||||
/// WARNING: If you choose to manually increase the ABI version, note that
|
||||
/// pybind11 may not be tested as thoroughly with a non-default ABI version, and
|
||||
/// further ABI-incompatible changes may be made before the ABI is officially
|
||||
/// changed to the new version.
|
||||
#ifndef PYBIND11_INTERNALS_VERSION
|
||||
# define PYBIND11_INTERNALS_VERSION 4
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
using ExceptionTranslator = void (*)(std::exception_ptr);
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
// Forward declarations
|
||||
inline PyTypeObject *make_static_property_type();
|
||||
inline PyTypeObject *make_default_metaclass();
|
||||
@@ -21,30 +43,59 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass);
|
||||
// The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new
|
||||
// Thread Specific Storage (TSS) API.
|
||||
#if PY_VERSION_HEX >= 0x03070000
|
||||
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr
|
||||
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key))
|
||||
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value))
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr)
|
||||
# define PYBIND11_TLS_FREE(key) PyThread_tss_free(key)
|
||||
#else
|
||||
// Usually an int but a long on Cygwin64 with Python 3.x
|
||||
# define PYBIND11_TLS_KEY_INIT(var) decltype(PyThread_create_key()) var = 0
|
||||
# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key))
|
||||
# if PY_MAJOR_VERSION < 3
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) \
|
||||
PyThread_delete_key_value(key)
|
||||
# define PYBIND11_TLS_REPLACE_VALUE(key, value) \
|
||||
do { \
|
||||
PyThread_delete_key_value((key)); \
|
||||
PyThread_set_key_value((key), (value)); \
|
||||
} while (false)
|
||||
// Avoid unnecessary allocation of `Py_tss_t`, since we cannot use
|
||||
// `Py_LIMITED_API` anyway.
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
# define PYBIND11_TLS_KEY_REF Py_tss_t &
|
||||
# ifdef __GNUC__
|
||||
// Clang on macOS warns due to `Py_tss_NEEDS_INIT` not specifying an initializer
|
||||
// for every field.
|
||||
# define PYBIND11_TLS_KEY_INIT(var) \
|
||||
_Pragma("GCC diagnostic push") /**/ \
|
||||
_Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \
|
||||
Py_tss_t var \
|
||||
= Py_tss_NEEDS_INIT; \
|
||||
_Pragma("GCC diagnostic pop")
|
||||
# else
|
||||
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t var = Py_tss_NEEDS_INIT;
|
||||
# endif
|
||||
# define PYBIND11_TLS_KEY_CREATE(var) (PyThread_tss_create(&(var)) == 0)
|
||||
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get(&(key))
|
||||
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set(&(key), (value))
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set(&(key), nullptr)
|
||||
# define PYBIND11_TLS_FREE(key) PyThread_tss_delete(&(key))
|
||||
# else
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) \
|
||||
PyThread_set_key_value((key), nullptr)
|
||||
# define PYBIND11_TLS_REPLACE_VALUE(key, value) \
|
||||
PyThread_set_key_value((key), (value))
|
||||
# define PYBIND11_TLS_KEY_REF Py_tss_t *
|
||||
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr;
|
||||
# define PYBIND11_TLS_KEY_CREATE(var) \
|
||||
(((var) = PyThread_tss_alloc()) != nullptr && (PyThread_tss_create((var)) == 0))
|
||||
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key))
|
||||
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value))
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr)
|
||||
# define PYBIND11_TLS_FREE(key) PyThread_tss_free(key)
|
||||
# endif
|
||||
# define PYBIND11_TLS_FREE(key) (void)key
|
||||
#else
|
||||
// Usually an int but a long on Cygwin64 with Python 3.x
|
||||
# define PYBIND11_TLS_KEY_REF decltype(PyThread_create_key())
|
||||
# define PYBIND11_TLS_KEY_INIT(var) PYBIND11_TLS_KEY_REF var = 0;
|
||||
# define PYBIND11_TLS_KEY_CREATE(var) (((var) = PyThread_create_key()) != -1)
|
||||
# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key))
|
||||
# if PY_MAJOR_VERSION < 3 || defined(PYPY_VERSION)
|
||||
// On CPython < 3.4 and on PyPy, `PyThread_set_key_value` strangely does not set
|
||||
// the value if it has already been set. Instead, it must first be deleted and
|
||||
// then set again.
|
||||
inline void tls_replace_value(PYBIND11_TLS_KEY_REF key, void *value) {
|
||||
PyThread_delete_key_value(key);
|
||||
PyThread_set_key_value(key, value);
|
||||
}
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_delete_key_value(key)
|
||||
# define PYBIND11_TLS_REPLACE_VALUE(key, value) \
|
||||
::pybind11::detail::tls_replace_value((key), (value))
|
||||
# else
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_set_key_value((key), nullptr)
|
||||
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_set_key_value((key), (value))
|
||||
# endif
|
||||
# define PYBIND11_TLS_FREE(key) (void) key
|
||||
#endif
|
||||
|
||||
// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly
|
||||
@@ -100,24 +151,33 @@ struct internals {
|
||||
std::unordered_set<std::pair<const PyObject *, const char *>, override_hash> inactive_override_cache;
|
||||
type_map<std::vector<bool (*)(PyObject *, void *&)>> direct_conversions;
|
||||
std::unordered_map<const PyObject *, std::vector<PyObject *>> patients;
|
||||
std::forward_list<void (*) (std::exception_ptr)> registered_exception_translators;
|
||||
std::forward_list<ExceptionTranslator> registered_exception_translators;
|
||||
std::unordered_map<std::string, void *> shared_data; // Custom data to be shared across extensions
|
||||
std::vector<PyObject *> loader_patient_stack; // Used by `loader_life_support`
|
||||
#if PYBIND11_INTERNALS_VERSION == 4
|
||||
std::vector<PyObject *> unused_loader_patient_stack_remove_at_v5;
|
||||
#endif
|
||||
std::forward_list<std::string> static_strings; // Stores the std::strings backing detail::c_str()
|
||||
PyTypeObject *static_property_type;
|
||||
PyTypeObject *default_metaclass;
|
||||
PyObject *instance_base;
|
||||
#if defined(WITH_THREAD)
|
||||
PYBIND11_TLS_KEY_INIT(tstate);
|
||||
PYBIND11_TLS_KEY_INIT(tstate)
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
|
||||
# endif // PYBIND11_INTERNALS_VERSION > 4
|
||||
PyInterpreterState *istate = nullptr;
|
||||
~internals() {
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
PYBIND11_TLS_FREE(loader_life_support_tls_key);
|
||||
# endif // PYBIND11_INTERNALS_VERSION > 4
|
||||
|
||||
// This destructor is called *after* Py_Finalize() in finalize_interpreter().
|
||||
// That *SHOULD BE* fine. The following details what happens when PyThread_tss_free is called.
|
||||
// PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does nothing.
|
||||
// PyThread_tss_free calls PyThread_tss_delete and PyMem_RawFree.
|
||||
// PyThread_tss_delete just calls TlsFree (on Windows) or pthread_key_delete (on *NIX). Neither
|
||||
// of those have anything to do with CPython internals.
|
||||
// PyMem_RawFree *requires* that the `tstate` be allocated with the CPython allocator.
|
||||
// That *SHOULD BE* fine. The following details what happens when PyThread_tss_free is
|
||||
// called. PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does
|
||||
// nothing. PyThread_tss_free calls PyThread_tss_delete and PyMem_RawFree.
|
||||
// PyThread_tss_delete just calls TlsFree (on Windows) or pthread_key_delete (on *NIX).
|
||||
// Neither of those have anything to do with CPython internals. PyMem_RawFree *requires*
|
||||
// that the `tstate` be allocated with the CPython allocator.
|
||||
PYBIND11_TLS_FREE(tstate);
|
||||
}
|
||||
#endif
|
||||
@@ -149,9 +209,6 @@ struct type_info {
|
||||
bool module_local : 1;
|
||||
};
|
||||
|
||||
/// Tracks the `internals` and `type_info` ABI version independent of the main library version
|
||||
#define PYBIND11_INTERNALS_VERSION 4
|
||||
|
||||
/// On MSVC, debug and release builds are not ABI-compatible!
|
||||
#if defined(_MSC_VER) && defined(_DEBUG)
|
||||
# define PYBIND11_BUILD_TYPE "_debug"
|
||||
@@ -253,7 +310,7 @@ inline void translate_local_exception(std::exception_ptr p) {
|
||||
#endif
|
||||
|
||||
/// Return a reference to the current `internals` data
|
||||
PYBIND11_NOINLINE inline internals &get_internals() {
|
||||
PYBIND11_NOINLINE internals &get_internals() {
|
||||
auto **&internals_pp = get_internals_pp();
|
||||
if (internals_pp && *internals_pp)
|
||||
return **internals_pp;
|
||||
@@ -276,6 +333,8 @@ PYBIND11_NOINLINE inline internals &get_internals() {
|
||||
// initial exception translator, below, so add another for our local exception classes.
|
||||
//
|
||||
// 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__)
|
||||
(*internals_pp)->registered_exception_translators.push_front(&translate_local_exception);
|
||||
#endif
|
||||
@@ -285,21 +344,21 @@ PYBIND11_NOINLINE inline internals &get_internals() {
|
||||
internals_ptr = new internals();
|
||||
#if defined(WITH_THREAD)
|
||||
|
||||
#if PY_VERSION_HEX < 0x03090000
|
||||
PyEval_InitThreads();
|
||||
#endif
|
||||
# if PY_VERSION_HEX < 0x03090000
|
||||
PyEval_InitThreads();
|
||||
# endif
|
||||
PyThreadState *tstate = PyThreadState_Get();
|
||||
#if PY_VERSION_HEX >= 0x03070000
|
||||
internals_ptr->tstate = PyThread_tss_alloc();
|
||||
if (!internals_ptr->tstate || PyThread_tss_create(internals_ptr->tstate))
|
||||
pybind11_fail("get_internals: could not successfully initialize the TSS key!");
|
||||
PyThread_tss_set(internals_ptr->tstate, tstate);
|
||||
#else
|
||||
internals_ptr->tstate = PyThread_create_key();
|
||||
if (internals_ptr->tstate == -1)
|
||||
pybind11_fail("get_internals: could not successfully initialize the TLS key!");
|
||||
PyThread_set_key_value(internals_ptr->tstate, tstate);
|
||||
#endif
|
||||
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->tstate)) {
|
||||
pybind11_fail("get_internals: could not successfully initialize the tstate TSS key!");
|
||||
}
|
||||
PYBIND11_TLS_REPLACE_VALUE(internals_ptr->tstate, tstate);
|
||||
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->loader_life_support_tls_key)) {
|
||||
pybind11_fail("get_internals: could not successfully initialize the "
|
||||
"loader_life_support TSS key!");
|
||||
}
|
||||
# endif
|
||||
internals_ptr->istate = tstate->interp;
|
||||
#endif
|
||||
builtins[id] = capsule(internals_pp);
|
||||
@@ -311,12 +370,57 @@ PYBIND11_NOINLINE inline internals &get_internals() {
|
||||
return **internals_pp;
|
||||
}
|
||||
|
||||
/// Works like `internals.registered_types_cpp`, but for module-local registered types:
|
||||
inline type_map<type_info *> ®istered_local_types_cpp() {
|
||||
static type_map<type_info *> locals{};
|
||||
return locals;
|
||||
// the internals struct (above) is shared between all the modules. local_internals are only
|
||||
// for a single module. Any changes made to internals may require an update to
|
||||
// PYBIND11_INTERNALS_VERSION, breaking backwards compatibility. local_internals is, by design,
|
||||
// restricted to a single module. Whether a module has local internals or not should not
|
||||
// impact any other modules, because the only things accessing the local internals is the
|
||||
// module that contains them.
|
||||
struct local_internals {
|
||||
type_map<type_info *> registered_types_cpp;
|
||||
std::forward_list<ExceptionTranslator> registered_exception_translators;
|
||||
#if defined(WITH_THREAD) && PYBIND11_INTERNALS_VERSION == 4
|
||||
|
||||
// For ABI compatibility, we can't store the loader_life_support TLS key in
|
||||
// the `internals` struct directly. Instead, we store it in `shared_data` and
|
||||
// cache a copy in `local_internals`. If we allocated a separate TLS key for
|
||||
// each instance of `local_internals`, we could end up allocating hundreds of
|
||||
// TLS keys if hundreds of different pybind11 modules are loaded (which is a
|
||||
// plausible number).
|
||||
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
|
||||
|
||||
// Holds the shared TLS key for the loader_life_support stack.
|
||||
struct shared_loader_life_support_data {
|
||||
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
|
||||
shared_loader_life_support_data() {
|
||||
if (!PYBIND11_TLS_KEY_CREATE(loader_life_support_tls_key)) {
|
||||
pybind11_fail("local_internals: could not successfully initialize the "
|
||||
"loader_life_support TLS key!");
|
||||
}
|
||||
}
|
||||
// We can't help but leak the TLS key, because Python never unloads extension modules.
|
||||
};
|
||||
|
||||
local_internals() {
|
||||
auto &internals = get_internals();
|
||||
// Get or create the `loader_life_support_stack_key`.
|
||||
auto &ptr = internals.shared_data["_life_support"];
|
||||
if (!ptr) {
|
||||
ptr = new shared_loader_life_support_data;
|
||||
}
|
||||
loader_life_support_tls_key
|
||||
= static_cast<shared_loader_life_support_data *>(ptr)->loader_life_support_tls_key;
|
||||
}
|
||||
#endif // defined(WITH_THREAD) && PYBIND11_INTERNALS_VERSION == 4
|
||||
};
|
||||
|
||||
/// Works like `get_internals`, but for things which are locally registered.
|
||||
inline local_internals &get_local_internals() {
|
||||
static local_internals locals;
|
||||
return locals;
|
||||
}
|
||||
|
||||
|
||||
/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its
|
||||
/// `c_str()`. Such strings objects have a long storage duration -- the internal strings are only
|
||||
/// cleared when the program exits or after interpreter shutdown (when embedding), and so are
|
||||
@@ -333,14 +437,14 @@ PYBIND11_NAMESPACE_END(detail)
|
||||
/// Returns a named pointer that is shared among all extension modules (using the same
|
||||
/// pybind11 version) running in the current interpreter. Names starting with underscores
|
||||
/// are reserved for internal usage. Returns `nullptr` if no matching entry was found.
|
||||
inline PYBIND11_NOINLINE void *get_shared_data(const std::string &name) {
|
||||
PYBIND11_NOINLINE void *get_shared_data(const std::string &name) {
|
||||
auto &internals = detail::get_internals();
|
||||
auto it = internals.shared_data.find(name);
|
||||
return it != internals.shared_data.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
/// Set the shared data that can be later recovered by `get_shared_data()`.
|
||||
inline PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) {
|
||||
PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) {
|
||||
detail::get_internals().shared_data[name] = data;
|
||||
return data;
|
||||
}
|
||||
|
||||
985
ext/pybind11/include/pybind11/detail/type_caster_base.h
Normal file
985
ext/pybind11/include/pybind11/detail/type_caster_base.h
Normal file
@@ -0,0 +1,985 @@
|
||||
/*
|
||||
pybind11/detail/type_caster_base.h (originally first part of pybind11/cast.h)
|
||||
|
||||
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
||||
|
||||
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 "../pytypes.h"
|
||||
#include "common.h"
|
||||
#include "descr.h"
|
||||
#include "internals.h"
|
||||
#include "typeid.h"
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <new>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
/// A life support system for temporary objects created by `type_caster::load()`.
|
||||
/// Adding a patient will keep it alive up until the enclosing function returns.
|
||||
class loader_life_support {
|
||||
private:
|
||||
loader_life_support* parent = nullptr;
|
||||
std::unordered_set<PyObject *> keep_alive;
|
||||
|
||||
#if defined(WITH_THREAD)
|
||||
// Store stack pointer in thread-local storage.
|
||||
static PYBIND11_TLS_KEY_REF get_stack_tls_key() {
|
||||
# if PYBIND11_INTERNALS_VERSION == 4
|
||||
return get_local_internals().loader_life_support_tls_key;
|
||||
# else
|
||||
return get_internals().loader_life_support_tls_key;
|
||||
# endif
|
||||
}
|
||||
static loader_life_support *get_stack_top() {
|
||||
return static_cast<loader_life_support *>(PYBIND11_TLS_GET_VALUE(get_stack_tls_key()));
|
||||
}
|
||||
static void set_stack_top(loader_life_support *value) {
|
||||
PYBIND11_TLS_REPLACE_VALUE(get_stack_tls_key(), value);
|
||||
}
|
||||
#else
|
||||
// Use single global variable for stack.
|
||||
static loader_life_support **get_stack_pp() {
|
||||
static loader_life_support *global_stack = nullptr;
|
||||
return global_stack;
|
||||
}
|
||||
static loader_life_support *get_stack_top() { return *get_stack_pp(); }
|
||||
static void set_stack_top(loader_life_support *value) { *get_stack_pp() = value; }
|
||||
#endif
|
||||
|
||||
public:
|
||||
/// A new patient frame is created when a function is entered
|
||||
loader_life_support() {
|
||||
parent = get_stack_top();
|
||||
set_stack_top(this);
|
||||
}
|
||||
|
||||
/// ... and destroyed after it returns
|
||||
~loader_life_support() {
|
||||
if (get_stack_top() != this)
|
||||
pybind11_fail("loader_life_support: internal error");
|
||||
set_stack_top(parent);
|
||||
for (auto* item : keep_alive)
|
||||
Py_DECREF(item);
|
||||
}
|
||||
|
||||
/// This can only be used inside a pybind11-bound function, either by `argument_loader`
|
||||
/// at argument preparation time or by `py::cast()` at execution time.
|
||||
PYBIND11_NOINLINE static void add_patient(handle h) {
|
||||
loader_life_support *frame = get_stack_top();
|
||||
if (!frame) {
|
||||
// NOTE: It would be nice to include the stack frames here, as this indicates
|
||||
// use of pybind11::cast<> outside the normal call framework, finding such
|
||||
// a location is challenging. Developers could consider printing out
|
||||
// stack frame addresses here using something like __builtin_frame_address(0)
|
||||
throw cast_error("When called outside a bound function, py::cast() cannot "
|
||||
"do Python -> C++ conversions which require the creation "
|
||||
"of temporary values");
|
||||
}
|
||||
|
||||
if (frame->keep_alive.insert(h.ptr()).second)
|
||||
Py_INCREF(h.ptr());
|
||||
}
|
||||
};
|
||||
|
||||
// Gets the cache entry for the given type, creating it if necessary. The return value is the pair
|
||||
// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was
|
||||
// just created.
|
||||
inline std::pair<decltype(internals::registered_types_py)::iterator, bool> all_type_info_get_cache(PyTypeObject *type);
|
||||
|
||||
// Populates a just-created cache entry.
|
||||
PYBIND11_NOINLINE void all_type_info_populate(PyTypeObject *t, std::vector<type_info *> &bases) {
|
||||
std::vector<PyTypeObject *> check;
|
||||
for (handle parent : reinterpret_borrow<tuple>(t->tp_bases))
|
||||
check.push_back((PyTypeObject *) parent.ptr());
|
||||
|
||||
auto const &type_dict = get_internals().registered_types_py;
|
||||
for (size_t i = 0; i < check.size(); i++) {
|
||||
auto type = check[i];
|
||||
// Ignore Python2 old-style class super type:
|
||||
if (!PyType_Check((PyObject *) type)) continue;
|
||||
|
||||
// Check `type` in the current set of registered python types:
|
||||
auto it = type_dict.find(type);
|
||||
if (it != type_dict.end()) {
|
||||
// We found a cache entry for it, so it's either pybind-registered or has pre-computed
|
||||
// pybind bases, but we have to make sure we haven't already seen the type(s) before: we
|
||||
// want to follow Python/virtual C++ rules that there should only be one instance of a
|
||||
// common base.
|
||||
for (auto *tinfo : it->second) {
|
||||
// NB: Could use a second set here, rather than doing a linear search, but since
|
||||
// having a large number of immediate pybind11-registered types seems fairly
|
||||
// unlikely, that probably isn't worthwhile.
|
||||
bool found = false;
|
||||
for (auto *known : bases) {
|
||||
if (known == tinfo) { found = true; break; }
|
||||
}
|
||||
if (!found) bases.push_back(tinfo);
|
||||
}
|
||||
}
|
||||
else if (type->tp_bases) {
|
||||
// It's some python type, so keep follow its bases classes to look for one or more
|
||||
// registered types
|
||||
if (i + 1 == check.size()) {
|
||||
// When we're at the end, we can pop off the current element to avoid growing
|
||||
// `check` when adding just one base (which is typical--i.e. when there is no
|
||||
// multiple inheritance)
|
||||
check.pop_back();
|
||||
i--;
|
||||
}
|
||||
for (handle parent : reinterpret_borrow<tuple>(type->tp_bases))
|
||||
check.push_back((PyTypeObject *) parent.ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts vector of type_info pointers of pybind-registered roots of the given Python type. Will
|
||||
* be just 1 pybind type for the Python type of a pybind-registered class, or for any Python-side
|
||||
* derived class that uses single inheritance. Will contain as many types as required for a Python
|
||||
* class that uses multiple inheritance to inherit (directly or indirectly) from multiple
|
||||
* pybind-registered classes. Will be empty if neither the type nor any base classes are
|
||||
* pybind-registered.
|
||||
*
|
||||
* The value is cached for the lifetime of the Python type.
|
||||
*/
|
||||
inline const std::vector<detail::type_info *> &all_type_info(PyTypeObject *type) {
|
||||
auto ins = all_type_info_get_cache(type);
|
||||
if (ins.second)
|
||||
// New cache entry: populate it
|
||||
all_type_info_populate(type, ins.first->second);
|
||||
|
||||
return ins.first->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any
|
||||
* ancestors are pybind11-registered. Throws an exception if there are multiple bases--use
|
||||
* `all_type_info` instead if you want to support multiple bases.
|
||||
*/
|
||||
PYBIND11_NOINLINE detail::type_info* get_type_info(PyTypeObject *type) {
|
||||
auto &bases = all_type_info(type);
|
||||
if (bases.empty())
|
||||
return nullptr;
|
||||
if (bases.size() > 1)
|
||||
pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases");
|
||||
return bases.front();
|
||||
}
|
||||
|
||||
inline detail::type_info *get_local_type_info(const std::type_index &tp) {
|
||||
auto &locals = get_local_internals().registered_types_cpp;
|
||||
auto it = locals.find(tp);
|
||||
if (it != locals.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline detail::type_info *get_global_type_info(const std::type_index &tp) {
|
||||
auto &types = get_internals().registered_types_cpp;
|
||||
auto it = types.find(tp);
|
||||
if (it != types.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Return the type info for a given C++ type; on lookup failure can either throw or return nullptr.
|
||||
PYBIND11_NOINLINE detail::type_info *get_type_info(const std::type_index &tp,
|
||||
bool throw_if_missing = false) {
|
||||
if (auto ltype = get_local_type_info(tp))
|
||||
return ltype;
|
||||
if (auto gtype = get_global_type_info(tp))
|
||||
return gtype;
|
||||
|
||||
if (throw_if_missing) {
|
||||
std::string tname = tp.name();
|
||||
detail::clean_type_id(tname);
|
||||
pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\"");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE handle get_type_handle(const std::type_info &tp, bool throw_if_missing) {
|
||||
detail::type_info *type_info = get_type_info(tp, throw_if_missing);
|
||||
return handle(type_info ? ((PyObject *) type_info->type) : nullptr);
|
||||
}
|
||||
|
||||
// Searches the inheritance graph for a registered Python instance, using all_type_info().
|
||||
PYBIND11_NOINLINE handle find_registered_python_instance(void *src,
|
||||
const detail::type_info *tinfo) {
|
||||
auto it_instances = get_internals().registered_instances.equal_range(src);
|
||||
for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) {
|
||||
for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) {
|
||||
if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype))
|
||||
return handle((PyObject *) it_i->second).inc_ref();
|
||||
}
|
||||
}
|
||||
return handle();
|
||||
}
|
||||
|
||||
struct value_and_holder {
|
||||
instance *inst = nullptr;
|
||||
size_t index = 0u;
|
||||
const detail::type_info *type = nullptr;
|
||||
void **vh = nullptr;
|
||||
|
||||
// Main constructor for a found value/holder:
|
||||
value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) :
|
||||
inst{i}, index{index}, type{type},
|
||||
vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]}
|
||||
{}
|
||||
|
||||
// Default constructor (used to signal a value-and-holder not found by get_value_and_holder())
|
||||
value_and_holder() = default;
|
||||
|
||||
// Used for past-the-end iterator
|
||||
explicit value_and_holder(size_t index) : index{index} {}
|
||||
|
||||
template <typename V = void> V *&value_ptr() const {
|
||||
return reinterpret_cast<V *&>(vh[0]);
|
||||
}
|
||||
// True if this `value_and_holder` has a non-null value pointer
|
||||
explicit operator bool() const { return value_ptr() != nullptr; }
|
||||
|
||||
template <typename H> H &holder() const {
|
||||
return reinterpret_cast<H &>(vh[1]);
|
||||
}
|
||||
bool holder_constructed() const {
|
||||
return inst->simple_layout
|
||||
? inst->simple_holder_constructed
|
||||
: (inst->nonsimple.status[index] & instance::status_holder_constructed) != 0u;
|
||||
}
|
||||
// NOLINTNEXTLINE(readability-make-member-function-const)
|
||||
void set_holder_constructed(bool v = true) {
|
||||
if (inst->simple_layout)
|
||||
inst->simple_holder_constructed = v;
|
||||
else if (v)
|
||||
inst->nonsimple.status[index] |= instance::status_holder_constructed;
|
||||
else
|
||||
inst->nonsimple.status[index] &= (std::uint8_t) ~instance::status_holder_constructed;
|
||||
}
|
||||
bool instance_registered() const {
|
||||
return inst->simple_layout
|
||||
? inst->simple_instance_registered
|
||||
: ((inst->nonsimple.status[index] & instance::status_instance_registered) != 0);
|
||||
}
|
||||
// NOLINTNEXTLINE(readability-make-member-function-const)
|
||||
void set_instance_registered(bool v = true) {
|
||||
if (inst->simple_layout)
|
||||
inst->simple_instance_registered = v;
|
||||
else if (v)
|
||||
inst->nonsimple.status[index] |= instance::status_instance_registered;
|
||||
else
|
||||
inst->nonsimple.status[index] &= (std::uint8_t) ~instance::status_instance_registered;
|
||||
}
|
||||
};
|
||||
|
||||
// Container for accessing and iterating over an instance's values/holders
|
||||
struct values_and_holders {
|
||||
private:
|
||||
instance *inst;
|
||||
using type_vec = std::vector<detail::type_info *>;
|
||||
const type_vec &tinfo;
|
||||
|
||||
public:
|
||||
explicit values_and_holders(instance *inst)
|
||||
: inst{inst}, tinfo(all_type_info(Py_TYPE(inst))) {}
|
||||
|
||||
struct iterator {
|
||||
private:
|
||||
instance *inst = nullptr;
|
||||
const type_vec *types = nullptr;
|
||||
value_and_holder curr;
|
||||
friend struct values_and_holders;
|
||||
iterator(instance *inst, const type_vec *tinfo)
|
||||
: inst{inst}, types{tinfo},
|
||||
curr(inst /* instance */,
|
||||
types->empty() ? nullptr : (*types)[0] /* type info */,
|
||||
0, /* vpos: (non-simple types only): the first vptr comes first */
|
||||
0 /* index */)
|
||||
{}
|
||||
// Past-the-end iterator:
|
||||
explicit iterator(size_t end) : curr(end) {}
|
||||
|
||||
public:
|
||||
bool operator==(const iterator &other) const { return curr.index == other.curr.index; }
|
||||
bool operator!=(const iterator &other) const { return curr.index != other.curr.index; }
|
||||
iterator &operator++() {
|
||||
if (!inst->simple_layout)
|
||||
curr.vh += 1 + (*types)[curr.index]->holder_size_in_ptrs;
|
||||
++curr.index;
|
||||
curr.type = curr.index < types->size() ? (*types)[curr.index] : nullptr;
|
||||
return *this;
|
||||
}
|
||||
value_and_holder &operator*() { return curr; }
|
||||
value_and_holder *operator->() { return &curr; }
|
||||
};
|
||||
|
||||
iterator begin() { return iterator(inst, &tinfo); }
|
||||
iterator end() { return iterator(tinfo.size()); }
|
||||
|
||||
iterator find(const type_info *find_type) {
|
||||
auto it = begin(), endit = end();
|
||||
while (it != endit && it->type != find_type) ++it;
|
||||
return it;
|
||||
}
|
||||
|
||||
size_t size() { return tinfo.size(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts C++ value and holder pointer references from an instance (which may contain multiple
|
||||
* values/holders for python-side multiple inheritance) that match the given type. Throws an error
|
||||
* if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance. If
|
||||
* `find_type` is omitted (or explicitly specified as nullptr) the first value/holder are returned,
|
||||
* regardless of type (and the resulting .type will be nullptr).
|
||||
*
|
||||
* The returned object should be short-lived: in particular, it must not outlive the called-upon
|
||||
* instance.
|
||||
*/
|
||||
PYBIND11_NOINLINE value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/, bool throw_if_missing /*= true in common.h*/) {
|
||||
// Optimize common case:
|
||||
if (!find_type || Py_TYPE(this) == find_type->type)
|
||||
return value_and_holder(this, find_type, 0, 0);
|
||||
|
||||
detail::values_and_holders vhs(this);
|
||||
auto it = vhs.find(find_type);
|
||||
if (it != vhs.end())
|
||||
return *it;
|
||||
|
||||
if (!throw_if_missing)
|
||||
return value_and_holder();
|
||||
|
||||
#if defined(NDEBUG)
|
||||
pybind11_fail("pybind11::detail::instance::get_value_and_holder: "
|
||||
"type is not a pybind11 base of the given instance "
|
||||
"(compile in debug mode for type details)");
|
||||
#else
|
||||
pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" +
|
||||
get_fully_qualified_tp_name(find_type->type) + "' is not a pybind11 base of the given `" +
|
||||
get_fully_qualified_tp_name(Py_TYPE(this)) + "' instance");
|
||||
#endif
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE void instance::allocate_layout() {
|
||||
auto &tinfo = all_type_info(Py_TYPE(this));
|
||||
|
||||
const size_t n_types = tinfo.size();
|
||||
|
||||
if (n_types == 0)
|
||||
pybind11_fail("instance allocation failed: new instance has no pybind11-registered base types");
|
||||
|
||||
simple_layout =
|
||||
n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs();
|
||||
|
||||
// Simple path: no python-side multiple inheritance, and a small-enough holder
|
||||
if (simple_layout) {
|
||||
simple_value_holder[0] = nullptr;
|
||||
simple_holder_constructed = false;
|
||||
simple_instance_registered = false;
|
||||
}
|
||||
else { // multiple base types or a too-large holder
|
||||
// Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer,
|
||||
// [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool
|
||||
// values that tracks whether each associated holder has been initialized. Each [block] is
|
||||
// padded, if necessary, to an integer multiple of sizeof(void *).
|
||||
size_t space = 0;
|
||||
for (auto t : tinfo) {
|
||||
space += 1; // value pointer
|
||||
space += t->holder_size_in_ptrs; // holder instance
|
||||
}
|
||||
size_t flags_at = space;
|
||||
space += size_in_ptrs(n_types); // status bytes (holder_constructed and instance_registered)
|
||||
|
||||
// Allocate space for flags, values, and holders, and initialize it to 0 (flags and values,
|
||||
// in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6
|
||||
// they default to using pymalloc, which is designed to be efficient for small allocations
|
||||
// like the one we're doing here; in earlier versions (and for larger allocations) they are
|
||||
// just wrappers around malloc.
|
||||
#if PY_VERSION_HEX >= 0x03050000
|
||||
nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *));
|
||||
if (!nonsimple.values_and_holders) throw std::bad_alloc();
|
||||
#else
|
||||
nonsimple.values_and_holders = (void **) PyMem_New(void *, space);
|
||||
if (!nonsimple.values_and_holders) throw std::bad_alloc();
|
||||
std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *));
|
||||
#endif
|
||||
nonsimple.status = reinterpret_cast<std::uint8_t *>(&nonsimple.values_and_holders[flags_at]);
|
||||
}
|
||||
owned = true;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-make-member-function-const)
|
||||
PYBIND11_NOINLINE void instance::deallocate_layout() {
|
||||
if (!simple_layout)
|
||||
PyMem_Free(nonsimple.values_and_holders);
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE bool isinstance_generic(handle obj, const std::type_info &tp) {
|
||||
handle type = detail::get_type_handle(tp, false);
|
||||
if (!type)
|
||||
return false;
|
||||
return isinstance(obj, type);
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE std::string error_string() {
|
||||
if (!PyErr_Occurred()) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred");
|
||||
return "Unknown internal error occurred";
|
||||
}
|
||||
|
||||
error_scope scope; // Preserve error state
|
||||
|
||||
std::string errorString;
|
||||
if (scope.type) {
|
||||
errorString += handle(scope.type).attr("__name__").cast<std::string>();
|
||||
errorString += ": ";
|
||||
}
|
||||
if (scope.value)
|
||||
errorString += (std::string) str(scope.value);
|
||||
|
||||
PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace);
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
if (scope.trace != nullptr)
|
||||
PyException_SetTraceback(scope.value, scope.trace);
|
||||
#endif
|
||||
|
||||
#if !defined(PYPY_VERSION)
|
||||
if (scope.trace) {
|
||||
auto *trace = (PyTracebackObject *) scope.trace;
|
||||
|
||||
/* Get the deepest trace possible */
|
||||
while (trace->tb_next)
|
||||
trace = trace->tb_next;
|
||||
|
||||
PyFrameObject *frame = trace->tb_frame;
|
||||
errorString += "\n\nAt:\n";
|
||||
while (frame) {
|
||||
#if PY_VERSION_HEX >= 0x03090000
|
||||
PyCodeObject *f_code = PyFrame_GetCode(frame);
|
||||
#else
|
||||
PyCodeObject *f_code = frame->f_code;
|
||||
Py_INCREF(f_code);
|
||||
#endif
|
||||
int lineno = PyFrame_GetLineNumber(frame);
|
||||
errorString +=
|
||||
" " + handle(f_code->co_filename).cast<std::string>() +
|
||||
"(" + std::to_string(lineno) + "): " +
|
||||
handle(f_code->co_name).cast<std::string>() + "\n";
|
||||
frame = frame->f_back;
|
||||
Py_DECREF(f_code);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return errorString;
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_info *type ) {
|
||||
auto &instances = get_internals().registered_instances;
|
||||
auto range = instances.equal_range(ptr);
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
for (const auto &vh : values_and_holders(it->second)) {
|
||||
if (vh.type == type)
|
||||
return handle((PyObject *) it->second);
|
||||
}
|
||||
}
|
||||
return handle();
|
||||
}
|
||||
|
||||
inline PyThreadState *get_thread_state_unchecked() {
|
||||
#if defined(PYPY_VERSION)
|
||||
return PyThreadState_GET();
|
||||
#elif PY_VERSION_HEX < 0x03000000
|
||||
return _PyThreadState_Current;
|
||||
#elif PY_VERSION_HEX < 0x03050000
|
||||
return (PyThreadState*) _Py_atomic_load_relaxed(&_PyThreadState_Current);
|
||||
#elif PY_VERSION_HEX < 0x03050200
|
||||
return (PyThreadState*) _PyThreadState_Current.value;
|
||||
#else
|
||||
return _PyThreadState_UncheckedGet();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Forward declarations
|
||||
void keep_alive_impl(handle nurse, handle patient);
|
||||
inline PyObject *make_new_instance(PyTypeObject *type);
|
||||
|
||||
class type_caster_generic {
|
||||
public:
|
||||
PYBIND11_NOINLINE explicit type_caster_generic(const std::type_info &type_info)
|
||||
: typeinfo(get_type_info(type_info)), cpptype(&type_info) {}
|
||||
|
||||
explicit type_caster_generic(const type_info *typeinfo)
|
||||
: typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) {}
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
return load_impl<type_caster_generic>(src, convert);
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent,
|
||||
const detail::type_info *tinfo,
|
||||
void *(*copy_constructor)(const void *),
|
||||
void *(*move_constructor)(const void *),
|
||||
const void *existing_holder = nullptr) {
|
||||
if (!tinfo) // no type info: error will be set already
|
||||
return handle();
|
||||
|
||||
void *src = const_cast<void *>(_src);
|
||||
if (src == nullptr)
|
||||
return none().release();
|
||||
|
||||
if (handle registered_inst = find_registered_python_instance(src, tinfo))
|
||||
return registered_inst;
|
||||
|
||||
auto inst = reinterpret_steal<object>(make_new_instance(tinfo->type));
|
||||
auto wrapper = reinterpret_cast<instance *>(inst.ptr());
|
||||
wrapper->owned = false;
|
||||
void *&valueptr = values_and_holders(wrapper).begin()->value_ptr();
|
||||
|
||||
switch (policy) {
|
||||
case return_value_policy::automatic:
|
||||
case return_value_policy::take_ownership:
|
||||
valueptr = src;
|
||||
wrapper->owned = true;
|
||||
break;
|
||||
|
||||
case return_value_policy::automatic_reference:
|
||||
case return_value_policy::reference:
|
||||
valueptr = src;
|
||||
wrapper->owned = false;
|
||||
break;
|
||||
|
||||
case return_value_policy::copy:
|
||||
if (copy_constructor)
|
||||
valueptr = copy_constructor(src);
|
||||
else {
|
||||
#if defined(NDEBUG)
|
||||
throw cast_error("return_value_policy = copy, but type is "
|
||||
"non-copyable! (compile in debug mode for details)");
|
||||
#else
|
||||
std::string type_name(tinfo->cpptype->name());
|
||||
detail::clean_type_id(type_name);
|
||||
throw cast_error("return_value_policy = copy, but type " +
|
||||
type_name + " is non-copyable!");
|
||||
#endif
|
||||
}
|
||||
wrapper->owned = true;
|
||||
break;
|
||||
|
||||
case return_value_policy::move:
|
||||
if (move_constructor)
|
||||
valueptr = move_constructor(src);
|
||||
else if (copy_constructor)
|
||||
valueptr = copy_constructor(src);
|
||||
else {
|
||||
#if defined(NDEBUG)
|
||||
throw cast_error("return_value_policy = move, but type is neither "
|
||||
"movable nor copyable! "
|
||||
"(compile in debug mode for details)");
|
||||
#else
|
||||
std::string type_name(tinfo->cpptype->name());
|
||||
detail::clean_type_id(type_name);
|
||||
throw cast_error("return_value_policy = move, but type " +
|
||||
type_name + " is neither movable nor copyable!");
|
||||
#endif
|
||||
}
|
||||
wrapper->owned = true;
|
||||
break;
|
||||
|
||||
case return_value_policy::reference_internal:
|
||||
valueptr = src;
|
||||
wrapper->owned = false;
|
||||
keep_alive_impl(inst, parent);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw cast_error("unhandled return_value_policy: should not happen!");
|
||||
}
|
||||
|
||||
tinfo->init_instance(wrapper, existing_holder);
|
||||
|
||||
return inst.release();
|
||||
}
|
||||
|
||||
// Base methods for generic caster; there are overridden in copyable_holder_caster
|
||||
void load_value(value_and_holder &&v_h) {
|
||||
auto *&vptr = v_h.value_ptr();
|
||||
// Lazy allocation for unallocated values:
|
||||
if (vptr == nullptr) {
|
||||
auto *type = v_h.type ? v_h.type : typeinfo;
|
||||
if (type->operator_new) {
|
||||
vptr = type->operator_new(type->type_size);
|
||||
} else {
|
||||
#if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912)
|
||||
if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
|
||||
vptr = ::operator new(type->type_size,
|
||||
std::align_val_t(type->type_align));
|
||||
else
|
||||
#endif
|
||||
vptr = ::operator new(type->type_size);
|
||||
}
|
||||
}
|
||||
value = vptr;
|
||||
}
|
||||
bool try_implicit_casts(handle src, bool convert) {
|
||||
for (auto &cast : typeinfo->implicit_casts) {
|
||||
type_caster_generic sub_caster(*cast.first);
|
||||
if (sub_caster.load(src, convert)) {
|
||||
value = cast.second(sub_caster.value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool try_direct_conversions(handle src) {
|
||||
for (auto &converter : *typeinfo->direct_conversions) {
|
||||
if (converter(src.ptr(), value))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void check_holder_compat() {}
|
||||
|
||||
PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) {
|
||||
auto caster = type_caster_generic(ti);
|
||||
if (caster.load(src, false))
|
||||
return caster.value;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Try to load with foreign typeinfo, if available. Used when there is no
|
||||
/// native typeinfo, or when the native one wasn't able to produce a value.
|
||||
PYBIND11_NOINLINE bool try_load_foreign_module_local(handle src) {
|
||||
constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID;
|
||||
const auto pytype = type::handle_of(src);
|
||||
if (!hasattr(pytype, local_key))
|
||||
return false;
|
||||
|
||||
type_info *foreign_typeinfo = reinterpret_borrow<capsule>(getattr(pytype, local_key));
|
||||
// Only consider this foreign loader if actually foreign and is a loader of the correct cpp type
|
||||
if (foreign_typeinfo->module_local_load == &local_load
|
||||
|| (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype)))
|
||||
return false;
|
||||
|
||||
if (auto result = foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo)) {
|
||||
value = result;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant
|
||||
// bits of code between here and copyable_holder_caster where the two classes need different
|
||||
// logic (without having to resort to virtual inheritance).
|
||||
template <typename ThisT>
|
||||
PYBIND11_NOINLINE bool load_impl(handle src, bool convert) {
|
||||
if (!src) return false;
|
||||
if (!typeinfo) return try_load_foreign_module_local(src);
|
||||
|
||||
auto &this_ = static_cast<ThisT &>(*this);
|
||||
this_.check_holder_compat();
|
||||
|
||||
PyTypeObject *srctype = Py_TYPE(src.ptr());
|
||||
|
||||
// Case 1: If src is an exact type match for the target type then we can reinterpret_cast
|
||||
// the instance's value pointer to the target type:
|
||||
if (srctype == typeinfo->type) {
|
||||
this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder());
|
||||
return true;
|
||||
}
|
||||
// Case 2: We have a derived class
|
||||
if (PyType_IsSubtype(srctype, typeinfo->type)) {
|
||||
auto &bases = all_type_info(srctype);
|
||||
bool no_cpp_mi = typeinfo->simple_type;
|
||||
|
||||
// Case 2a: the python type is a Python-inherited derived class that inherits from just
|
||||
// one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of
|
||||
// the right type and we can use reinterpret_cast.
|
||||
// (This is essentially the same as case 2b, but because not using multiple inheritance
|
||||
// is extremely common, we handle it specially to avoid the loop iterator and type
|
||||
// pointer lookup overhead)
|
||||
if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) {
|
||||
this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder());
|
||||
return true;
|
||||
}
|
||||
// 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
|
||||
// can safely reinterpret_cast to the relevant pointer.
|
||||
if (bases.size() > 1) {
|
||||
for (auto base : bases) {
|
||||
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));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match
|
||||
// in the registered bases, above, so try implicit casting (needed for proper C++ casting
|
||||
// when MI is involved).
|
||||
if (this_.try_implicit_casts(src, convert))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Perform an implicit conversion
|
||||
if (convert) {
|
||||
for (auto &converter : typeinfo->implicit_conversions) {
|
||||
auto temp = reinterpret_steal<object>(converter(src.ptr(), typeinfo->type));
|
||||
if (load_impl<ThisT>(temp, false)) {
|
||||
loader_life_support::add_patient(temp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (this_.try_direct_conversions(src))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Failed to match local typeinfo. Try again with global.
|
||||
if (typeinfo->module_local) {
|
||||
if (auto gtype = get_global_type_info(*typeinfo->cpptype)) {
|
||||
typeinfo = gtype;
|
||||
return load(src, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Global typeinfo has precedence over foreign module_local
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
// Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast
|
||||
// isn't needed or can't be used. If the type is unknown, sets the error and returns a pair
|
||||
// with .second = nullptr. (p.first = nullptr is not an error: it becomes None).
|
||||
PYBIND11_NOINLINE static std::pair<const void *, const type_info *> src_and_type(
|
||||
const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) {
|
||||
if (auto *tpi = get_type_info(cast_type))
|
||||
return {src, const_cast<const type_info *>(tpi)};
|
||||
|
||||
// Not found, set error:
|
||||
std::string tname = rtti_type ? rtti_type->name() : cast_type.name();
|
||||
detail::clean_type_id(tname);
|
||||
std::string msg = "Unregistered type : " + tname;
|
||||
PyErr_SetString(PyExc_TypeError, msg.c_str());
|
||||
return {nullptr, nullptr};
|
||||
}
|
||||
|
||||
const type_info *typeinfo = nullptr;
|
||||
const std::type_info *cpptype = nullptr;
|
||||
void *value = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster
|
||||
* needs to provide `operator T*()` and `operator T&()` operators.
|
||||
*
|
||||
* If the type supports moving the value away via an `operator T&&() &&` method, it should use
|
||||
* `movable_cast_op_type` instead.
|
||||
*/
|
||||
template <typename T>
|
||||
using cast_op_type =
|
||||
conditional_t<std::is_pointer<remove_reference_t<T>>::value,
|
||||
typename std::add_pointer<intrinsic_t<T>>::type,
|
||||
typename std::add_lvalue_reference<intrinsic_t<T>>::type>;
|
||||
|
||||
/**
|
||||
* Determine suitable casting operator for a type caster with a movable value. Such a type caster
|
||||
* needs to provide `operator T*()`, `operator T&()`, and `operator T&&() &&`. The latter will be
|
||||
* called in appropriate contexts where the value can be moved rather than copied.
|
||||
*
|
||||
* These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro.
|
||||
*/
|
||||
template <typename T>
|
||||
using movable_cast_op_type =
|
||||
conditional_t<std::is_pointer<typename std::remove_reference<T>::type>::value,
|
||||
typename std::add_pointer<intrinsic_t<T>>::type,
|
||||
conditional_t<std::is_rvalue_reference<T>::value,
|
||||
typename std::add_rvalue_reference<intrinsic_t<T>>::type,
|
||||
typename std::add_lvalue_reference<intrinsic_t<T>>::type>>;
|
||||
|
||||
// std::is_copy_constructible isn't quite enough: it lets std::vector<T> (and similar) through when
|
||||
// T is non-copyable, but code containing such a copy constructor fails to actually compile.
|
||||
template <typename T, typename SFINAE = void> struct is_copy_constructible : std::is_copy_constructible<T> {};
|
||||
|
||||
// Specialization for types that appear to be copy constructible but also look like stl containers
|
||||
// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if
|
||||
// so, copy constructability depends on whether the value_type is copy constructible.
|
||||
template <typename Container> struct is_copy_constructible<Container, enable_if_t<all_of<
|
||||
std::is_copy_constructible<Container>,
|
||||
std::is_same<typename Container::value_type &, typename Container::reference>,
|
||||
// Avoid infinite recursion
|
||||
negation<std::is_same<Container, typename Container::value_type>>
|
||||
>::value>> : is_copy_constructible<typename Container::value_type> {};
|
||||
|
||||
// Likewise for std::pair
|
||||
// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't themselves
|
||||
// copy constructible, but this can not be relied upon when T1 or T2 are themselves containers).
|
||||
template <typename T1, typename T2> struct is_copy_constructible<std::pair<T1, T2>>
|
||||
: all_of<is_copy_constructible<T1>, is_copy_constructible<T2>> {};
|
||||
|
||||
// The same problems arise with std::is_copy_assignable, so we use the same workaround.
|
||||
template <typename T, typename SFINAE = void> struct is_copy_assignable : std::is_copy_assignable<T> {};
|
||||
template <typename Container> struct is_copy_assignable<Container, enable_if_t<all_of<
|
||||
std::is_copy_assignable<Container>,
|
||||
std::is_same<typename Container::value_type &, typename Container::reference>
|
||||
>::value>> : is_copy_assignable<typename Container::value_type> {};
|
||||
template <typename T1, typename T2> struct is_copy_assignable<std::pair<T1, T2>>
|
||||
: all_of<is_copy_assignable<T1>, is_copy_assignable<T2>> {};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
// polymorphic_type_hook<itype>::get(src, tinfo) determines whether the object pointed
|
||||
// to by `src` actually is an instance of some class derived from `itype`.
|
||||
// If so, it sets `tinfo` to point to the std::type_info representing that derived
|
||||
// type, and returns a pointer to the start of the most-derived object of that type
|
||||
// (in which `src` is a subobject; this will be the same address as `src` in most
|
||||
// single inheritance cases). If not, or if `src` is nullptr, it simply returns `src`
|
||||
// and leaves `tinfo` at its default value of nullptr.
|
||||
//
|
||||
// The default polymorphic_type_hook just returns src. A specialization for polymorphic
|
||||
// types determines the runtime type of the passed object and adjusts the this-pointer
|
||||
// appropriately via dynamic_cast<void*>. This is what enables a C++ Animal* to appear
|
||||
// to Python as a Dog (if Dog inherits from Animal, Animal is polymorphic, Dog is
|
||||
// registered with pybind11, and this Animal is in fact a Dog).
|
||||
//
|
||||
// You may specialize polymorphic_type_hook yourself for types that want to appear
|
||||
// polymorphic to Python but do not use C++ RTTI. (This is a not uncommon pattern
|
||||
// in performance-sensitive applications, used most notably in LLVM.)
|
||||
//
|
||||
// polymorphic_type_hook_base allows users to specialize polymorphic_type_hook with
|
||||
// std::enable_if. User provided specializations will always have higher priority than
|
||||
// the default implementation and specialization provided in polymorphic_type_hook_base.
|
||||
template <typename itype, typename SFINAE = void>
|
||||
struct polymorphic_type_hook_base
|
||||
{
|
||||
static const void *get(const itype *src, const std::type_info*&) { return src; }
|
||||
};
|
||||
template <typename itype>
|
||||
struct polymorphic_type_hook_base<itype, detail::enable_if_t<std::is_polymorphic<itype>::value>>
|
||||
{
|
||||
static const void *get(const itype *src, const std::type_info*& type) {
|
||||
type = src ? &typeid(*src) : nullptr;
|
||||
return dynamic_cast<const void*>(src);
|
||||
}
|
||||
};
|
||||
template <typename itype, typename SFINAE = void>
|
||||
struct polymorphic_type_hook : public polymorphic_type_hook_base<itype> {};
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
/// Generic type caster for objects stored on the heap
|
||||
template <typename type> class type_caster_base : public type_caster_generic {
|
||||
using itype = intrinsic_t<type>;
|
||||
|
||||
public:
|
||||
static constexpr auto name = _<type>();
|
||||
|
||||
type_caster_base() : type_caster_base(typeid(type)) { }
|
||||
explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { }
|
||||
|
||||
static handle cast(const itype &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference)
|
||||
policy = return_value_policy::copy;
|
||||
return cast(&src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(itype &&src, return_value_policy, handle parent) {
|
||||
return cast(&src, return_value_policy::move, parent);
|
||||
}
|
||||
|
||||
// Returns a (pointer, type_info) pair taking care of necessary type lookup for a
|
||||
// polymorphic type (using RTTI by default, but can be overridden by specializing
|
||||
// polymorphic_type_hook). If the instance isn't derived, returns the base version.
|
||||
static std::pair<const void *, const type_info *> src_and_type(const itype *src) {
|
||||
auto &cast_type = typeid(itype);
|
||||
const std::type_info *instance_type = nullptr;
|
||||
const void *vsrc = polymorphic_type_hook<itype>::get(src, instance_type);
|
||||
if (instance_type && !same_type(cast_type, *instance_type)) {
|
||||
// This is a base pointer to a derived type. If the derived type is registered
|
||||
// with pybind11, we want to make the full derived object available.
|
||||
// In the typical case where itype is polymorphic, we get the correct
|
||||
// derived pointer (which may be != base pointer) by a dynamic_cast to
|
||||
// most derived type. If itype is not polymorphic, we won't get here
|
||||
// except via a user-provided specialization of polymorphic_type_hook,
|
||||
// and the user has promised that no this-pointer adjustment is
|
||||
// required in that case, so it's OK to use static_cast.
|
||||
if (const auto *tpi = get_type_info(*instance_type))
|
||||
return {vsrc, tpi};
|
||||
}
|
||||
// Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so
|
||||
// don't do a cast
|
||||
return type_caster_generic::src_and_type(src, cast_type, instance_type);
|
||||
}
|
||||
|
||||
static handle cast(const itype *src, return_value_policy policy, handle parent) {
|
||||
auto st = src_and_type(src);
|
||||
return type_caster_generic::cast(
|
||||
st.first, policy, parent, st.second,
|
||||
make_copy_constructor(src), make_move_constructor(src));
|
||||
}
|
||||
|
||||
static handle cast_holder(const itype *src, const void *holder) {
|
||||
auto st = src_and_type(src);
|
||||
return type_caster_generic::cast(
|
||||
st.first, return_value_policy::take_ownership, {}, st.second,
|
||||
nullptr, nullptr, holder);
|
||||
}
|
||||
|
||||
template <typename T> using cast_op_type = detail::cast_op_type<T>;
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator itype*() { return (type *) value; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator itype&() { if (!value) throw reference_cast_error(); return *((itype *) value); }
|
||||
|
||||
protected:
|
||||
using Constructor = void *(*)(const void *);
|
||||
|
||||
/* Only enabled when the types are {copy,move}-constructible *and* when the type
|
||||
does not have a private operator new implementation. A comma operator is used in the decltype
|
||||
argument to apply SFINAE to the public copy/move constructors.*/
|
||||
template <typename T, typename = enable_if_t<is_copy_constructible<T>::value>>
|
||||
static auto make_copy_constructor(const T *) -> decltype(new T(std::declval<const T>()), Constructor{}) {
|
||||
return [](const void *arg) -> void * {
|
||||
return new T(*reinterpret_cast<const T *>(arg));
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T, typename = enable_if_t<std::is_move_constructible<T>::value>>
|
||||
static auto make_move_constructor(const T *) -> decltype(new T(std::declval<T&&>()), Constructor{}) {
|
||||
return [](const void *arg) -> void * {
|
||||
return new T(std::move(*const_cast<T *>(reinterpret_cast<const T *>(arg))));
|
||||
};
|
||||
}
|
||||
|
||||
static Constructor make_copy_constructor(...) { return nullptr; }
|
||||
static Constructor make_move_constructor(...) { return nullptr; }
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
@@ -29,7 +29,7 @@ inline void erase_all(std::string &string, const std::string &search) {
|
||||
}
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE inline void clean_type_id(std::string &name) {
|
||||
PYBIND11_NOINLINE void clean_type_id(std::string &name) {
|
||||
#if defined(__GNUG__)
|
||||
int status = 0;
|
||||
std::unique_ptr<char, void (*)(void *)> res {
|
||||
|
||||
@@ -9,33 +9,31 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
/* HINT: To suppress warnings originating from the Eigen headers, use -isystem.
|
||||
See also:
|
||||
https://stackoverflow.com/questions/2579576/i-dir-vs-isystem-dir
|
||||
https://stackoverflow.com/questions/1741816/isystem-for-ms-visual-studio-c-compiler
|
||||
*/
|
||||
|
||||
#include "numpy.h"
|
||||
|
||||
#if defined(__INTEL_COMPILER)
|
||||
# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem)
|
||||
#elif defined(__GNUG__) || defined(__clang__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wconversion"
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
# ifdef __clang__
|
||||
// Eigen generates a bunch of implicit-copy-constructor-is-deprecated warnings with -Wdeprecated
|
||||
// under Clang, so disable that warning here:
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated"
|
||||
# endif
|
||||
# if __GNUC__ >= 7
|
||||
# pragma GCC diagnostic ignored "-Wint-in-bool-context"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// The C4127 suppression was introduced for Eigen 3.4.0. In theory we could
|
||||
// make it version specific, or even remove it later, but considering that
|
||||
// 1. C4127 is generally far more distracting than useful for modern template code, and
|
||||
// 2. we definitely want to ignore any MSVC warnings originating from Eigen code,
|
||||
// it is probably best to keep this around indefinitely.
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
||||
# pragma warning(disable: 4996) // warning C4996: std::unary_negate is deprecated in C++17
|
||||
# pragma warning(disable: 4127) // C4127: conditional expression is constant
|
||||
#endif
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/SparseCore>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit
|
||||
// move constructors that break things. We could detect this an explicitly copy, but an extra copy
|
||||
// of matrices seems highly undesirable.
|
||||
@@ -77,6 +75,7 @@ template <bool EigenRowMajor> struct EigenConformable {
|
||||
EigenDStride stride{0, 0}; // Only valid if negativestrides is false!
|
||||
bool negativestrides = false; // If true, do not use stride!
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
EigenConformable(bool fits = false) : conformable{fits} {}
|
||||
// Matrix type:
|
||||
EigenConformable(EigenIndex r, EigenIndex c,
|
||||
@@ -104,6 +103,7 @@ template <bool EigenRowMajor> struct EigenConformable {
|
||||
(props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() ||
|
||||
(EigenRowMajor ? rows : cols) == 1);
|
||||
}
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator bool() const { return conformable; }
|
||||
};
|
||||
|
||||
@@ -153,7 +153,8 @@ template <typename Type_> struct EigenProps {
|
||||
np_cols = a.shape(1),
|
||||
np_rstride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)),
|
||||
np_cstride = a.strides(1) / static_cast<ssize_t>(sizeof(Scalar));
|
||||
if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols))
|
||||
if ((PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && np_rows != rows) ||
|
||||
(PYBIND11_SILENCE_MSVC_C4127(fixed_cols) && np_cols != cols))
|
||||
return false;
|
||||
|
||||
return {np_rows, np_cols, np_rstride, np_cstride};
|
||||
@@ -165,25 +166,22 @@ template <typename Type_> struct EigenProps {
|
||||
stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar));
|
||||
|
||||
if (vector) { // Eigen type is a compile-time vector
|
||||
if (fixed && size != n)
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(fixed) && size != n)
|
||||
return false; // Vector size mismatch
|
||||
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
|
||||
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
|
||||
// equals the number of elements (rows is Dynamic, and so 1 row is allowed).
|
||||
if (cols != n) return false;
|
||||
return {1, n, stride};
|
||||
}
|
||||
else {
|
||||
// Otherwise it's either fully dynamic, or column dynamic; both become a column vector
|
||||
if (fixed_rows && rows != n) return false;
|
||||
} // Otherwise it's either fully dynamic, or column dynamic; both become a column vector
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && rows != n) return false;
|
||||
return {n, 1, stride};
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr bool show_writeable = is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value;
|
||||
@@ -344,8 +342,11 @@ public:
|
||||
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type*() { return &value; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type&() { return value; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type&&() && { return std::move(value); }
|
||||
template <typename T> using cast_op_type = movable_cast_op_type<T>;
|
||||
|
||||
@@ -469,7 +470,9 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type*() { return ref.get(); }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type&() { return *ref; }
|
||||
template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>;
|
||||
|
||||
@@ -570,7 +573,9 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
||||
if (!values || !innerIndices || !outerIndices)
|
||||
return false;
|
||||
|
||||
value = Eigen::MappedSparseMatrix<Scalar, Type::Flags, StorageIndex>(
|
||||
value = Eigen::MappedSparseMatrix<Scalar,
|
||||
Type::Flags & (Eigen::RowMajor | Eigen::ColMajor),
|
||||
StorageIndex>(
|
||||
shape[0].cast<Index>(), shape[1].cast<Index>(), nnz,
|
||||
outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data());
|
||||
|
||||
@@ -599,9 +604,3 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
#if defined(__GNUG__) || defined(__clang__)
|
||||
# pragma GCC diagnostic pop
|
||||
#elif defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
#include "pybind11.h"
|
||||
#include "eval.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#if defined(PYPY_VERSION)
|
||||
# error Embedding the interpreter is not supported with PyPy
|
||||
#endif
|
||||
@@ -45,25 +48,23 @@
|
||||
});
|
||||
}
|
||||
\endrst */
|
||||
#define PYBIND11_EMBEDDED_MODULE(name, variable) \
|
||||
static ::pybind11::module_::module_def \
|
||||
PYBIND11_CONCAT(pybind11_module_def_, name); \
|
||||
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
|
||||
static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \
|
||||
auto m = ::pybind11::module_::create_extension_module( \
|
||||
PYBIND11_TOSTRING(name), nullptr, \
|
||||
&PYBIND11_CONCAT(pybind11_module_def_, name)); \
|
||||
try { \
|
||||
PYBIND11_CONCAT(pybind11_init_, name)(m); \
|
||||
return m.ptr(); \
|
||||
} PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
} \
|
||||
PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name) \
|
||||
(PYBIND11_TOSTRING(name), \
|
||||
PYBIND11_CONCAT(pybind11_init_impl_, name)); \
|
||||
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &variable)
|
||||
|
||||
#define PYBIND11_EMBEDDED_MODULE(name, variable) \
|
||||
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name); \
|
||||
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
|
||||
static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \
|
||||
auto m = ::pybind11::module_::create_extension_module( \
|
||||
PYBIND11_TOSTRING(name), nullptr, &PYBIND11_CONCAT(pybind11_module_def_, name)); \
|
||||
try { \
|
||||
PYBIND11_CONCAT(pybind11_init_, name)(m); \
|
||||
return m.ptr(); \
|
||||
} \
|
||||
PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
} \
|
||||
PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name)( \
|
||||
PYBIND11_TOSTRING(name), PYBIND11_CONCAT(pybind11_init_impl_, name)); \
|
||||
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ \
|
||||
& variable) // NOLINT(bugprone-macro-parentheses)
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
@@ -76,7 +77,7 @@ struct embedded_module {
|
||||
using init_t = void (*)();
|
||||
#endif
|
||||
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");
|
||||
|
||||
auto result = PyImport_AppendInittab(name, init);
|
||||
@@ -85,29 +86,106 @@ struct embedded_module {
|
||||
}
|
||||
};
|
||||
|
||||
struct wide_char_arg_deleter {
|
||||
void operator()(wchar_t *ptr) const {
|
||||
#if PY_VERSION_HEX >= 0x030500f0
|
||||
// API docs: https://docs.python.org/3/c-api/sys.html#c.Py_DecodeLocale
|
||||
PyMem_RawFree(ptr);
|
||||
#else
|
||||
delete[] ptr;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
inline wchar_t *widen_chars(const char *safe_arg) {
|
||||
#if PY_VERSION_HEX >= 0x030500f0
|
||||
wchar_t *widened_arg = Py_DecodeLocale(safe_arg, nullptr);
|
||||
#else
|
||||
wchar_t *widened_arg = nullptr;
|
||||
# if defined(HAVE_BROKEN_MBSTOWCS) && HAVE_BROKEN_MBSTOWCS
|
||||
size_t count = strlen(safe_arg);
|
||||
# else
|
||||
size_t count = mbstowcs(nullptr, safe_arg, 0);
|
||||
# endif
|
||||
if (count != static_cast<size_t>(-1)) {
|
||||
widened_arg = new wchar_t[count + 1];
|
||||
mbstowcs(widened_arg, safe_arg, count + 1);
|
||||
}
|
||||
#endif
|
||||
return widened_arg;
|
||||
}
|
||||
|
||||
/// Python 2.x/3.x-compatible version of `PySys_SetArgv`
|
||||
inline void set_interpreter_argv(int argc, const char *const *argv, bool add_program_dir_to_path) {
|
||||
// Before it was special-cased in python 3.8, passing an empty or null argv
|
||||
// caused a segfault, so we have to reimplement the special case ourselves.
|
||||
bool special_case = (argv == nullptr || argc <= 0);
|
||||
|
||||
const char *const empty_argv[]{"\0"};
|
||||
const char *const *safe_argv = special_case ? empty_argv : argv;
|
||||
if (special_case)
|
||||
argc = 1;
|
||||
|
||||
auto argv_size = static_cast<size_t>(argc);
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
// SetArgv* on python 3 takes wchar_t, so we have to convert.
|
||||
std::unique_ptr<wchar_t *[]> widened_argv(new wchar_t *[argv_size]);
|
||||
std::vector<std::unique_ptr<wchar_t[], wide_char_arg_deleter>> widened_argv_entries;
|
||||
widened_argv_entries.reserve(argv_size);
|
||||
for (size_t ii = 0; ii < argv_size; ++ii) {
|
||||
widened_argv_entries.emplace_back(widen_chars(safe_argv[ii]));
|
||||
if (!widened_argv_entries.back()) {
|
||||
// A null here indicates a character-encoding failure or the python
|
||||
// interpreter out of memory. Give up.
|
||||
return;
|
||||
}
|
||||
widened_argv[ii] = widened_argv_entries.back().get();
|
||||
}
|
||||
|
||||
auto pysys_argv = widened_argv.get();
|
||||
#else
|
||||
// python 2.x
|
||||
std::vector<std::string> strings{safe_argv, safe_argv + argv_size};
|
||||
std::vector<char *> char_strings{argv_size};
|
||||
for (std::size_t i = 0; i < argv_size; ++i)
|
||||
char_strings[i] = &strings[i][0];
|
||||
char **pysys_argv = char_strings.data();
|
||||
#endif
|
||||
|
||||
PySys_SetArgvEx(argc, pysys_argv, static_cast<int>(add_program_dir_to_path));
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
/** \rst
|
||||
Initialize the Python interpreter. No other pybind11 or CPython API functions can be
|
||||
called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The
|
||||
optional parameter can be used to skip the registration of signal handlers (see the
|
||||
`Python documentation`_ for details). Calling this function again after the interpreter
|
||||
has already been initialized is a fatal error.
|
||||
optional `init_signal_handlers` parameter can be used to skip the registration of
|
||||
signal handlers (see the `Python documentation`_ for details). Calling this function
|
||||
again after the interpreter has already been initialized is a fatal error.
|
||||
|
||||
If initializing the Python interpreter fails, then the program is terminated. (This
|
||||
is controlled by the CPython runtime and is an exception to pybind11's normal behavior
|
||||
of throwing exceptions on errors.)
|
||||
|
||||
The remaining optional parameters, `argc`, `argv`, and `add_program_dir_to_path` are
|
||||
used to populate ``sys.argv`` and ``sys.path``.
|
||||
See the |PySys_SetArgvEx documentation|_ for details.
|
||||
|
||||
.. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx
|
||||
.. |PySys_SetArgvEx documentation| replace:: ``PySys_SetArgvEx`` documentation
|
||||
.. _PySys_SetArgvEx documentation: https://docs.python.org/3/c-api/init.html#c.PySys_SetArgvEx
|
||||
\endrst */
|
||||
inline void initialize_interpreter(bool init_signal_handlers = true) {
|
||||
if (Py_IsInitialized())
|
||||
inline void initialize_interpreter(bool init_signal_handlers = true,
|
||||
int argc = 0,
|
||||
const char *const *argv = nullptr,
|
||||
bool add_program_dir_to_path = true) {
|
||||
if (Py_IsInitialized() != 0)
|
||||
pybind11_fail("The interpreter is already running");
|
||||
|
||||
Py_InitializeEx(init_signal_handlers ? 1 : 0);
|
||||
|
||||
// Make .py files in the working directory available by default
|
||||
module_::import("sys").attr("path").cast<list>().append(".");
|
||||
detail::set_interpreter_argv(argc, argv, add_program_dir_to_path);
|
||||
}
|
||||
|
||||
/** \rst
|
||||
@@ -169,6 +247,8 @@ inline void finalize_interpreter() {
|
||||
Scope guard version of `initialize_interpreter` and `finalize_interpreter`.
|
||||
This a move-only guard and only a single instance can exist.
|
||||
|
||||
See `initialize_interpreter` for a discussion of its constructor arguments.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <pybind11/embed.h>
|
||||
@@ -180,8 +260,11 @@ inline void finalize_interpreter() {
|
||||
\endrst */
|
||||
class scoped_interpreter {
|
||||
public:
|
||||
scoped_interpreter(bool init_signal_handlers = true) {
|
||||
initialize_interpreter(init_signal_handlers);
|
||||
explicit scoped_interpreter(bool init_signal_handlers = true,
|
||||
int argc = 0,
|
||||
const char *const *argv = nullptr,
|
||||
bool add_program_dir_to_path = true) {
|
||||
initialize_interpreter(init_signal_handlers, argc, argv, add_program_dir_to_path);
|
||||
}
|
||||
|
||||
scoped_interpreter(const scoped_interpreter &) = delete;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
pybind11/exec.h: Support for evaluating Python expressions and statements
|
||||
pybind11/eval.h: Support for evaluating Python expressions and statements
|
||||
from strings and files
|
||||
|
||||
Copyright (c) 2016 Klemens Morgenstern <klemens.morgenstern@ed-chemnitz.de> and
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "pybind11.h"
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
@@ -43,7 +45,7 @@ enum eval_mode {
|
||||
};
|
||||
|
||||
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)
|
||||
local = global;
|
||||
|
||||
@@ -53,7 +55,7 @@ object eval(str expr, object global = globals(), object local = object()) {
|
||||
this seems to be the only alternative */
|
||||
std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr;
|
||||
|
||||
int start;
|
||||
int start = 0;
|
||||
switch (mode) {
|
||||
case eval_expr: start = Py_eval_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);
|
||||
}
|
||||
|
||||
inline void exec(str expr, object global = globals(), object local = object()) {
|
||||
eval<eval_statements>(expr, global, local);
|
||||
inline void exec(const str &expr, object global = globals(), object local = object()) {
|
||||
eval<eval_statements>(expr, std::move(global), std::move(local));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
int start;
|
||||
int start = 0;
|
||||
switch (mode) {
|
||||
case eval_expr: start = Py_eval_input; break;
|
||||
case eval_single_statement: start = Py_single_input; break;
|
||||
@@ -134,6 +136,15 @@ object eval_file(str fname, object global = globals(), object local = object())
|
||||
pybind11_fail("File \"" + fname_str + "\" could not be opened!");
|
||||
}
|
||||
|
||||
// In Python2, this should be encoded by getfilesystemencoding.
|
||||
// We don't boher setting it since Python2 is past EOL anyway.
|
||||
// See PR#3233
|
||||
#if PY_VERSION_HEX >= 0x03000000
|
||||
if (!global.contains("__file__")) {
|
||||
global["__file__"] = std::move(fname);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION)
|
||||
PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(),
|
||||
local.ptr());
|
||||
|
||||
@@ -43,24 +43,42 @@ public:
|
||||
captured variables), in which case the roundtrip can be avoided.
|
||||
*/
|
||||
if (auto cfunc = func.cpp_function()) {
|
||||
auto c = reinterpret_borrow<capsule>(PyCFunction_GET_SELF(cfunc.ptr()));
|
||||
auto rec = (function_record *) c;
|
||||
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;
|
||||
|
||||
if (rec && rec->is_stateless &&
|
||||
same_type(typeid(function_type), *reinterpret_cast<const std::type_info *>(rec->data[1]))) {
|
||||
struct capture { function_type f; };
|
||||
value = ((capture *) &rec->data)->f;
|
||||
return true;
|
||||
while (rec != nullptr) {
|
||||
if (rec->is_stateless
|
||||
&& same_type(typeid(function_type),
|
||||
*reinterpret_cast<const std::type_info *>(rec->data[1]))) {
|
||||
struct capture {
|
||||
function_type f;
|
||||
};
|
||||
value = ((capture *) &rec->data)->f;
|
||||
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
|
||||
struct func_handle {
|
||||
function f;
|
||||
func_handle(function&& f_) : f(std::move(f_)) {}
|
||||
func_handle(const func_handle& f_) {
|
||||
#if !(defined(_MSC_VER) && _MSC_VER == 1916 && defined(PYBIND11_CPP17))
|
||||
// This triggers a syntax error under very special conditions (very weird indeed).
|
||||
explicit
|
||||
#endif
|
||||
func_handle(function &&f_) noexcept : f(std::move(f_)) {}
|
||||
func_handle(const func_handle &f_) { operator=(f_); }
|
||||
func_handle &operator=(const func_handle &f_) {
|
||||
gil_scoped_acquire acq;
|
||||
f = f_.f;
|
||||
return *this;
|
||||
}
|
||||
~func_handle() {
|
||||
gil_scoped_acquire acq;
|
||||
@@ -71,7 +89,7 @@ public:
|
||||
// to emulate 'move initialization capture' in C++11
|
||||
struct func_wrapper {
|
||||
func_handle hfunc;
|
||||
func_wrapper(func_handle&& hf): hfunc(std::move(hf)) {}
|
||||
explicit func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {}
|
||||
Return operator()(Args... args) const {
|
||||
gil_scoped_acquire acq;
|
||||
object retval(hfunc.f(std::forward<Args>(args)...));
|
||||
@@ -92,8 +110,7 @@ public:
|
||||
auto result = f_.template target<function_type>();
|
||||
if (result)
|
||||
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();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(type, _("Callable[[") + concat(make_caster<Args>::name...) + _("], ")
|
||||
|
||||
193
ext/pybind11/include/pybind11/gil.h
Normal file
193
ext/pybind11/include/pybind11/gil.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
pybind11/gil.h: RAII helpers for managing the GIL
|
||||
|
||||
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
||||
|
||||
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 "detail/common.h"
|
||||
#include "detail/internals.h"
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
// forward declarations
|
||||
PyThreadState *get_thread_state_unchecked();
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
|
||||
#if defined(WITH_THREAD) && !defined(PYPY_VERSION)
|
||||
|
||||
/* The functions below essentially reproduce the PyGILState_* API using a RAII
|
||||
* pattern, but there are a few important differences:
|
||||
*
|
||||
* 1. When acquiring the GIL from an non-main thread during the finalization
|
||||
* phase, the GILState API blindly terminates the calling thread, which
|
||||
* is often not what is wanted. This API does not do this.
|
||||
*
|
||||
* 2. The gil_scoped_release function can optionally cut the relationship
|
||||
* of a PyThreadState and its associated thread, which allows moving it to
|
||||
* another thread (this is a fairly rare/advanced use case).
|
||||
*
|
||||
* 3. The reference count of an acquired thread state can be controlled. This
|
||||
* can be handy to prevent cases where callbacks issued from an external
|
||||
* thread would otherwise constantly construct and destroy thread state data
|
||||
* structures.
|
||||
*
|
||||
* See the Python bindings of NanoGUI (http://github.com/wjakob/nanogui) for an
|
||||
* example which uses features 2 and 3 to migrate the Python thread of
|
||||
* execution to another thread (to run the event loop on the original thread,
|
||||
* in this case).
|
||||
*/
|
||||
|
||||
class gil_scoped_acquire {
|
||||
public:
|
||||
PYBIND11_NOINLINE gil_scoped_acquire() {
|
||||
auto &internals = detail::get_internals();
|
||||
tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate);
|
||||
|
||||
if (!tstate) {
|
||||
/* Check if the GIL was acquired using the PyGILState_* API instead (e.g. if
|
||||
calling from a Python thread). Since we use a different key, this ensures
|
||||
we don't create a new thread state and deadlock in PyEval_AcquireThread
|
||||
below. Note we don't save this state with internals.tstate, since we don't
|
||||
create it we would fail to clear it (its reference count should be > 0). */
|
||||
tstate = PyGILState_GetThisThreadState();
|
||||
}
|
||||
|
||||
if (!tstate) {
|
||||
tstate = PyThreadState_New(internals.istate);
|
||||
#if !defined(NDEBUG)
|
||||
if (!tstate)
|
||||
pybind11_fail("scoped_acquire: could not create thread state!");
|
||||
#endif
|
||||
tstate->gilstate_counter = 0;
|
||||
PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate);
|
||||
} else {
|
||||
release = detail::get_thread_state_unchecked() != tstate;
|
||||
}
|
||||
|
||||
if (release) {
|
||||
PyEval_AcquireThread(tstate);
|
||||
}
|
||||
|
||||
inc_ref();
|
||||
}
|
||||
|
||||
void inc_ref() {
|
||||
++tstate->gilstate_counter;
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE void dec_ref() {
|
||||
--tstate->gilstate_counter;
|
||||
#if !defined(NDEBUG)
|
||||
if (detail::get_thread_state_unchecked() != tstate)
|
||||
pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!");
|
||||
if (tstate->gilstate_counter < 0)
|
||||
pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!");
|
||||
#endif
|
||||
if (tstate->gilstate_counter == 0) {
|
||||
#if !defined(NDEBUG)
|
||||
if (!release)
|
||||
pybind11_fail("scoped_acquire::dec_ref(): internal error!");
|
||||
#endif
|
||||
PyThreadState_Clear(tstate);
|
||||
if (active)
|
||||
PyThreadState_DeleteCurrent();
|
||||
PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate);
|
||||
release = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// This method will disable the PyThreadState_DeleteCurrent call and the
|
||||
/// GIL won't be acquired. This method should be used if the interpreter
|
||||
/// could be shutting down when this is called, as thread deletion is not
|
||||
/// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
|
||||
/// protect subsequent code.
|
||||
PYBIND11_NOINLINE void disarm() {
|
||||
active = false;
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE ~gil_scoped_acquire() {
|
||||
dec_ref();
|
||||
if (release)
|
||||
PyEval_SaveThread();
|
||||
}
|
||||
private:
|
||||
PyThreadState *tstate = nullptr;
|
||||
bool release = true;
|
||||
bool active = true;
|
||||
};
|
||||
|
||||
class gil_scoped_release {
|
||||
public:
|
||||
explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) {
|
||||
// `get_internals()` must be called here unconditionally in order to initialize
|
||||
// `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an
|
||||
// initialization race could occur as multiple threads try `gil_scoped_acquire`.
|
||||
auto &internals = detail::get_internals();
|
||||
tstate = PyEval_SaveThread();
|
||||
if (disassoc) {
|
||||
auto key = internals.tstate;
|
||||
PYBIND11_TLS_DELETE_VALUE(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// This method will disable the PyThreadState_DeleteCurrent call and the
|
||||
/// GIL won't be acquired. This method should be used if the interpreter
|
||||
/// could be shutting down when this is called, as thread deletion is not
|
||||
/// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
|
||||
/// protect subsequent code.
|
||||
PYBIND11_NOINLINE void disarm() {
|
||||
active = false;
|
||||
}
|
||||
|
||||
~gil_scoped_release() {
|
||||
if (!tstate)
|
||||
return;
|
||||
// `PyEval_RestoreThread()` should not be called if runtime is finalizing
|
||||
if (active)
|
||||
PyEval_RestoreThread(tstate);
|
||||
if (disassoc) {
|
||||
auto key = detail::get_internals().tstate;
|
||||
PYBIND11_TLS_REPLACE_VALUE(key, tstate);
|
||||
}
|
||||
}
|
||||
private:
|
||||
PyThreadState *tstate;
|
||||
bool disassoc;
|
||||
bool active = true;
|
||||
};
|
||||
#elif defined(PYPY_VERSION)
|
||||
class gil_scoped_acquire {
|
||||
PyGILState_STATE state;
|
||||
public:
|
||||
gil_scoped_acquire() { state = PyGILState_Ensure(); }
|
||||
~gil_scoped_acquire() { PyGILState_Release(state); }
|
||||
void disarm() {}
|
||||
};
|
||||
|
||||
class gil_scoped_release {
|
||||
PyThreadState *state;
|
||||
public:
|
||||
gil_scoped_release() { state = PyEval_SaveThread(); }
|
||||
~gil_scoped_release() { PyEval_RestoreThread(state); }
|
||||
void disarm() {}
|
||||
};
|
||||
#else
|
||||
class gil_scoped_acquire {
|
||||
void disarm() {}
|
||||
};
|
||||
class gil_scoped_release {
|
||||
void disarm() {}
|
||||
};
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
@@ -5,17 +5,31 @@
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
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
|
||||
|
||||
#include "pybind11.h"
|
||||
|
||||
#include <streambuf>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <streambuf>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
@@ -38,25 +52,68 @@ private:
|
||||
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
|
||||
// rare MSVC test failure shows up with this version, then this should be
|
||||
// simplified to a fully qualified call.
|
||||
// Computes how many bytes at the end of the buffer are part of an
|
||||
// incomplete sequence of UTF-8 bytes.
|
||||
// Precondition: pbase() < pptr()
|
||||
size_t utf8_remainder() const {
|
||||
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)
|
||||
remainder = 1; // 1-byte code point is impossible
|
||||
else if (dist == 1)
|
||||
remainder = is_leading_2b(*leading) ? 0 : dist + 1;
|
||||
else if (dist == 2)
|
||||
remainder = is_leading_3b(*leading) ? 0 : dist + 1;
|
||||
// else if (dist >= 3), at least 4 bytes before encountering an UTF-8
|
||||
// leading byte, either no remainder or invalid UTF-8.
|
||||
// Invalid UTF-8 will cause an exception later when converting
|
||||
// to a Python string, so that's not handled here.
|
||||
return remainder;
|
||||
}
|
||||
|
||||
// This function must be non-virtual to be called in a destructor.
|
||||
int _sync() {
|
||||
if (pbase() != pptr()) {
|
||||
|
||||
{
|
||||
gil_scoped_acquire tmp;
|
||||
|
||||
// This subtraction cannot be negative, so dropping the sign.
|
||||
str line(pbase(), static_cast<size_t>(pptr() - pbase()));
|
||||
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();
|
||||
|
||||
// Placed inside gil_scoped_aquire as a mutex to avoid a race
|
||||
setp(pbase(), epptr());
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
@@ -66,11 +123,8 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
pythonbuf(object pyostream, size_t buffer_size = 1024)
|
||||
: buf_size(buffer_size),
|
||||
d_buffer(new char[buf_size]),
|
||||
pywrite(pyostream.attr("write")),
|
||||
explicit pythonbuf(const object &pyostream, size_t buffer_size = 1024)
|
||||
: buf_size(buffer_size), d_buffer(new char[buf_size]), pywrite(pyostream.attr("write")),
|
||||
pyflush(pyostream.attr("flush")) {
|
||||
setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
|
||||
}
|
||||
@@ -117,9 +171,9 @@ protected:
|
||||
detail::pythonbuf buffer;
|
||||
|
||||
public:
|
||||
scoped_ostream_redirect(
|
||||
std::ostream &costream = std::cout,
|
||||
object pyostream = module_::import("sys").attr("stdout"))
|
||||
explicit scoped_ostream_redirect(std::ostream &costream = std::cout,
|
||||
const object &pyostream
|
||||
= module_::import("sys").attr("stdout"))
|
||||
: costream(costream), buffer(pyostream) {
|
||||
old = costream.rdbuf(&buffer);
|
||||
}
|
||||
@@ -148,10 +202,10 @@ public:
|
||||
\endrst */
|
||||
class scoped_estream_redirect : public scoped_ostream_redirect {
|
||||
public:
|
||||
scoped_estream_redirect(
|
||||
std::ostream &costream = std::cerr,
|
||||
object pyostream = module_::import("sys").attr("stderr"))
|
||||
: scoped_ostream_redirect(costream,pyostream) {}
|
||||
explicit scoped_estream_redirect(std::ostream &costream = std::cerr,
|
||||
const object &pyostream
|
||||
= module_::import("sys").attr("stderr"))
|
||||
: scoped_ostream_redirect(costream, pyostream) {}
|
||||
};
|
||||
|
||||
|
||||
@@ -165,7 +219,7 @@ class OstreamRedirect {
|
||||
std::unique_ptr<scoped_estream_redirect> redirect_stderr;
|
||||
|
||||
public:
|
||||
OstreamRedirect(bool do_stdout = true, bool do_stderr = true)
|
||||
explicit OstreamRedirect(bool do_stdout = true, bool do_stderr = true)
|
||||
: do_stdout_(do_stdout), do_stderr_(do_stderr) {}
|
||||
|
||||
void enter() {
|
||||
@@ -210,11 +264,12 @@ PYBIND11_NAMESPACE_END(detail)
|
||||
m.noisy_function_with_error_printing()
|
||||
|
||||
\endrst */
|
||||
inline class_<detail::OstreamRedirect> add_ostream_redirect(module_ m, std::string name = "ostream_redirect") {
|
||||
return class_<detail::OstreamRedirect>(m, name.c_str(), module_local())
|
||||
.def(init<bool,bool>(), arg("stdout")=true, arg("stderr")=true)
|
||||
inline class_<detail::OstreamRedirect>
|
||||
add_ostream_redirect(module_ m, const std::string &name = "ostream_redirect") {
|
||||
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("__exit__", [](detail::OstreamRedirect &self_, args) { self_.exit(); });
|
||||
.def("__exit__", [](detail::OstreamRedirect &self_, const args &) { self_.exit(); });
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
@@ -25,11 +25,6 @@
|
||||
#include <vector>
|
||||
#include <typeindex>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
||||
#endif
|
||||
|
||||
/* This will be true on all flat address space platforms and allows us to reduce the
|
||||
whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size
|
||||
and dimension types (e.g. shape, strides, indexing), instead of inflicting this
|
||||
@@ -104,7 +99,7 @@ struct numpy_internals {
|
||||
}
|
||||
};
|
||||
|
||||
inline PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) {
|
||||
PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) {
|
||||
ptr = &get_or_create_shared_data<numpy_internals>("_numpy_internals");
|
||||
}
|
||||
|
||||
@@ -164,10 +159,10 @@ struct npy_api {
|
||||
NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_),
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct PyArray_Dims {
|
||||
Py_intptr_t *ptr;
|
||||
int len;
|
||||
} PyArray_Dims;
|
||||
};
|
||||
|
||||
static npy_api& get() {
|
||||
static npy_api api = lookup();
|
||||
@@ -203,6 +198,9 @@ struct npy_api {
|
||||
// Unused. Not removed because that affects ABI of the class.
|
||||
int (*PyArray_SetBaseObject_)(PyObject *, PyObject *);
|
||||
PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int);
|
||||
PyObject* (*PyArray_Newshape_)(PyObject*, PyArray_Dims*, int);
|
||||
PyObject* (*PyArray_View_)(PyObject*, PyObject*, PyObject*);
|
||||
|
||||
private:
|
||||
enum functions {
|
||||
API_PyArray_GetNDArrayCFeatureVersion = 211,
|
||||
@@ -217,10 +215,12 @@ private:
|
||||
API_PyArray_NewCopy = 85,
|
||||
API_PyArray_NewFromDescr = 94,
|
||||
API_PyArray_DescrNewFromType = 96,
|
||||
API_PyArray_Newshape = 135,
|
||||
API_PyArray_Squeeze = 136,
|
||||
API_PyArray_View = 137,
|
||||
API_PyArray_DescrConverter = 174,
|
||||
API_PyArray_EquivTypes = 182,
|
||||
API_PyArray_GetArrayParamsFromObject = 278,
|
||||
API_PyArray_Squeeze = 136,
|
||||
API_PyArray_SetBaseObject = 282
|
||||
};
|
||||
|
||||
@@ -248,11 +248,14 @@ private:
|
||||
DECL_NPY_API(PyArray_NewCopy);
|
||||
DECL_NPY_API(PyArray_NewFromDescr);
|
||||
DECL_NPY_API(PyArray_DescrNewFromType);
|
||||
DECL_NPY_API(PyArray_Newshape);
|
||||
DECL_NPY_API(PyArray_Squeeze);
|
||||
DECL_NPY_API(PyArray_View);
|
||||
DECL_NPY_API(PyArray_DescrConverter);
|
||||
DECL_NPY_API(PyArray_EquivTypes);
|
||||
DECL_NPY_API(PyArray_GetArrayParamsFromObject);
|
||||
DECL_NPY_API(PyArray_Squeeze);
|
||||
DECL_NPY_API(PyArray_SetBaseObject);
|
||||
|
||||
#undef DECL_NPY_API
|
||||
return api;
|
||||
}
|
||||
@@ -319,14 +322,13 @@ template <typename T> using remove_all_extents_t = typename array_info<T>::type;
|
||||
|
||||
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
|
||||
#if !defined(__GNUG__) || defined(_LIBCPP_VERSION) || defined(_GLIBCXX_USE_CXX11_ABI)
|
||||
// _GLIBCXX_USE_CXX11_ABI indicates that we're using libstdc++ from GCC 5 or newer, independent
|
||||
// of the actual compiler (Clang can also use libstdc++, but it always defines __GNUC__ == 4).
|
||||
std::is_trivially_copyable<T>,
|
||||
#else
|
||||
// GCC 4 doesn't implement is_trivially_copyable, so approximate it
|
||||
#if defined(__GLIBCXX__) && (__GLIBCXX__ < 20150422 || __GLIBCXX__ == 20150426 || __GLIBCXX__ == 20150623 || __GLIBCXX__ == 20150626 || __GLIBCXX__ == 20160803)
|
||||
// libstdc++ < 5 (including versions 4.8.5, 4.9.3 and 4.9.4 which were released after 5)
|
||||
// don't implement is_trivially_copyable, so approximate it
|
||||
std::is_trivially_destructible<T>,
|
||||
satisfies_any_of<T, std::has_trivial_copy_constructor, std::has_trivial_copy_assign>,
|
||||
#else
|
||||
std::is_trivially_copyable<T>,
|
||||
#endif
|
||||
satisfies_none_of<T, std::is_reference, std::is_array, is_std_array, std::is_arithmetic, is_complex, std::is_enum>
|
||||
>;
|
||||
@@ -466,28 +468,30 @@ public:
|
||||
explicit dtype(const buffer_info &info) {
|
||||
dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format)));
|
||||
// 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) {
|
||||
m_ptr = from_args(pybind11::str(format)).release().ptr();
|
||||
}
|
||||
|
||||
dtype(const char *format) : dtype(std::string(format)) { }
|
||||
explicit dtype(const char *format) : dtype(std::string(format)) {}
|
||||
|
||||
dtype(list names, list formats, list offsets, ssize_t itemsize) {
|
||||
dict args;
|
||||
args["names"] = names;
|
||||
args["formats"] = formats;
|
||||
args["offsets"] = offsets;
|
||||
args["names"] = std::move(names);
|
||||
args["formats"] = std::move(formats);
|
||||
args["offsets"] = std::move(offsets);
|
||||
args["itemsize"] = pybind11::int_(itemsize);
|
||||
m_ptr = from_args(args).release().ptr();
|
||||
m_ptr = from_args(std::move(args)).release().ptr();
|
||||
}
|
||||
|
||||
/// This is essentially the same as calling numpy.dtype(args) in Python.
|
||||
static dtype from_args(object args) {
|
||||
PyObject *ptr = nullptr;
|
||||
if (!detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) || !ptr)
|
||||
if ((detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) == 0) || !ptr)
|
||||
throw error_already_set();
|
||||
return reinterpret_steal<dtype>(ptr);
|
||||
}
|
||||
@@ -507,11 +511,21 @@ public:
|
||||
return detail::array_descriptor_proxy(m_ptr)->names != nullptr;
|
||||
}
|
||||
|
||||
/// Single-character type code.
|
||||
/// Single-character code for dtype's kind.
|
||||
/// For example, floating point types are 'f' and integral types are 'i'.
|
||||
char kind() const {
|
||||
return detail::array_descriptor_proxy(m_ptr)->kind;
|
||||
}
|
||||
|
||||
/// Single-character for dtype's type.
|
||||
/// For example, ``float`` is 'f', ``double`` 'd', ``int`` 'i', and ``long`` 'l'.
|
||||
char char_() const {
|
||||
// Note: The signature, `dtype::char_` follows the naming of NumPy's
|
||||
// public Python API (i.e., ``dtype.char``), rather than its internal
|
||||
// C API (``PyArray_Descr::type``).
|
||||
return detail::array_descriptor_proxy(m_ptr)->type;
|
||||
}
|
||||
|
||||
private:
|
||||
static object _dtype_from_pep3118() {
|
||||
static PyObject *obj = module_::import("numpy.core._internal")
|
||||
@@ -533,7 +547,7 @@ private:
|
||||
auto name = spec[0].cast<pybind11::str>();
|
||||
auto format = spec[1].cast<tuple>()[0].cast<dtype>();
|
||||
auto offset = spec[1].cast<tuple>()[1].cast<pybind11::int_>();
|
||||
if (!len(name) && format.kind() == 'V')
|
||||
if ((len(name) == 0u) && format.kind() == 'V')
|
||||
continue;
|
||||
field_descriptors.push_back({(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset});
|
||||
}
|
||||
@@ -549,7 +563,7 @@ private:
|
||||
formats.append(descr.format);
|
||||
offsets.append(descr.offset);
|
||||
}
|
||||
return dtype(names, formats, offsets, itemsize);
|
||||
return dtype(std::move(names), std::move(formats), std::move(offsets), itemsize);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -736,7 +750,7 @@ public:
|
||||
* and the caller must take care not to access invalid dimensions or dimension indices.
|
||||
*/
|
||||
template <typename T, ssize_t Dims = -1> detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() & {
|
||||
if (Dims >= 0 && ndim() != Dims)
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Dims >= 0) && ndim() != Dims)
|
||||
throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) +
|
||||
"; expected " + std::to_string(Dims));
|
||||
return detail::unchecked_mutable_reference<T, Dims>(mutable_data(), shape(), strides(), ndim());
|
||||
@@ -750,7 +764,7 @@ public:
|
||||
* invalid dimensions or dimension indices.
|
||||
*/
|
||||
template <typename T, ssize_t Dims = -1> detail::unchecked_reference<T, Dims> unchecked() const & {
|
||||
if (Dims >= 0 && ndim() != Dims)
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Dims >= 0) && ndim() != Dims)
|
||||
throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) +
|
||||
"; expected " + std::to_string(Dims));
|
||||
return detail::unchecked_reference<T, Dims>(data(), shape(), strides(), ndim());
|
||||
@@ -779,6 +793,33 @@ public:
|
||||
if (isinstance<array>(new_array)) { *this = std::move(new_array); }
|
||||
}
|
||||
|
||||
/// Optional `order` parameter omitted, to be added as needed.
|
||||
array reshape(ShapeContainer new_shape) {
|
||||
detail::npy_api::PyArray_Dims d
|
||||
= {reinterpret_cast<Py_intptr_t *>(new_shape->data()), int(new_shape->size())};
|
||||
auto new_array
|
||||
= reinterpret_steal<array>(detail::npy_api::get().PyArray_Newshape_(m_ptr, &d, 0));
|
||||
if (!new_array) {
|
||||
throw error_already_set();
|
||||
}
|
||||
return new_array;
|
||||
}
|
||||
|
||||
/// Create a view of an array in a different data type.
|
||||
/// This function may fundamentally reinterpret the data in the array.
|
||||
/// It is the responsibility of the caller to ensure that this is safe.
|
||||
/// Only supports the `dtype` argument, the `type` argument is omitted,
|
||||
/// to be added as needed.
|
||||
array view(const std::string &dtype) {
|
||||
auto &api = detail::npy_api::get();
|
||||
auto new_view = reinterpret_steal<array>(api.PyArray_View_(
|
||||
m_ptr, dtype::from_args(pybind11::str(dtype)).release().ptr(), nullptr));
|
||||
if (!new_view) {
|
||||
throw error_already_set();
|
||||
}
|
||||
return new_view;
|
||||
}
|
||||
|
||||
/// Ensure that the argument is a NumPy array
|
||||
/// In case of an error, nullptr is returned and the Python error is cleared.
|
||||
static array ensure(handle h, int ExtraFlags = 0) {
|
||||
@@ -853,6 +894,7 @@ public:
|
||||
if (!is_borrowed) Py_XDECREF(h.ptr());
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) {
|
||||
if (!m_ptr) throw error_already_set();
|
||||
}
|
||||
@@ -863,11 +905,12 @@ public:
|
||||
: array(std::move(shape), std::move(strides), ptr, base) { }
|
||||
|
||||
explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle())
|
||||
: array_t(private_ctor{}, std::move(shape),
|
||||
ExtraFlags & f_style
|
||||
? detail::f_strides(*shape, itemsize())
|
||||
: detail::c_strides(*shape, itemsize()),
|
||||
ptr, base) { }
|
||||
: array_t(private_ctor{},
|
||||
std::move(shape),
|
||||
(ExtraFlags & f_style) != 0 ? detail::f_strides(*shape, itemsize())
|
||||
: detail::c_strides(*shape, itemsize()),
|
||||
ptr,
|
||||
base) {}
|
||||
|
||||
explicit array_t(ssize_t count, const T *ptr = nullptr, handle base = handle())
|
||||
: array({count}, {}, ptr, base) { }
|
||||
@@ -1020,7 +1063,10 @@ struct npy_format_descriptor_name<T, enable_if_t<std::is_integral<T>::value>> {
|
||||
|
||||
template <typename T>
|
||||
struct npy_format_descriptor_name<T, enable_if_t<std::is_floating_point<T>::value>> {
|
||||
static constexpr auto name = _<std::is_same<T, float>::value || std::is_same<T, double>::value>(
|
||||
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")
|
||||
);
|
||||
};
|
||||
@@ -1028,7 +1074,9 @@ struct npy_format_descriptor_name<T, enable_if_t<std::is_floating_point<T>::valu
|
||||
template <typename T>
|
||||
struct npy_format_descriptor_name<T, enable_if_t<is_complex<T>::value>> {
|
||||
static constexpr auto name = _<std::is_same<typename T::value_type, float>::value
|
||||
|| std::is_same<typename T::value_type, double>::value>(
|
||||
|| 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")
|
||||
);
|
||||
};
|
||||
@@ -1093,7 +1141,7 @@ struct field_descriptor {
|
||||
dtype descr;
|
||||
};
|
||||
|
||||
inline PYBIND11_NOINLINE void register_structured_dtype(
|
||||
PYBIND11_NOINLINE void register_structured_dtype(
|
||||
any_container<field_descriptor> fields,
|
||||
const std::type_info& tinfo, ssize_t itemsize,
|
||||
bool (*direct_converter)(PyObject *, void *&)) {
|
||||
@@ -1117,7 +1165,10 @@ inline PYBIND11_NOINLINE void register_structured_dtype(
|
||||
formats.append(field.descr);
|
||||
offsets.append(pybind11::int_(field.offset));
|
||||
}
|
||||
auto dtype_ptr = pybind11::dtype(names, formats, offsets, itemsize).release().ptr();
|
||||
auto dtype_ptr
|
||||
= pybind11::dtype(std::move(names), std::move(formats), std::move(offsets), itemsize)
|
||||
.release()
|
||||
.ptr();
|
||||
|
||||
// There is an existing bug in NumPy (as of v1.11): trailing bytes are
|
||||
// not encoded explicitly into the format string. This will supposedly
|
||||
@@ -1277,7 +1328,7 @@ public:
|
||||
using value_type = container_type::value_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)
|
||||
: p_ptr(reinterpret_cast<char*>(ptr)), m_strides(strides.size()) {
|
||||
@@ -1298,7 +1349,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
char* p_ptr;
|
||||
char *p_ptr{0};
|
||||
container_type m_strides;
|
||||
};
|
||||
|
||||
@@ -1326,9 +1377,8 @@ public:
|
||||
if (++m_index[i] != m_shape[i]) {
|
||||
increment_common_iterator(i);
|
||||
break;
|
||||
} else {
|
||||
m_index[i] = 0;
|
||||
}
|
||||
m_index[i] = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -1479,8 +1529,7 @@ struct vectorize_returned_array {
|
||||
static Type create(broadcast_trivial trivial, const std::vector<ssize_t> &shape) {
|
||||
if (trivial == broadcast_trivial::f_trivial)
|
||||
return array_t<Return, array::f_style>(shape);
|
||||
else
|
||||
return array_t<Return>(shape);
|
||||
return array_t<Return>(shape);
|
||||
}
|
||||
|
||||
static Return *mutable_data(Type &array) {
|
||||
@@ -1536,8 +1585,11 @@ private:
|
||||
"pybind11::vectorize(...) requires a function with at least one vectorizable argument");
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
explicit vectorize_helper(T &&f) : f(std::forward<T>(f)) { }
|
||||
template <typename T,
|
||||
// SFINAE to prevent shadowing the copy constructor.
|
||||
typename = detail::enable_if_t<
|
||||
!std::is_same<vectorize_helper, typename std::decay<T>::type>::value>>
|
||||
explicit vectorize_helper(T &&f) : f(std::forward<T>(f)) {}
|
||||
|
||||
object operator()(typename vectorize_arg<Args>::type... args) {
|
||||
return run(args...,
|
||||
@@ -1687,7 +1739,3 @@ Helper vectorize(Return (Class::*f)(Args...) const) {
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
@@ -11,13 +11,6 @@
|
||||
|
||||
#include "pybind11.h"
|
||||
|
||||
#if defined(__clang__) && !defined(__INTEL_COMPILER)
|
||||
# pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type()))
|
||||
#elif defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
@@ -58,7 +51,8 @@ template <op_id id, op_type ot, typename L, typename R> struct op_ {
|
||||
using op = op_impl<id, ot, Base, L_type, R_type>;
|
||||
cl.def(op::name(), &op::execute, is_operator(), extra...);
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (id == op_truediv || id == op_itruediv)
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(id == op_truediv) ||
|
||||
PYBIND11_SILENCE_MSVC_C4127(id == op_itruediv))
|
||||
cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__",
|
||||
&op::execute, is_operator(), extra...);
|
||||
#endif
|
||||
@@ -167,7 +161,3 @@ using detail::self;
|
||||
using detail::hash;
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,10 @@
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#if defined(PYBIND11_HAS_OPTIONAL)
|
||||
# include <optional>
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
/* A few forward declarations */
|
||||
@@ -24,7 +28,7 @@ struct arg; struct arg_v;
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
class args_proxy;
|
||||
inline bool isinstance_generic(handle obj, const std::type_info &tp);
|
||||
bool isinstance_generic(handle obj, const std::type_info &tp);
|
||||
|
||||
// Accessor forward declarations
|
||||
template <typename Policy> class accessor;
|
||||
@@ -178,6 +182,7 @@ public:
|
||||
/// The default constructor creates a handle with a ``nullptr``-valued pointer
|
||||
handle() = default;
|
||||
/// Creates a ``handle`` from the given raw Python object pointer
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
handle(PyObject *ptr) : m_ptr(ptr) { } // Allow implicit conversion from PyObject*
|
||||
|
||||
/// Return the underlying ``PyObject *`` pointer
|
||||
@@ -254,8 +259,11 @@ public:
|
||||
|
||||
object& operator=(const object &other) {
|
||||
other.inc_ref();
|
||||
dec_ref();
|
||||
// Use temporary variable to ensure `*this` remains valid while
|
||||
// `Py_XDECREF` executes, in case `*this` is accessible from Python.
|
||||
handle temp(m_ptr);
|
||||
m_ptr = other.m_ptr;
|
||||
temp.dec_ref();
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -279,8 +287,10 @@ protected:
|
||||
struct borrowed_t { };
|
||||
struct stolen_t { };
|
||||
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS // Issue in breathe 4.26.1
|
||||
template <typename T> friend T reinterpret_borrow(handle);
|
||||
template <typename T> friend T reinterpret_steal(handle);
|
||||
#endif
|
||||
|
||||
public:
|
||||
// Only accessible from derived classes and the reinterpret_* functions
|
||||
@@ -314,14 +324,18 @@ template <typename T> T reinterpret_borrow(handle h) { return {h, object::borrow
|
||||
template <typename T> T reinterpret_steal(handle h) { return {h, object::stolen_t{}}; }
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
inline std::string error_string();
|
||||
std::string error_string();
|
||||
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
|
||||
/// 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
|
||||
/// python).
|
||||
class error_already_set : public std::runtime_error {
|
||||
class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::runtime_error {
|
||||
public:
|
||||
/// Constructs a new exception from the current Python error indicator, if any. The current
|
||||
/// Python error indicator will be cleared.
|
||||
@@ -339,16 +353,17 @@ public:
|
||||
/// 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()); }
|
||||
|
||||
/// If it is impossible to raise the currently-held error, such as in destructor, we can write
|
||||
/// 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
|
||||
/// type and value of the error, so there is no need to repeat that. For example, __func__ could
|
||||
/// be helpful. After this call, the current object no longer stores the error variables,
|
||||
/// and neither does Python.
|
||||
/// 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
|
||||
/// 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. After this call, the current
|
||||
/// object no longer stores the error variables, and neither does Python.
|
||||
void discard_as_unraisable(object err_context) {
|
||||
restore();
|
||||
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) {
|
||||
discard_as_unraisable(reinterpret_steal<object>(PYBIND11_FROM_STRING(err_context)));
|
||||
}
|
||||
@@ -360,7 +375,9 @@ public:
|
||||
/// Check if the currently trapped error type matches the given Python exception class (or a
|
||||
/// subclass thereof). May also be passed a tuple to search for any exception class matches in
|
||||
/// the given tuple.
|
||||
bool matches(handle 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& value() const { return m_value; }
|
||||
@@ -369,6 +386,50 @@ public:
|
||||
private:
|
||||
object m_type, m_value, m_trace;
|
||||
};
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
|
||||
/// Replaces the current Python error indicator with the chosen error, performing a
|
||||
/// 'raise from' to indicate that the chosen error was caused by the original error.
|
||||
inline void raise_from(PyObject *type, const char *message) {
|
||||
// Based on _PyErr_FormatVFromCause:
|
||||
// https://github.com/python/cpython/blob/467ab194fc6189d9f7310c89937c51abeac56839/Python/errors.c#L405
|
||||
// See https://github.com/pybind/pybind11/pull/2112 for details.
|
||||
PyObject *exc = nullptr, *val = nullptr, *val2 = nullptr, *tb = nullptr;
|
||||
|
||||
assert(PyErr_Occurred());
|
||||
PyErr_Fetch(&exc, &val, &tb);
|
||||
PyErr_NormalizeException(&exc, &val, &tb);
|
||||
if (tb != nullptr) {
|
||||
PyException_SetTraceback(val, tb);
|
||||
Py_DECREF(tb);
|
||||
}
|
||||
Py_DECREF(exc);
|
||||
assert(!PyErr_Occurred());
|
||||
|
||||
PyErr_SetString(type, message);
|
||||
|
||||
PyErr_Fetch(&exc, &val2, &tb);
|
||||
PyErr_NormalizeException(&exc, &val2, &tb);
|
||||
Py_INCREF(val);
|
||||
PyException_SetCause(val2, val);
|
||||
PyException_SetContext(val2, val);
|
||||
PyErr_Restore(exc, val2, tb);
|
||||
}
|
||||
|
||||
/// Sets the current Python error indicator with the chosen error, performing a 'raise from'
|
||||
/// from the error contained in error_already_set to indicate that the chosen error was
|
||||
/// caused by the original error. After this function is called error_already_set will
|
||||
/// no longer contain an error.
|
||||
inline void raise_from(error_already_set& err, PyObject *type, const char *message) {
|
||||
err.restore();
|
||||
raise_from(type, message);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/** \defgroup python_builtins _
|
||||
Unless stated otherwise, the following C++ functions behave the same
|
||||
@@ -431,19 +492,17 @@ inline object getattr(handle obj, const char *name) {
|
||||
inline object getattr(handle obj, handle name, handle default_) {
|
||||
if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) {
|
||||
return reinterpret_steal<object>(result);
|
||||
} else {
|
||||
PyErr_Clear();
|
||||
return reinterpret_borrow<object>(default_);
|
||||
}
|
||||
PyErr_Clear();
|
||||
return reinterpret_borrow<object>(default_);
|
||||
}
|
||||
|
||||
inline object getattr(handle obj, const char *name, handle default_) {
|
||||
if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) {
|
||||
return reinterpret_steal<object>(result);
|
||||
} else {
|
||||
PyErr_Clear();
|
||||
return reinterpret_borrow<object>(default_);
|
||||
}
|
||||
PyErr_Clear();
|
||||
return reinterpret_borrow<object>(default_);
|
||||
}
|
||||
|
||||
inline void setattr(handle obj, handle name, handle value) {
|
||||
@@ -476,6 +535,43 @@ inline handle get_function(handle 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.
|
||||
// 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`.
|
||||
@@ -487,6 +583,10 @@ object object_or_cast(T &&o);
|
||||
// Match a PyObject*, which we want to convert directly to handle via its converting constructor
|
||||
inline handle object_or_cast(PyObject *ptr) { return ptr; }
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1920
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified
|
||||
#endif
|
||||
template <typename Policy>
|
||||
class accessor : public object_api<accessor<Policy>> {
|
||||
using key_type = typename Policy::key_type;
|
||||
@@ -494,7 +594,7 @@ class accessor : public object_api<accessor<Policy>> {
|
||||
public:
|
||||
accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) { }
|
||||
accessor(const accessor &) = default;
|
||||
accessor(accessor &&) = default;
|
||||
accessor(accessor &&) noexcept = default;
|
||||
|
||||
// accessor overload required to override default assignment operator (templates are not allowed
|
||||
// to replace default compiler-generated assignments).
|
||||
@@ -520,6 +620,7 @@ public:
|
||||
return obj.contains(key);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator object() const { return get_cache(); }
|
||||
PyObject *ptr() const { return get_cache().ptr(); }
|
||||
template <typename T> T cast() const { return get_cache().template cast<T>(); }
|
||||
@@ -535,6 +636,9 @@ private:
|
||||
key_type key;
|
||||
mutable object cache;
|
||||
};
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1920
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(accessor_policies)
|
||||
struct obj_attr {
|
||||
@@ -566,15 +670,17 @@ struct generic_item {
|
||||
struct sequence_item {
|
||||
using key_type = size_t;
|
||||
|
||||
static object get(handle obj, size_t index) {
|
||||
PyObject *result = PySequence_GetItem(obj.ptr(), static_cast<ssize_t>(index));
|
||||
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
|
||||
static object get(handle obj, const IdxType &index) {
|
||||
PyObject *result = PySequence_GetItem(obj.ptr(), ssize_t_cast(index));
|
||||
if (!result) { throw error_already_set(); }
|
||||
return reinterpret_steal<object>(result);
|
||||
}
|
||||
|
||||
static void set(handle obj, size_t index, handle val) {
|
||||
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
|
||||
static void set(handle obj, const IdxType &index, handle val) {
|
||||
// PySequence_SetItem does not steal a reference to 'val'
|
||||
if (PySequence_SetItem(obj.ptr(), static_cast<ssize_t>(index), val.ptr()) != 0) {
|
||||
if (PySequence_SetItem(obj.ptr(), ssize_t_cast(index), val.ptr()) != 0) {
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
@@ -583,15 +689,17 @@ struct sequence_item {
|
||||
struct list_item {
|
||||
using key_type = size_t;
|
||||
|
||||
static object get(handle obj, size_t index) {
|
||||
PyObject *result = PyList_GetItem(obj.ptr(), static_cast<ssize_t>(index));
|
||||
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
|
||||
static object get(handle obj, const IdxType &index) {
|
||||
PyObject *result = PyList_GetItem(obj.ptr(), ssize_t_cast(index));
|
||||
if (!result) { throw error_already_set(); }
|
||||
return reinterpret_borrow<object>(result);
|
||||
}
|
||||
|
||||
static void set(handle obj, size_t index, handle val) {
|
||||
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
|
||||
static void set(handle obj, const IdxType &index, handle val) {
|
||||
// PyList_SetItem steals a reference to 'val'
|
||||
if (PyList_SetItem(obj.ptr(), static_cast<ssize_t>(index), val.inc_ref().ptr()) != 0) {
|
||||
if (PyList_SetItem(obj.ptr(), ssize_t_cast(index), val.inc_ref().ptr()) != 0) {
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
@@ -600,15 +708,17 @@ struct list_item {
|
||||
struct tuple_item {
|
||||
using key_type = size_t;
|
||||
|
||||
static object get(handle obj, size_t index) {
|
||||
PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast<ssize_t>(index));
|
||||
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
|
||||
static object get(handle obj, const IdxType &index) {
|
||||
PyObject *result = PyTuple_GetItem(obj.ptr(), ssize_t_cast(index));
|
||||
if (!result) { throw error_already_set(); }
|
||||
return reinterpret_borrow<object>(result);
|
||||
}
|
||||
|
||||
static void set(handle obj, size_t index, handle val) {
|
||||
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
|
||||
static void set(handle obj, const IdxType &index, handle val) {
|
||||
// PyTuple_SetItem steals a reference to 'val'
|
||||
if (PyTuple_SetItem(obj.ptr(), static_cast<ssize_t>(index), val.inc_ref().ptr()) != 0) {
|
||||
if (PyTuple_SetItem(obj.ptr(), ssize_t_cast(index), val.inc_ref().ptr()) != 0) {
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
@@ -630,7 +740,9 @@ public:
|
||||
generic_iterator() = default;
|
||||
generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { }
|
||||
|
||||
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
|
||||
reference operator*() const { return Policy::dereference(); }
|
||||
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
|
||||
reference operator[](difference_type n) const { return *(*this + n); }
|
||||
pointer operator->() const { return **this; }
|
||||
|
||||
@@ -660,7 +772,8 @@ template <typename T>
|
||||
struct arrow_proxy {
|
||||
T value;
|
||||
|
||||
arrow_proxy(T &&value) : value(std::move(value)) { }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
arrow_proxy(T &&value) noexcept : value(std::move(value)) { }
|
||||
T *operator->() const { return &value; }
|
||||
};
|
||||
|
||||
@@ -669,11 +782,12 @@ class sequence_fast_readonly {
|
||||
protected:
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
using value_type = handle;
|
||||
using reference = const handle;
|
||||
using reference = const handle; // PR #3263
|
||||
using pointer = arrow_proxy<const handle>;
|
||||
|
||||
sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) { }
|
||||
|
||||
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
|
||||
reference dereference() const { return *ptr; }
|
||||
void increment() { ++ptr; }
|
||||
void decrement() { --ptr; }
|
||||
@@ -712,14 +826,19 @@ class dict_readonly {
|
||||
protected:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = std::pair<handle, handle>;
|
||||
using reference = const value_type;
|
||||
using reference = const value_type; // PR #3263
|
||||
using pointer = arrow_proxy<const value_type>;
|
||||
|
||||
dict_readonly() = default;
|
||||
dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); }
|
||||
|
||||
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
|
||||
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; }
|
||||
|
||||
private:
|
||||
@@ -745,16 +864,20 @@ inline bool PyIterable_Check(PyObject *obj) {
|
||||
if (iter) {
|
||||
Py_DECREF(iter);
|
||||
return true;
|
||||
} else {
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool PyNone_Check(PyObject *o) { return o == Py_None; }
|
||||
inline bool PyEllipsis_Check(PyObject *o) { return o == Py_Ellipsis; }
|
||||
|
||||
#ifdef PYBIND11_STR_LEGACY_PERMISSIVE
|
||||
inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); }
|
||||
#define PYBIND11_STR_CHECK_FUN detail::PyUnicode_Check_Permissive
|
||||
#else
|
||||
#define PYBIND11_STR_CHECK_FUN PyUnicode_Check
|
||||
#endif
|
||||
|
||||
inline bool PyStaticMethod_Check(PyObject *o) { return o->ob_type == &PyStaticMethod_Type; }
|
||||
|
||||
@@ -797,21 +920,28 @@ PYBIND11_NAMESPACE_END(detail)
|
||||
Name(handle h, borrowed_t) : Parent(h, borrowed_t{}) { } \
|
||||
Name(handle h, stolen_t) : Parent(h, stolen_t{}) { } \
|
||||
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()); } \
|
||||
template <typename Policy_> \
|
||||
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
|
||||
Name(const ::pybind11::detail::accessor<Policy_> &a) : Name(object(a)) { }
|
||||
|
||||
#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \
|
||||
PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
|
||||
/* This is deliberately not 'explicit' to allow implicit conversion from object: */ \
|
||||
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
|
||||
Name(const object &o) \
|
||||
: Parent(check_(o) ? o.inc_ref().ptr() : ConvertFun(o.ptr()), stolen_t{}) \
|
||||
{ if (!m_ptr) throw error_already_set(); } \
|
||||
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
|
||||
Name(object &&o) \
|
||||
: Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) \
|
||||
{ if (!m_ptr) throw error_already_set(); }
|
||||
|
||||
#define PYBIND11_OBJECT_CVT_DEFAULT(Name, Parent, CheckFun, ConvertFun) \
|
||||
PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \
|
||||
Name() : Parent() { }
|
||||
|
||||
#define PYBIND11_OBJECT_CHECK_FAILED(Name, o_ptr) \
|
||||
::pybind11::type_error("Object of type '" + \
|
||||
::pybind11::detail::get_fully_qualified_tp_name(Py_TYPE(o_ptr)) + \
|
||||
@@ -820,8 +950,10 @@ PYBIND11_NAMESPACE_END(detail)
|
||||
#define PYBIND11_OBJECT(Name, Parent, CheckFun) \
|
||||
PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
|
||||
/* This is deliberately not 'explicit' to allow implicit conversion from object: */ \
|
||||
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
|
||||
Name(const object &o) : Parent(o) \
|
||||
{ if (m_ptr && !check_(m_ptr)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, m_ptr); } \
|
||||
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
|
||||
Name(object &&o) : Parent(std::move(o)) \
|
||||
{ if (m_ptr && !check_(m_ptr)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, m_ptr); }
|
||||
|
||||
@@ -845,7 +977,7 @@ public:
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using difference_type = ssize_t;
|
||||
using value_type = handle;
|
||||
using reference = const handle;
|
||||
using reference = const handle; // PR #3263
|
||||
using pointer = const handle *;
|
||||
|
||||
PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check)
|
||||
@@ -861,6 +993,7 @@ public:
|
||||
return rv;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
|
||||
reference operator*() const {
|
||||
if (m_ptr && !value.ptr()) {
|
||||
auto& self = const_cast<iterator &>(*this);
|
||||
@@ -934,19 +1067,22 @@ class bytes;
|
||||
|
||||
class str : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(str, object, detail::PyUnicode_Check_Permissive, raw_str)
|
||||
PYBIND11_OBJECT_CVT(str, object, PYBIND11_STR_CHECK_FUN, raw_str)
|
||||
|
||||
str(const char *c, size_t n)
|
||||
: object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen_t{}) {
|
||||
template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
|
||||
str(const char *c, const SzType &n)
|
||||
: object(PyUnicode_FromStringAndSize(c, ssize_t_cast(n)), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate string object!");
|
||||
}
|
||||
|
||||
// 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
str(const char *c = "")
|
||||
: object(PyUnicode_FromString(c), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate string object!");
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
str(const std::string &s) : str(s.data(), s.size()) { }
|
||||
|
||||
explicit str(const bytes &b);
|
||||
@@ -957,15 +1093,16 @@ public:
|
||||
\endrst */
|
||||
explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { if (!m_ptr) throw error_already_set(); }
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator std::string() const {
|
||||
object temp = *this;
|
||||
if (PyUnicode_Check(m_ptr)) {
|
||||
temp = reinterpret_steal<object>(PyUnicode_AsUTF8String(m_ptr));
|
||||
if (!temp)
|
||||
pybind11_fail("Unable to extract string contents! (encoding issue)");
|
||||
throw error_already_set();
|
||||
}
|
||||
char *buffer;
|
||||
ssize_t length;
|
||||
char *buffer = nullptr;
|
||||
ssize_t length = 0;
|
||||
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length))
|
||||
pybind11_fail("Unable to extract string contents! (invalid type)");
|
||||
return std::string(buffer, (size_t) length);
|
||||
@@ -1004,24 +1141,28 @@ public:
|
||||
PYBIND11_OBJECT(bytes, object, PYBIND11_BYTES_CHECK)
|
||||
|
||||
// Allow implicit conversion:
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
bytes(const char *c = "")
|
||||
: object(PYBIND11_BYTES_FROM_STRING(c), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
|
||||
}
|
||||
|
||||
bytes(const char *c, size_t n)
|
||||
: object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen_t{}) {
|
||||
template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
|
||||
bytes(const char *c, const SzType &n)
|
||||
: object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, ssize_t_cast(n)), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
|
||||
}
|
||||
|
||||
// Allow implicit conversion:
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
bytes(const std::string &s) : bytes(s.data(), s.size()) { }
|
||||
|
||||
explicit bytes(const pybind11::str &s);
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator std::string() const {
|
||||
char *buffer;
|
||||
ssize_t length;
|
||||
char *buffer = nullptr;
|
||||
ssize_t length = 0;
|
||||
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length))
|
||||
pybind11_fail("Unable to extract bytes contents!");
|
||||
return std::string(buffer, (size_t) length);
|
||||
@@ -1038,8 +1179,8 @@ inline bytes::bytes(const pybind11::str &s) {
|
||||
if (!temp)
|
||||
pybind11_fail("Unable to extract string contents! (encoding issue)");
|
||||
}
|
||||
char *buffer;
|
||||
ssize_t length;
|
||||
char *buffer = nullptr;
|
||||
ssize_t length = 0;
|
||||
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length))
|
||||
pybind11_fail("Unable to extract string contents! (invalid type)");
|
||||
auto obj = reinterpret_steal<object>(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length));
|
||||
@@ -1049,16 +1190,45 @@ inline bytes::bytes(const pybind11::str &s) {
|
||||
}
|
||||
|
||||
inline str::str(const bytes& b) {
|
||||
char *buffer;
|
||||
ssize_t length;
|
||||
char *buffer = nullptr;
|
||||
ssize_t length = 0;
|
||||
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length))
|
||||
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, length));
|
||||
if (!obj)
|
||||
pybind11_fail("Could not allocate string object!");
|
||||
m_ptr = obj.release().ptr();
|
||||
}
|
||||
|
||||
/// \addtogroup pytypes
|
||||
/// @{
|
||||
class bytearray : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(bytearray, object, PyByteArray_Check, PyByteArray_FromObject)
|
||||
|
||||
template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
|
||||
bytearray(const char *c, const SzType &n)
|
||||
: object(PyByteArray_FromStringAndSize(c, ssize_t_cast(n)), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate bytearray object!");
|
||||
}
|
||||
|
||||
bytearray()
|
||||
: bytearray("", 0) {}
|
||||
|
||||
explicit bytearray(const std::string &s) : bytearray(s.data(), s.size()) { }
|
||||
|
||||
size_t size() const { return static_cast<size_t>(PyByteArray_Size(m_ptr)); }
|
||||
|
||||
explicit operator std::string() const {
|
||||
char *buffer = PyByteArray_AS_STRING(m_ptr);
|
||||
ssize_t size = PyByteArray_GET_SIZE(m_ptr);
|
||||
return std::string(buffer, static_cast<size_t>(size));
|
||||
}
|
||||
};
|
||||
// Note: breathe >= 4.17.0 will fail to build docs if the below two constructors
|
||||
// are included in the doxygen group; close here and reopen after as a workaround
|
||||
/// @} pytypes
|
||||
|
||||
/// \addtogroup pytypes
|
||||
/// @{
|
||||
class none : public object {
|
||||
@@ -1078,15 +1248,17 @@ public:
|
||||
PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool)
|
||||
bool_() : object(Py_False, borrowed_t{}) { }
|
||||
// Allow implicit conversion from and to `bool`:
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) { }
|
||||
operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator bool() const { return (m_ptr != nullptr) && PyLong_AsLong(m_ptr) != 0; }
|
||||
|
||||
private:
|
||||
/// Return the truth value of an object -- always returns a new reference
|
||||
static PyObject *raw_bool(PyObject *op) {
|
||||
const auto value = PyObject_IsTrue(op);
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1097,18 +1269,16 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
// unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes).
|
||||
template <typename Unsigned>
|
||||
Unsigned as_unsigned(PyObject *o) {
|
||||
if (sizeof(Unsigned) <= sizeof(unsigned long)
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(Unsigned) <= sizeof(unsigned long))
|
||||
#if PY_VERSION_HEX < 0x03000000
|
||||
|| PyInt_Check(o)
|
||||
|| PyInt_Check(o)
|
||||
#endif
|
||||
) {
|
||||
unsigned long v = PyLong_AsUnsignedLong(o);
|
||||
return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v;
|
||||
}
|
||||
else {
|
||||
unsigned long long v = PyLong_AsUnsignedLongLong(o);
|
||||
return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v;
|
||||
}
|
||||
unsigned long long v = PyLong_AsUnsignedLongLong(o);
|
||||
return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v;
|
||||
}
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
@@ -1119,8 +1289,9 @@ public:
|
||||
// Allow implicit conversion from C++ integral types:
|
||||
template <typename T,
|
||||
detail::enable_if_t<std::is_integral<T>::value, int> = 0>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
int_(T value) {
|
||||
if (sizeof(T) <= sizeof(long)) {
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(T) <= sizeof(long))) {
|
||||
if (std::is_signed<T>::value)
|
||||
m_ptr = PyLong_FromLong((long) value);
|
||||
else
|
||||
@@ -1136,6 +1307,7 @@ public:
|
||||
|
||||
template <typename T,
|
||||
detail::enable_if_t<std::is_integral<T>::value, int> = 0>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator T() const {
|
||||
return std::is_unsigned<T>::value
|
||||
? detail::as_unsigned<T>(m_ptr)
|
||||
@@ -1149,33 +1321,51 @@ class float_ : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float)
|
||||
// Allow implicit conversion from float/double:
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
float_(float value) : object(PyFloat_FromDouble((double) value), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate float object!");
|
||||
}
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
float_(double value = .0) : object(PyFloat_FromDouble((double) value), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate float object!");
|
||||
}
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator float() const { return (float) PyFloat_AsDouble(m_ptr); }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator double() const { return (double) PyFloat_AsDouble(m_ptr); }
|
||||
};
|
||||
|
||||
class weakref : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check)
|
||||
PYBIND11_OBJECT_CVT_DEFAULT(weakref, object, PyWeakref_Check, raw_weakref)
|
||||
explicit weakref(handle obj, handle callback = {})
|
||||
: object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate weak reference!");
|
||||
}
|
||||
|
||||
private:
|
||||
static PyObject *raw_weakref(PyObject *o) {
|
||||
return PyWeakref_NewRef(o, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
class slice : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check)
|
||||
slice(ssize_t start_, ssize_t stop_, ssize_t step_) {
|
||||
int_ start(start_), stop(stop_), step(step_);
|
||||
slice(handle start, handle stop, handle step) {
|
||||
m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr());
|
||||
if (!m_ptr) pybind11_fail("Could not allocate slice object!");
|
||||
if (!m_ptr)
|
||||
pybind11_fail("Could not allocate slice object!");
|
||||
}
|
||||
|
||||
#ifdef PYBIND11_HAS_OPTIONAL
|
||||
slice(std::optional<ssize_t> start, std::optional<ssize_t> stop, std::optional<ssize_t> step)
|
||||
: slice(index_to_object(start), index_to_object(stop), index_to_object(step)) {}
|
||||
#else
|
||||
slice(ssize_t start_, ssize_t stop_, ssize_t step_)
|
||||
: slice(int_(start_), int_(stop_), int_(step_)) {}
|
||||
#endif
|
||||
|
||||
bool compute(size_t length, size_t *start, size_t *stop, size_t *step,
|
||||
size_t *slicelength) const {
|
||||
return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr,
|
||||
@@ -1190,6 +1380,12 @@ public:
|
||||
stop, step,
|
||||
slicelength) == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
static object index_to_object(T index) {
|
||||
return index ? object(int_(*index)) : object(none());
|
||||
}
|
||||
};
|
||||
|
||||
class capsule : public object {
|
||||
@@ -1225,7 +1421,7 @@ public:
|
||||
pybind11_fail("Could not set capsule context!");
|
||||
}
|
||||
|
||||
capsule(void (*destructor)()) {
|
||||
explicit capsule(void (*destructor)()) {
|
||||
m_ptr = PyCapsule_New(reinterpret_cast<void *>(destructor), nullptr, [](PyObject *o) {
|
||||
auto destructor = reinterpret_cast<void (*)()>(PyCapsule_GetPointer(o, nullptr));
|
||||
destructor();
|
||||
@@ -1235,6 +1431,7 @@ public:
|
||||
pybind11_fail("Could not allocate capsule object!");
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
template <typename T> operator T *() const {
|
||||
return get_pointer<T>();
|
||||
}
|
||||
@@ -1244,14 +1441,19 @@ public:
|
||||
T* get_pointer() const {
|
||||
auto name = this->name();
|
||||
T *result = static_cast<T *>(PyCapsule_GetPointer(m_ptr, name));
|
||||
if (!result) pybind11_fail("Unable to extract capsule contents!");
|
||||
if (!result) {
|
||||
PyErr_Clear();
|
||||
pybind11_fail("Unable to extract capsule contents!");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Replaces a capsule's pointer *without* calling the destructor on the existing one.
|
||||
void set_pointer(const void *value) {
|
||||
if (PyCapsule_SetPointer(m_ptr, const_cast<void *>(value)) != 0)
|
||||
if (PyCapsule_SetPointer(m_ptr, const_cast<void *>(value)) != 0) {
|
||||
PyErr_Clear();
|
||||
pybind11_fail("Could not set capsule pointer");
|
||||
}
|
||||
}
|
||||
|
||||
const char *name() const { return PyCapsule_GetName(m_ptr); }
|
||||
@@ -1260,7 +1462,10 @@ public:
|
||||
class tuple : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple)
|
||||
explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen_t{}) {
|
||||
template <typename SzType = ssize_t,
|
||||
detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
|
||||
// Some compilers generate link errors when using `const SzType &` here:
|
||||
explicit tuple(SzType size = 0) : object(PyTuple_New(ssize_t_cast(size)), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate tuple object!");
|
||||
}
|
||||
size_t size() const { return (size_t) PyTuple_Size(m_ptr); }
|
||||
@@ -1296,7 +1501,7 @@ public:
|
||||
bool empty() const { return size() == 0; }
|
||||
detail::dict_iterator begin() const { return {*this, 0}; }
|
||||
detail::dict_iterator end() const { return {}; }
|
||||
void clear() const { PyDict_Clear(ptr()); }
|
||||
void clear() /* py-non-const */ { PyDict_Clear(ptr()); }
|
||||
template <typename T> bool contains(T &&key) const {
|
||||
return PyDict_Contains(m_ptr, detail::object_or_cast(std::forward<T>(key)).ptr()) == 1;
|
||||
}
|
||||
@@ -1329,7 +1534,10 @@ public:
|
||||
class list : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List)
|
||||
explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen_t{}) {
|
||||
template <typename SzType = ssize_t,
|
||||
detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
|
||||
// Some compilers generate link errors when using `const SzType &` here:
|
||||
explicit list(SzType size = 0) : object(PyList_New(ssize_t_cast(size)), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate list object!");
|
||||
}
|
||||
size_t size() const { return (size_t) PyList_Size(m_ptr); }
|
||||
@@ -1338,12 +1546,15 @@ public:
|
||||
detail::item_accessor operator[](handle h) const { return object::operator[](h); }
|
||||
detail::list_iterator begin() const { return {*this, 0}; }
|
||||
detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; }
|
||||
template <typename T> void append(T &&val) const {
|
||||
template <typename T> void append(T &&val) /* py-non-const */ {
|
||||
PyList_Append(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr());
|
||||
}
|
||||
template <typename T> void insert(size_t index, T &&val) const {
|
||||
PyList_Insert(m_ptr, static_cast<ssize_t>(index),
|
||||
detail::object_or_cast(std::forward<T>(val)).ptr());
|
||||
template <typename IdxType,
|
||||
typename ValType,
|
||||
detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
|
||||
void insert(const IdxType &index, ValType &&val) /* py-non-const */ {
|
||||
PyList_Insert(
|
||||
m_ptr, ssize_t_cast(index), detail::object_or_cast(std::forward<ValType>(val)).ptr());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1358,10 +1569,10 @@ public:
|
||||
}
|
||||
size_t size() const { return (size_t) PySet_Size(m_ptr); }
|
||||
bool empty() const { return size() == 0; }
|
||||
template <typename T> bool add(T &&val) const {
|
||||
template <typename T> bool add(T &&val) /* py-non-const */ {
|
||||
return PySet_Add(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 0;
|
||||
}
|
||||
void clear() const { PySet_Clear(m_ptr); }
|
||||
void clear() /* py-non-const */ { PySet_Clear(m_ptr); }
|
||||
template <typename T> bool contains(T &&val) const {
|
||||
return PySet_Contains(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 1;
|
||||
}
|
||||
@@ -1457,7 +1668,7 @@ public:
|
||||
detail::any_container<ssize_t> shape,
|
||||
detail::any_container<ssize_t> strides) {
|
||||
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>
|
||||
@@ -1514,7 +1725,7 @@ inline memoryview memoryview::from_buffer(
|
||||
size_t ndim = shape->size();
|
||||
if (ndim != strides->size())
|
||||
pybind11_fail("memoryview: shape length doesn't match strides length");
|
||||
ssize_t size = ndim ? 1 : 0;
|
||||
ssize_t size = ndim != 0u ? 1 : 0;
|
||||
for (size_t i = 0; i < ndim; ++i)
|
||||
size *= (*shape)[i];
|
||||
Py_buffer view;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "detail/common.h"
|
||||
#include "pybind11.h"
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
@@ -19,33 +20,15 @@
|
||||
#include <deque>
|
||||
#include <valarray>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
||||
// See `detail/common.h` for implementation of these guards.
|
||||
#if defined(PYBIND11_HAS_OPTIONAL)
|
||||
# include <optional>
|
||||
#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
|
||||
# include <experimental/optional>
|
||||
#endif
|
||||
|
||||
#ifdef __has_include
|
||||
// std::optional (but including it in c++14 mode isn't allowed)
|
||||
# if defined(PYBIND11_CPP17) && __has_include(<optional>)
|
||||
# include <optional>
|
||||
# define PYBIND11_HAS_OPTIONAL 1
|
||||
# endif
|
||||
// std::experimental::optional (but not allowed in c++11 mode)
|
||||
# if defined(PYBIND11_CPP14) && (__has_include(<experimental/optional>) && \
|
||||
!__has_include(<optional>))
|
||||
# include <experimental/optional>
|
||||
# define PYBIND11_HAS_EXP_OPTIONAL 1
|
||||
# endif
|
||||
// std::variant
|
||||
# if defined(PYBIND11_CPP17) && __has_include(<variant>)
|
||||
# include <variant>
|
||||
# define PYBIND11_HAS_VARIANT 1
|
||||
# endif
|
||||
#elif defined(_MSC_VER) && defined(PYBIND11_CPP17)
|
||||
# include <optional>
|
||||
#if defined(PYBIND11_HAS_VARIANT)
|
||||
# include <variant>
|
||||
# define PYBIND11_HAS_OPTIONAL 1
|
||||
# define PYBIND11_HAS_VARIANT 1
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
@@ -144,7 +127,7 @@ template <typename Type, typename Value> struct list_caster {
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<sequence>(src) || isinstance<str>(src))
|
||||
if (!isinstance<sequence>(src) || isinstance<bytes>(src) || isinstance<str>(src))
|
||||
return false;
|
||||
auto s = reinterpret_borrow<sequence>(src);
|
||||
value.clear();
|
||||
@@ -159,10 +142,13 @@ template <typename Type, typename Value> struct list_caster {
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T = Type,
|
||||
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(sequence, void *) { }
|
||||
template <
|
||||
typename T = Type,
|
||||
enable_if_t<std::is_same<decltype(std::declval<T>().reserve(0)), void>::value, int> = 0>
|
||||
void reserve_maybe(const sequence &s, Type *) {
|
||||
value.reserve(s.size());
|
||||
}
|
||||
void reserve_maybe(const sequence &, void *) {}
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
@@ -170,12 +156,12 @@ public:
|
||||
if (!std::is_lvalue_reference<T>::value)
|
||||
policy = return_value_policy_override<Value>::policy(policy);
|
||||
list l(src.size());
|
||||
size_t index = 0;
|
||||
ssize_t index = 0;
|
||||
for (auto &&value : src) {
|
||||
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_)
|
||||
return handle();
|
||||
PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference
|
||||
PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
|
||||
}
|
||||
return l.release();
|
||||
}
|
||||
@@ -227,12 +213,12 @@ public:
|
||||
template <typename T>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
list l(src.size());
|
||||
size_t index = 0;
|
||||
ssize_t index = 0;
|
||||
for (auto &&value : src) {
|
||||
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_)
|
||||
return handle();
|
||||
PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference
|
||||
PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
|
||||
}
|
||||
return l.release();
|
||||
}
|
||||
@@ -259,34 +245,35 @@ template <typename Key, typename Value, typename Hash, typename Equal, typename
|
||||
: map_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>, Key, Value> { };
|
||||
|
||||
// This type caster is intended to be used for std::optional and std::experimental::optional
|
||||
template<typename T> struct optional_caster {
|
||||
using value_conv = make_caster<typename T::value_type>;
|
||||
template<typename Type, typename Value = typename Type::value_type> struct optional_caster {
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
template <typename T_>
|
||||
static handle cast(T_ &&src, return_value_policy policy, handle parent) {
|
||||
template <typename T>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
if (!src)
|
||||
return none().inc_ref();
|
||||
if (!std::is_lvalue_reference<T>::value) {
|
||||
policy = return_value_policy_override<T>::policy(policy);
|
||||
policy = return_value_policy_override<Value>::policy(policy);
|
||||
}
|
||||
return value_conv::cast(*std::forward<T_>(src), policy, parent);
|
||||
return value_conv::cast(*std::forward<T>(src), policy, parent);
|
||||
}
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
if (!src) {
|
||||
return false;
|
||||
} else if (src.is_none()) {
|
||||
}
|
||||
if (src.is_none()) {
|
||||
return true; // default-constructed value is already empty
|
||||
}
|
||||
value_conv inner_caster;
|
||||
if (!inner_caster.load(src, convert))
|
||||
return false;
|
||||
|
||||
value.emplace(cast_op<typename T::value_type &&>(std::move(inner_caster)));
|
||||
value.emplace(cast_op<Value &&>(std::move(inner_caster)));
|
||||
return true;
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name + _("]"));
|
||||
PYBIND11_TYPE_CASTER(Type, _("Optional[") + value_conv::name + _("]"));
|
||||
};
|
||||
|
||||
#if defined(PYBIND11_HAS_OPTIONAL)
|
||||
@@ -377,12 +364,12 @@ struct type_caster<std::variant<Ts...>> : variant_caster<std::variant<Ts...>> {
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
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);
|
||||
#endif
|
||||
return os;
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
103
ext/pybind11/include/pybind11/stl/filesystem.h
Normal file
103
ext/pybind11/include/pybind11/stl/filesystem.h
Normal 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)
|
||||
@@ -128,11 +128,11 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
arg("x"),
|
||||
"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());
|
||||
v->reserve(len_hint(it));
|
||||
for (handle h : it)
|
||||
v->push_back(h.cast<T>());
|
||||
v->push_back(h.cast<T>());
|
||||
return v.release();
|
||||
}));
|
||||
|
||||
@@ -151,27 +151,28 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
"Extend the list by appending all the items in the given list"
|
||||
);
|
||||
|
||||
cl.def("extend",
|
||||
[](Vector &v, iterable it) {
|
||||
const size_t old_size = v.size();
|
||||
v.reserve(old_size + len_hint(it));
|
||||
try {
|
||||
for (handle h : it) {
|
||||
v.push_back(h.cast<T>());
|
||||
}
|
||||
} catch (const cast_error &) {
|
||||
v.erase(v.begin() + static_cast<typename Vector::difference_type>(old_size), v.end());
|
||||
try {
|
||||
v.shrink_to_fit();
|
||||
} catch (const std::exception &) {
|
||||
// Do nothing
|
||||
}
|
||||
throw;
|
||||
}
|
||||
},
|
||||
arg("L"),
|
||||
"Extend the list by appending all the items in the given list"
|
||||
);
|
||||
cl.def(
|
||||
"extend",
|
||||
[](Vector &v, const iterable &it) {
|
||||
const size_t old_size = v.size();
|
||||
v.reserve(old_size + len_hint(it));
|
||||
try {
|
||||
for (handle h : it) {
|
||||
v.push_back(h.cast<T>());
|
||||
}
|
||||
} catch (const cast_error &) {
|
||||
v.erase(v.begin() + static_cast<typename Vector::difference_type>(old_size),
|
||||
v.end());
|
||||
try {
|
||||
v.shrink_to_fit();
|
||||
} catch (const std::exception &) {
|
||||
// Do nothing
|
||||
}
|
||||
throw;
|
||||
}
|
||||
},
|
||||
arg("L"),
|
||||
"Extend the list by appending all the items in the given list");
|
||||
|
||||
cl.def("insert",
|
||||
[](Vector &v, DiffType i, const T &x) {
|
||||
@@ -190,7 +191,7 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
[](Vector &v) {
|
||||
if (v.empty())
|
||||
throw index_error();
|
||||
T t = v.back();
|
||||
T t = std::move(v.back());
|
||||
v.pop_back();
|
||||
return t;
|
||||
},
|
||||
@@ -200,8 +201,8 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
cl.def("pop",
|
||||
[wrap_i](Vector &v, DiffType i) {
|
||||
i = wrap_i(i, v.size());
|
||||
T t = v[(SizeType) i];
|
||||
v.erase(v.begin() + i);
|
||||
T t = std::move(v[(SizeType) i]);
|
||||
v.erase(std::next(v.begin(), i));
|
||||
return t;
|
||||
},
|
||||
arg("i"),
|
||||
@@ -216,9 +217,10 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
);
|
||||
|
||||
/// Slicing protocol
|
||||
cl.def("__getitem__",
|
||||
cl.def(
|
||||
"__getitem__",
|
||||
[](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))
|
||||
throw error_already_set();
|
||||
@@ -233,12 +235,12 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
return seq;
|
||||
},
|
||||
arg("s"),
|
||||
"Retrieve list elements using a slice object"
|
||||
);
|
||||
"Retrieve list elements using a slice object");
|
||||
|
||||
cl.def("__setitem__",
|
||||
[](Vector &v, slice slice, const Vector &value) {
|
||||
size_t start, stop, step, slicelength;
|
||||
cl.def(
|
||||
"__setitem__",
|
||||
[](Vector &v, slice slice, const Vector &value) {
|
||||
size_t start = 0, stop = 0, step = 0, slicelength = 0;
|
||||
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength))
|
||||
throw error_already_set();
|
||||
|
||||
@@ -250,8 +252,7 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
start += step;
|
||||
}
|
||||
},
|
||||
"Assign list elements using a slice object"
|
||||
);
|
||||
"Assign list elements using a slice object");
|
||||
|
||||
cl.def("__delitem__",
|
||||
[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``"
|
||||
);
|
||||
|
||||
cl.def("__delitem__",
|
||||
cl.def(
|
||||
"__delitem__",
|
||||
[](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))
|
||||
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>),
|
||||
@@ -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)});
|
||||
});
|
||||
|
||||
cl.def(init([](buffer buf) {
|
||||
cl.def(init([](const buffer &buf) {
|
||||
auto info = buf.request();
|
||||
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");
|
||||
@@ -413,13 +413,12 @@ void vector_buffer_impl(Class_& cl, std::true_type) {
|
||||
if (step == 1) {
|
||||
return Vector(p, end);
|
||||
}
|
||||
else {
|
||||
Vector vec;
|
||||
vec.reserve((size_t) info.shape[0]);
|
||||
for (; p != end; p += step)
|
||||
vec.push_back(*p);
|
||||
return vec;
|
||||
}
|
||||
Vector vec;
|
||||
vec.reserve((size_t) info.shape[0]);
|
||||
for (; p != end; p += step)
|
||||
vec.push_back(*p);
|
||||
return vec;
|
||||
|
||||
}));
|
||||
|
||||
return;
|
||||
@@ -596,6 +595,23 @@ template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ &
|
||||
);
|
||||
}
|
||||
|
||||
template<typename Map>
|
||||
struct keys_view
|
||||
{
|
||||
Map ↦
|
||||
};
|
||||
|
||||
template<typename Map>
|
||||
struct values_view
|
||||
{
|
||||
Map ↦
|
||||
};
|
||||
|
||||
template<typename Map>
|
||||
struct items_view
|
||||
{
|
||||
Map ↦
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
@@ -603,6 +619,9 @@ template <typename Map, typename holder_type = std::unique_ptr<Map>, typename...
|
||||
class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&... args) {
|
||||
using KeyType = typename Map::key_type;
|
||||
using MappedType = typename Map::mapped_type;
|
||||
using KeysView = detail::keys_view<Map>;
|
||||
using ValuesView = detail::values_view<Map>;
|
||||
using ItemsView = detail::items_view<Map>;
|
||||
using Class_ = class_<Map, holder_type>;
|
||||
|
||||
// If either type is a non-module-local bound type then make the map binding non-local as well;
|
||||
@@ -616,6 +635,12 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
|
||||
}
|
||||
|
||||
Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...);
|
||||
class_<KeysView> keys_view(
|
||||
scope, ("KeysView[" + name + "]").c_str(), pybind11::module_local(local));
|
||||
class_<ValuesView> values_view(
|
||||
scope, ("ValuesView[" + name + "]").c_str(), pybind11::module_local(local));
|
||||
class_<ItemsView> items_view(
|
||||
scope, ("ItemsView[" + name + "]").c_str(), pybind11::module_local(local));
|
||||
|
||||
cl.def(init<>());
|
||||
|
||||
@@ -629,12 +654,22 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
|
||||
|
||||
cl.def("__iter__",
|
||||
[](Map &m) { return make_key_iterator(m.begin(), m.end()); },
|
||||
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while iterator exists */
|
||||
);
|
||||
|
||||
cl.def("keys",
|
||||
[](Map &m) { return KeysView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
cl.def("values",
|
||||
[](Map &m) { return ValuesView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
cl.def("items",
|
||||
[](Map &m) { return make_iterator(m.begin(), m.end()); },
|
||||
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||
[](Map &m) { return ItemsView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
cl.def("__getitem__",
|
||||
@@ -655,6 +690,8 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
|
||||
return true;
|
||||
}
|
||||
);
|
||||
// Fallback for when the object is not of the key type
|
||||
cl.def("__contains__", [](Map &, const object &) -> bool { return false; });
|
||||
|
||||
// Assignment provided only if the type is copyable
|
||||
detail::map_assignment<Map, Class_>(cl);
|
||||
@@ -670,6 +707,40 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
|
||||
|
||||
cl.def("__len__", &Map::size);
|
||||
|
||||
keys_view.def("__len__", [](KeysView &view) { return view.map.size(); });
|
||||
keys_view.def("__iter__",
|
||||
[](KeysView &view) {
|
||||
return make_key_iterator(view.map.begin(), view.map.end());
|
||||
},
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
keys_view.def("__contains__",
|
||||
[](KeysView &view, const KeyType &k) -> bool {
|
||||
auto it = view.map.find(k);
|
||||
if (it == view.map.end())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
);
|
||||
// Fallback for when the object is not of the key type
|
||||
keys_view.def("__contains__", [](KeysView &, const object &) -> bool { return false; });
|
||||
|
||||
values_view.def("__len__", [](ValuesView &view) { return view.map.size(); });
|
||||
values_view.def("__iter__",
|
||||
[](ValuesView &view) {
|
||||
return make_value_iterator(view.map.begin(), view.map.end());
|
||||
},
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
|
||||
items_view.def("__len__", [](ItemsView &view) { return view.map.size(); });
|
||||
items_view.def("__iter__",
|
||||
[](ItemsView &view) {
|
||||
return make_iterator(view.map.begin(), view.map.end());
|
||||
},
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
|
||||
return cl;
|
||||
}
|
||||
|
||||
|
||||
89
ext/pybind11/noxfile.py
Normal file
89
ext/pybind11/noxfile.py
Normal file
@@ -0,0 +1,89 @@
|
||||
import nox
|
||||
|
||||
nox.options.sessions = ["lint", "tests", "tests_packaging"]
|
||||
|
||||
PYTHON_VERISONS = ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]
|
||||
|
||||
|
||||
@nox.session(reuse_venv=True)
|
||||
def lint(session: nox.Session) -> None:
|
||||
"""
|
||||
Lint the codebase (except for clang-format/tidy).
|
||||
"""
|
||||
session.install("pre-commit")
|
||||
session.run("pre-commit", "run", "-a")
|
||||
|
||||
|
||||
@nox.session(python=PYTHON_VERISONS)
|
||||
def tests(session: nox.Session) -> None:
|
||||
"""
|
||||
Run the tests (requires a compiler).
|
||||
"""
|
||||
tmpdir = session.create_tmp()
|
||||
session.install("cmake")
|
||||
session.install("-r", "tests/requirements.txt")
|
||||
session.run(
|
||||
"cmake",
|
||||
"-S",
|
||||
".",
|
||||
"-B",
|
||||
tmpdir,
|
||||
"-DPYBIND11_WERROR=ON",
|
||||
"-DDOWNLOAD_CATCH=ON",
|
||||
"-DDOWNLOAD_EIGEN=ON",
|
||||
*session.posargs
|
||||
)
|
||||
session.run("cmake", "--build", tmpdir)
|
||||
session.run("cmake", "--build", tmpdir, "--config=Release", "--target", "check")
|
||||
|
||||
|
||||
@nox.session
|
||||
def tests_packaging(session: nox.Session) -> None:
|
||||
"""
|
||||
Run the packaging tests.
|
||||
"""
|
||||
|
||||
session.install("-r", "tests/requirements.txt", "--prefer-binary")
|
||||
session.run("pytest", "tests/extra_python_package")
|
||||
|
||||
|
||||
@nox.session(reuse_venv=True)
|
||||
def docs(session: nox.Session) -> None:
|
||||
"""
|
||||
Build the docs. Pass "serve" to serve.
|
||||
"""
|
||||
|
||||
session.install("-r", "docs/requirements.txt")
|
||||
session.chdir("docs")
|
||||
|
||||
if "pdf" in session.posargs:
|
||||
session.run("sphinx-build", "-M", "latexpdf", ".", "_build")
|
||||
return
|
||||
|
||||
session.run("sphinx-build", "-M", "html", ".", "_build")
|
||||
|
||||
if "serve" in session.posargs:
|
||||
session.log("Launching docs at http://localhost:8000/ - use Ctrl-C to quit")
|
||||
session.run("python", "-m", "http.server", "8000", "-d", "_build/html")
|
||||
elif session.posargs:
|
||||
session.error("Unsupported argument to docs")
|
||||
|
||||
|
||||
@nox.session(reuse_venv=True)
|
||||
def make_changelog(session: nox.Session) -> None:
|
||||
"""
|
||||
Inspect the closed issues and make entries for a changelog.
|
||||
"""
|
||||
session.install("ghapi", "rich")
|
||||
session.run("python", "tools/make_changelog.py")
|
||||
|
||||
|
||||
@nox.session(reuse_venv=True)
|
||||
def build(session: nox.Session) -> None:
|
||||
"""
|
||||
Build SDists and wheels.
|
||||
"""
|
||||
|
||||
session.install("build")
|
||||
session.run("python", "-m", "build")
|
||||
session.run("python", "-m", "build", env={"PYBIND11_GLOBAL_SDIST": "1"})
|
||||
@@ -1,8 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ._version import version_info, __version__
|
||||
from .commands import get_include, get_cmake_dir
|
||||
|
||||
from ._version import __version__, version_info
|
||||
from .commands import get_cmake_dir, get_include
|
||||
|
||||
__all__ = (
|
||||
"version_info",
|
||||
|
||||
@@ -5,7 +5,7 @@ import argparse
|
||||
import sys
|
||||
import sysconfig
|
||||
|
||||
from .commands import get_include, get_cmake_dir
|
||||
from .commands import get_cmake_dir, get_include
|
||||
|
||||
|
||||
def print_includes():
|
||||
|
||||
@@ -8,5 +8,5 @@ def _to_int(s):
|
||||
return s
|
||||
|
||||
|
||||
__version__ = "2.6.2"
|
||||
__version__ = "2.8.1"
|
||||
version_info = tuple(_to_int(s) for s in __version__.split("."))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Union, Tuple
|
||||
from typing import Tuple, Union
|
||||
|
||||
def _to_int(s: str) -> Union[int, str]: ...
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
|
||||
|
||||
DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
|
||||
|
||||
@@ -41,25 +41,25 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import sys
|
||||
import sysconfig
|
||||
import tempfile
|
||||
import threading
|
||||
import platform
|
||||
import warnings
|
||||
|
||||
try:
|
||||
from setuptools.command.build_ext import build_ext as _build_ext
|
||||
from setuptools import Extension as _Extension
|
||||
from setuptools.command.build_ext import build_ext as _build_ext
|
||||
except ImportError:
|
||||
from distutils.command.build_ext import build_ext as _build_ext
|
||||
from distutils.extension import Extension as _Extension
|
||||
|
||||
import distutils.errors
|
||||
import distutils.ccompiler
|
||||
import distutils.errors
|
||||
|
||||
|
||||
WIN = sys.platform.startswith("win32")
|
||||
WIN = sys.platform.startswith("win32") and "mingw" not in sysconfig.get_platform()
|
||||
PY2 = sys.version_info[0] < 3
|
||||
MACOS = sys.platform.startswith("darwin")
|
||||
STD_TMPL = "/std:c++{}" if WIN else "-std=c++{}"
|
||||
@@ -84,7 +84,7 @@ class Pybind11Extension(_Extension):
|
||||
* ``stdlib=libc++`` on macOS
|
||||
* ``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
|
||||
level. It is _highly_ recommended you either set this, or use the provided
|
||||
``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)
|
||||
|
||||
|
||||
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):
|
||||
"""
|
||||
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)
|
||||
|
||||
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
|
||||
except ImportError:
|
||||
threads = 1
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
# IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI /
|
||||
# pre-commit).
|
||||
|
||||
from typing import Any, Callable, Iterator, Optional, Type, TypeVar, Union
|
||||
from types import TracebackType
|
||||
|
||||
import contextlib
|
||||
import distutils.ccompiler
|
||||
from distutils.command.build_ext import build_ext as _build_ext # type: ignore
|
||||
from distutils.extension import Extension as _Extension
|
||||
import distutils.ccompiler
|
||||
import contextlib
|
||||
from types import TracebackType
|
||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Type, TypeVar, Union
|
||||
|
||||
WIN: bool
|
||||
PY2: bool
|
||||
@@ -33,6 +32,9 @@ def auto_cpp_level(compiler: distutils.ccompiler.CCompiler) -> Union[int, str]:
|
||||
class build_ext(_build_ext): # type: ignore
|
||||
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 naive_recompile(obj: str, src: str) -> bool: ...
|
||||
|
||||
|
||||
@@ -1,3 +1,41 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=42", "wheel", "cmake>=3.18", "ninja"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.check-manifest]
|
||||
ignore = [
|
||||
"tests/**",
|
||||
"docs/**",
|
||||
"tools/**",
|
||||
"include/**",
|
||||
".*",
|
||||
"pybind11/include/**",
|
||||
"pybind11/share/**",
|
||||
"CMakeLists.txt",
|
||||
"noxfile.py",
|
||||
]
|
||||
|
||||
[tool.isort]
|
||||
# Needs the compiled .so modules and env.py from tests
|
||||
known_first_party = "env,pybind11_cross_module_tests,pybind11_tests,"
|
||||
# For black compatibility
|
||||
profile = "black"
|
||||
|
||||
[tool.mypy]
|
||||
files = "pybind11"
|
||||
python_version = "2.7"
|
||||
warn_unused_configs = true
|
||||
|
||||
disallow_any_generics = true
|
||||
disallow_subclassing_any = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
check_untyped_defs = true
|
||||
disallow_untyped_decorators = true
|
||||
no_implicit_optional = true
|
||||
warn_redundant_casts = true
|
||||
warn_unused_ignores = true
|
||||
warn_return_any = true
|
||||
no_implicit_reexport = true
|
||||
strict_equality = true
|
||||
|
||||
@@ -20,6 +20,7 @@ classifiers =
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
Programming Language :: Python :: 3.10
|
||||
License :: OSI Approved :: BSD License
|
||||
Programming Language :: Python :: Implementation :: PyPy
|
||||
Programming Language :: Python :: Implementation :: CPython
|
||||
@@ -37,17 +38,6 @@ zip_safe = False
|
||||
[bdist_wheel]
|
||||
universal=1
|
||||
|
||||
[check-manifest]
|
||||
ignore =
|
||||
tests/**
|
||||
docs/**
|
||||
tools/**
|
||||
include/**
|
||||
.*
|
||||
pybind11/include/**
|
||||
pybind11/share/**
|
||||
CMakeLists.txt
|
||||
|
||||
|
||||
[flake8]
|
||||
max-line-length = 99
|
||||
@@ -61,25 +51,6 @@ ignore =
|
||||
# Black conflict
|
||||
W503, E203
|
||||
|
||||
[mypy]
|
||||
files = pybind11
|
||||
python_version = 2.7
|
||||
warn_unused_configs = True
|
||||
|
||||
# Currently (0.800) identical to --strict
|
||||
disallow_any_generics = True
|
||||
disallow_subclassing_any = True
|
||||
disallow_untyped_calls = True
|
||||
disallow_untyped_defs = True
|
||||
disallow_incomplete_defs = True
|
||||
check_untyped_defs = True
|
||||
disallow_untyped_decorators = True
|
||||
no_implicit_optional = True
|
||||
warn_redundant_casts = True
|
||||
warn_unused_ignores = True
|
||||
warn_return_any = True
|
||||
no_implicit_reexport = True
|
||||
strict_equality = True
|
||||
|
||||
[tool:pytest]
|
||||
timeout = 300
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
# Setup script for PyPI; use CMakeFile.txt to build extension modules
|
||||
|
||||
import contextlib
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
@@ -19,6 +20,36 @@ VERSION_REGEX = re.compile(
|
||||
r"^\s*#\s*define\s+PYBIND11_VERSION_([A-Z]+)\s+(.*)$", re.MULTILINE
|
||||
)
|
||||
|
||||
|
||||
def build_expected_version_hex(matches):
|
||||
patch_level_serial = matches["PATCH"]
|
||||
serial = None
|
||||
try:
|
||||
major = int(matches["MAJOR"])
|
||||
minor = int(matches["MINOR"])
|
||||
flds = patch_level_serial.split(".")
|
||||
if flds:
|
||||
patch = int(flds[0])
|
||||
level = None
|
||||
if len(flds) == 1:
|
||||
level = "0"
|
||||
serial = 0
|
||||
elif len(flds) == 2:
|
||||
level_serial = flds[1]
|
||||
for level in ("a", "b", "c", "dev"):
|
||||
if level_serial.startswith(level):
|
||||
serial = int(level_serial[len(level) :])
|
||||
break
|
||||
except ValueError:
|
||||
pass
|
||||
if serial is None:
|
||||
msg = 'Invalid PYBIND11_VERSION_PATCH: "{}"'.format(patch_level_serial)
|
||||
raise RuntimeError(msg)
|
||||
return "0x{:02x}{:02x}{:02x}{}{:x}".format(
|
||||
major, minor, patch, level[:1].upper(), serial
|
||||
)
|
||||
|
||||
|
||||
# PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers
|
||||
# files, and the sys.prefix files (CMake and headers).
|
||||
|
||||
@@ -40,7 +71,7 @@ exec(code, loc)
|
||||
version = loc["__version__"]
|
||||
|
||||
# Verify that the version matches the one in C++
|
||||
with open("include/pybind11/detail/common.h") as f:
|
||||
with io.open("include/pybind11/detail/common.h", encoding="utf8") as f:
|
||||
matches = dict(VERSION_REGEX.findall(f.read()))
|
||||
cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches)
|
||||
if version != cpp_version:
|
||||
@@ -49,6 +80,15 @@ if version != cpp_version:
|
||||
)
|
||||
raise RuntimeError(msg)
|
||||
|
||||
version_hex = matches.get("HEX", "MISSING")
|
||||
expected_version_hex = build_expected_version_hex(matches)
|
||||
if version_hex != expected_version_hex:
|
||||
msg = "PYBIND11_VERSION_HEX {} does not match expected value {}!".format(
|
||||
version_hex,
|
||||
expected_version_hex,
|
||||
)
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
def get_and_replace(filename, binary=False, **opts):
|
||||
with open(filename, "rb" if binary else "r") as f:
|
||||
|
||||
@@ -10,10 +10,10 @@ cmake_minimum_required(VERSION 3.4)
|
||||
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
|
||||
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
|
||||
# the behavior using the following workaround:
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.18)
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.21)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.18)
|
||||
cmake_policy(VERSION 3.21)
|
||||
endif()
|
||||
|
||||
# Only needed for CMake < 3.5 support
|
||||
@@ -104,6 +104,7 @@ set(PYBIND11_TEST_FILES
|
||||
test_constants_and_functions.cpp
|
||||
test_copy_move.cpp
|
||||
test_custom_type_casters.cpp
|
||||
test_custom_type_setup.cpp
|
||||
test_docstring_options.cpp
|
||||
test_eigen.cpp
|
||||
test_enum.cpp
|
||||
@@ -129,6 +130,7 @@ set(PYBIND11_TEST_FILES
|
||||
test_stl.cpp
|
||||
test_stl_binders.cpp
|
||||
test_tagbased_polymorphic.cpp
|
||||
test_thread.cpp
|
||||
test_union.cpp
|
||||
test_virtual_functions.cpp)
|
||||
|
||||
@@ -169,6 +171,18 @@ set(PYBIND11_CROSS_MODULE_TESTS test_exceptions.py test_local_bindings.py test_s
|
||||
|
||||
set(PYBIND11_CROSS_MODULE_GIL_TESTS test_gil_scoped.py)
|
||||
|
||||
set(PYBIND11_EIGEN_REPO
|
||||
"https://gitlab.com/libeigen/eigen.git"
|
||||
CACHE STRING "Eigen repository to use for tests")
|
||||
# Always use a hash for reconfigure speed and security reasons
|
||||
# Include the version number for pretty printing (keep in sync)
|
||||
set(PYBIND11_EIGEN_VERSION_AND_HASH
|
||||
"3.4.0;929bc0e191d0927b1735b9a1ddc0e8b77e3a25ec"
|
||||
CACHE STRING "Eigen version to use for tests, format: VERSION;HASH")
|
||||
|
||||
list(GET PYBIND11_EIGEN_VERSION_AND_HASH 0 PYBIND11_EIGEN_VERSION_STRING)
|
||||
list(GET PYBIND11_EIGEN_VERSION_AND_HASH 1 PYBIND11_EIGEN_VERSION_HASH)
|
||||
|
||||
# Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but
|
||||
# keep it in PYBIND11_PYTEST_FILES, so that we get the "eigen is not installed"
|
||||
# skip message).
|
||||
@@ -182,22 +196,26 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
||||
message(FATAL_ERROR "CMake 3.11+ required when using DOWNLOAD_EIGEN")
|
||||
endif()
|
||||
|
||||
set(EIGEN3_VERSION_STRING "3.3.8")
|
||||
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
eigen
|
||||
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
|
||||
GIT_TAG ${EIGEN3_VERSION_STRING})
|
||||
GIT_REPOSITORY "${PYBIND11_EIGEN_REPO}"
|
||||
GIT_TAG "${PYBIND11_EIGEN_VERSION_HASH}")
|
||||
|
||||
FetchContent_GetProperties(eigen)
|
||||
if(NOT eigen_POPULATED)
|
||||
message(STATUS "Downloading Eigen")
|
||||
message(
|
||||
STATUS
|
||||
"Downloading Eigen ${PYBIND11_EIGEN_VERSION_STRING} (${PYBIND11_EIGEN_VERSION_HASH}) from ${PYBIND11_EIGEN_REPO}"
|
||||
)
|
||||
FetchContent_Populate(eigen)
|
||||
endif()
|
||||
|
||||
set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR})
|
||||
set(EIGEN3_FOUND TRUE)
|
||||
# When getting locally, the version is not visible from a superprojet,
|
||||
# so just force it.
|
||||
set(EIGEN3_VERSION "${PYBIND11_EIGEN_VERSION_STRING}")
|
||||
|
||||
else()
|
||||
find_package(Eigen3 3.2.7 QUIET CONFIG)
|
||||
@@ -225,7 +243,8 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
||||
message(STATUS "Building tests with Eigen v${EIGEN3_VERSION}")
|
||||
else()
|
||||
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
|
||||
message(STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN on CMake 3.11+ to download")
|
||||
message(
|
||||
STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN=ON on CMake 3.11+ to download")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -246,6 +265,43 @@ if(Boost_FOUND)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Check if we need to add -lstdc++fs or -lc++fs or nothing
|
||||
if(DEFINED CMAKE_CXX_STANDARD AND CMAKE_CXX_STANDARD LESS 17)
|
||||
set(STD_FS_NO_LIB_NEEDED TRUE)
|
||||
elseif(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 C++17 compiler - not passing -lstdc++fs")
|
||||
set(STD_FS_LIB "")
|
||||
endif()
|
||||
|
||||
# Compile with compiler warnings turned on
|
||||
function(pybind11_enable_warnings target_name)
|
||||
if(MSVC)
|
||||
@@ -267,12 +323,19 @@ function(pybind11_enable_warnings target_name)
|
||||
target_compile_options(${target_name} PRIVATE /WX)
|
||||
elseif(PYBIND11_CUDA_TESTS)
|
||||
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)
|
||||
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()
|
||||
|
||||
# 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
|
||||
AND CMAKE_CXX_COMPILER_ID MATCHES "Clang"
|
||||
AND PYTHON_VERSION VERSION_LESS 3.0)
|
||||
@@ -349,6 +412,8 @@ foreach(target ${test_targets})
|
||||
target_compile_definitions(${target} PRIVATE -DPYBIND11_TEST_BOOST)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${target} PRIVATE ${STD_FS_LIB})
|
||||
|
||||
# Always write the output file directly into the 'tests' directory (even on MSVC)
|
||||
if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
|
||||
set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import platform
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
LINUX = sys.platform.startswith("linux")
|
||||
MACOS = sys.platform.startswith("darwin")
|
||||
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
|
||||
|
||||
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()
|
||||
|
||||
@@ -25,6 +25,7 @@ main_headers = {
|
||||
"include/pybind11/embed.h",
|
||||
"include/pybind11/eval.h",
|
||||
"include/pybind11/functional.h",
|
||||
"include/pybind11/gil.h",
|
||||
"include/pybind11/iostream.h",
|
||||
"include/pybind11/numpy.h",
|
||||
"include/pybind11/operators.h",
|
||||
@@ -41,9 +42,14 @@ detail_headers = {
|
||||
"include/pybind11/detail/descr.h",
|
||||
"include/pybind11/detail/init.h",
|
||||
"include/pybind11/detail/internals.h",
|
||||
"include/pybind11/detail/type_caster_base.h",
|
||||
"include/pybind11/detail/typeid.h",
|
||||
}
|
||||
|
||||
stl_headers = {
|
||||
"include/pybind11/stl/filesystem.h",
|
||||
}
|
||||
|
||||
cmake_files = {
|
||||
"share/cmake/pybind11/FindPythonLibsNew.cmake",
|
||||
"share/cmake/pybind11/pybind11Common.cmake",
|
||||
@@ -65,7 +71,7 @@ py_files = {
|
||||
"setup_helpers.pyi",
|
||||
}
|
||||
|
||||
headers = main_headers | detail_headers
|
||||
headers = main_headers | detail_headers | stl_headers
|
||||
src_files = headers | cmake_files
|
||||
all_files = src_files | py_files
|
||||
|
||||
@@ -75,6 +81,7 @@ sdist_files = {
|
||||
"pybind11/include",
|
||||
"pybind11/include/pybind11",
|
||||
"pybind11/include/pybind11/detail",
|
||||
"pybind11/include/pybind11/stl",
|
||||
"pybind11/share",
|
||||
"pybind11/share/cmake",
|
||||
"pybind11/share/cmake/pybind11",
|
||||
@@ -119,7 +126,7 @@ def test_build_sdist(monkeypatch, tmpdir):
|
||||
with tarfile.open(str(sdist)) as tar:
|
||||
start = tar.getnames()[0] + "/"
|
||||
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(
|
||||
tar.extractfile(tar.getmember(start + "setup.py"))
|
||||
@@ -131,9 +138,19 @@ def test_build_sdist(monkeypatch, tmpdir):
|
||||
) as f:
|
||||
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 |= 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/requires.txt")
|
||||
assert simpler == files
|
||||
@@ -144,11 +161,11 @@ def test_build_sdist(monkeypatch, tmpdir):
|
||||
.substitute(version=version, extra_cmd="")
|
||||
.encode()
|
||||
)
|
||||
assert setup_py == contents
|
||||
assert setup_py == contents
|
||||
|
||||
with open(os.path.join(MAIN_DIR, "tools", "pyproject.toml"), "rb") as f:
|
||||
contents = f.read()
|
||||
assert pyproject_toml == contents
|
||||
assert pyproject_toml == contents
|
||||
|
||||
|
||||
def test_build_global_dist(monkeypatch, tmpdir):
|
||||
@@ -174,7 +191,7 @@ def test_build_global_dist(monkeypatch, tmpdir):
|
||||
with tarfile.open(str(sdist)) as tar:
|
||||
start = tar.getnames()[0] + "/"
|
||||
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(
|
||||
tar.extractfile(tar.getmember(start + "setup.py"))
|
||||
@@ -186,9 +203,9 @@ def test_build_global_dist(monkeypatch, tmpdir):
|
||||
) as f:
|
||||
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 |= 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
|
||||
|
||||
with open(os.path.join(MAIN_DIR, "tools", "setup_global.py.in"), "rb") as f:
|
||||
@@ -213,7 +230,7 @@ def tests_build_wheel(monkeypatch, tmpdir):
|
||||
|
||||
(wheel,) = tmpdir.visit("*.whl")
|
||||
|
||||
files = set("pybind11/{}".format(n) for n in all_files)
|
||||
files = {"pybind11/{}".format(n) for n in all_files}
|
||||
files |= {
|
||||
"dist-info/LICENSE",
|
||||
"dist-info/METADATA",
|
||||
@@ -226,10 +243,10 @@ def tests_build_wheel(monkeypatch, tmpdir):
|
||||
with zipfile.ZipFile(str(wheel)) as z:
|
||||
names = z.namelist()
|
||||
|
||||
trimmed = set(n for n in names if "dist-info" not in n)
|
||||
trimmed |= set(
|
||||
trimmed = {n for n in names if "dist-info" not in n}
|
||||
trimmed |= {
|
||||
"dist-info/{}".format(n.split("/", 1)[-1]) for n in names if "dist-info" in n
|
||||
)
|
||||
}
|
||||
assert files == trimmed
|
||||
|
||||
|
||||
@@ -243,8 +260,8 @@ def tests_build_global_wheel(monkeypatch, tmpdir):
|
||||
|
||||
(wheel,) = tmpdir.visit("*.whl")
|
||||
|
||||
files = set("data/data/{}".format(n) for n in src_files)
|
||||
files |= set("data/headers/{}".format(n[8:]) for n in headers)
|
||||
files = {"data/data/{}".format(n) for n in src_files}
|
||||
files |= {"data/headers/{}".format(n[8:]) for n in headers}
|
||||
files |= {
|
||||
"dist-info/LICENSE",
|
||||
"dist-info/METADATA",
|
||||
@@ -257,6 +274,6 @@ def tests_build_global_wheel(monkeypatch, tmpdir):
|
||||
names = z.namelist()
|
||||
|
||||
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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import sys
|
||||
from textwrap import dedent
|
||||
|
||||
import pytest
|
||||
@@ -99,3 +99,45 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
|
||||
subprocess.check_call(
|
||||
[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"
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#pragma once
|
||||
#include <utility>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
/// Simple class used to test py::local:
|
||||
template <int> class LocalBase {
|
||||
public:
|
||||
LocalBase(int i) : i(i) { }
|
||||
explicit LocalBase(int i) : i(i) { }
|
||||
int i = -1;
|
||||
};
|
||||
|
||||
@@ -33,6 +35,25 @@ using NonLocalVec2 = std::vector<NonLocal2>;
|
||||
using NonLocalMap = std::unordered_map<std::string, NonLocalType>;
|
||||
using NonLocalMap2 = std::unordered_map<std::string, uint8_t>;
|
||||
|
||||
|
||||
// Exception that will be caught via the module local translator.
|
||||
class LocalException : public std::exception {
|
||||
public:
|
||||
explicit LocalException(const char * m) : message{m} {}
|
||||
const char * what() const noexcept override {return message.c_str();}
|
||||
private:
|
||||
std::string message = "";
|
||||
};
|
||||
|
||||
// Exception that will be registered with register_local_exception_translator
|
||||
class LocalSimpleException : public std::exception {
|
||||
public:
|
||||
explicit LocalSimpleException(const char * m) : message{m} {}
|
||||
const char * what() const noexcept override {return message.c_str();}
|
||||
private:
|
||||
std::string message = "";
|
||||
};
|
||||
|
||||
PYBIND11_MAKE_OPAQUE(LocalVec);
|
||||
PYBIND11_MAKE_OPAQUE(LocalVec2);
|
||||
PYBIND11_MAKE_OPAQUE(LocalMap);
|
||||
@@ -54,11 +75,11 @@ py::class_<T> bind_local(Args && ...args) {
|
||||
namespace pets {
|
||||
class Pet {
|
||||
public:
|
||||
Pet(std::string name) : name_(name) {}
|
||||
explicit Pet(std::string name) : name_(std::move(name)) {}
|
||||
std::string name_;
|
||||
const std::string &name() { return name_; }
|
||||
const std::string &name() const { return name_; }
|
||||
};
|
||||
} // namespace pets
|
||||
|
||||
struct MixGL { int i; MixGL(int i) : i{i} {} };
|
||||
struct MixGL2 { int i; MixGL2(int i) : i{i} {} };
|
||||
struct MixGL { int i; explicit MixGL(int i) : i{i} {} };
|
||||
struct MixGL2 { int i; explicit MixGL2(int i) : i{i} {} };
|
||||
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
ref() : m_ptr(nullptr) { print_default_created(this); track_default_created((ref_tag*) this); }
|
||||
|
||||
/// Construct a reference from a pointer
|
||||
ref(T *ptr) : m_ptr(ptr) {
|
||||
explicit ref(T *ptr) : m_ptr(ptr) {
|
||||
if (m_ptr) ((Object *) m_ptr)->incRef();
|
||||
|
||||
print_created(this, "from pointer", m_ptr); track_created((ref_tag*) this, "from pointer");
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
}
|
||||
|
||||
/// Move constructor
|
||||
ref(ref &&r) : m_ptr(r.m_ptr) {
|
||||
ref(ref &&r) noexcept : m_ptr(r.m_ptr) {
|
||||
r.m_ptr = nullptr;
|
||||
|
||||
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
|
||||
ref& operator=(ref&& r) {
|
||||
ref &operator=(ref &&r) noexcept {
|
||||
print_move_assigned(this, "pointer", r.m_ptr); track_move_assigned((ref_tag*) this);
|
||||
|
||||
if (*this == r)
|
||||
@@ -110,7 +110,11 @@ public:
|
||||
|
||||
/// Overwrite this reference with another reference
|
||||
ref& operator=(const ref& r) {
|
||||
print_copy_assigned(this, "pointer", r.m_ptr); track_copy_assigned((ref_tag*) this);
|
||||
if (this == &r) {
|
||||
return *this;
|
||||
}
|
||||
print_copy_assigned(this, "pointer", r.m_ptr);
|
||||
track_copy_assigned((ref_tag *) this);
|
||||
|
||||
if (m_ptr == r.m_ptr)
|
||||
return *this;
|
||||
@@ -161,7 +165,7 @@ public:
|
||||
const T& operator*() const { return *m_ptr; }
|
||||
|
||||
/// Return a pointer to the referenced object
|
||||
operator T* () { return m_ptr; }
|
||||
explicit operator T* () { return m_ptr; }
|
||||
|
||||
/// Return a const pointer to the referenced object
|
||||
T* get_ptr() { return m_ptr; }
|
||||
|
||||
@@ -9,8 +9,12 @@
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "local_bindings.h"
|
||||
#include "test_exceptions.h"
|
||||
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
|
||||
PYBIND11_MODULE(pybind11_cross_module_tests, m) {
|
||||
m.doc() = "pybind11 cross-module test module";
|
||||
@@ -25,11 +29,32 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
|
||||
bind_local<ExternalType2>(m, "ExternalType2", py::module_local());
|
||||
|
||||
// test_exceptions.py
|
||||
py::register_local_exception<LocalSimpleException>(m, "LocalSimpleException");
|
||||
m.def("raise_runtime_error", []() { PyErr_SetString(PyExc_RuntimeError, "My runtime error"); throw py::error_already_set(); });
|
||||
m.def("raise_value_error", []() { PyErr_SetString(PyExc_ValueError, "My value error"); throw py::error_already_set(); });
|
||||
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_stop_iteration", []() { throw py::stop_iteration(); });
|
||||
m.def("throw_local_error", []() { throw LocalException("just local"); });
|
||||
m.def("throw_local_simple_error", []() { throw LocalSimpleException("external mod"); });
|
||||
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());
|
||||
}
|
||||
});
|
||||
|
||||
// translate the local exception into a key error but only in this module
|
||||
py::register_local_exception_translator([](std::exception_ptr p) {
|
||||
try {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (const LocalException &e) {
|
||||
PyErr_SetString(PyExc_KeyError, e.what());
|
||||
}
|
||||
});
|
||||
|
||||
// test_local_bindings.py
|
||||
// Local to both:
|
||||
@@ -83,7 +108,7 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
|
||||
m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); });
|
||||
|
||||
// test_internal_locals_differ
|
||||
m.def("local_cpp_types_addr", []() { return (uintptr_t) &py::detail::registered_local_types_cpp(); });
|
||||
m.def("local_cpp_types_addr", []() { return (uintptr_t) &py::detail::get_local_internals().registered_types_cpp; });
|
||||
|
||||
// test_stl_caster_vs_stl_bind
|
||||
py::bind_vector<std::vector<int>>(m, "VectorInt");
|
||||
@@ -96,7 +121,10 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
|
||||
m.def("return_self", [](LocalVec *v) { return 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:
|
||||
explicit Dog(std::string name) : Pet(std::move(name)) {}
|
||||
};
|
||||
py::class_<pets::Pet>(m, "Pet", py::module_local())
|
||||
.def("name", &pets::Pet::name);
|
||||
// Binding for local extending class:
|
||||
@@ -118,6 +146,6 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
|
||||
// test_missing_header_message
|
||||
// The main module already includes stl.h, but we need to test the error message
|
||||
// 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>(); });
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/eval.h>
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1910
|
||||
// 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
|
||||
|
||||
namespace py = pybind11;
|
||||
@@ -14,15 +16,14 @@ class test_initializer {
|
||||
using Initializer = void (*)(py::module_ &);
|
||||
|
||||
public:
|
||||
test_initializer(Initializer init);
|
||||
explicit test_initializer(Initializer init);
|
||||
test_initializer(const char *submodule_name, Initializer init);
|
||||
};
|
||||
|
||||
#define TEST_SUBMODULE(name, variable) \
|
||||
void test_submodule_##name(py::module_ &); \
|
||||
test_initializer name(#name, test_submodule_##name); \
|
||||
void test_submodule_##name(py::module_ &variable)
|
||||
|
||||
#define TEST_SUBMODULE(name, variable) \
|
||||
void test_submodule_##name(py::module_ &); \
|
||||
test_initializer name(#name, test_submodule_##name); \
|
||||
void test_submodule_##name(py::module_ &(variable))
|
||||
|
||||
/// Dummy type which is not exported anywhere -- something to trigger a conversion error
|
||||
struct UnregisteredType { };
|
||||
@@ -31,7 +32,7 @@ struct UnregisteredType { };
|
||||
class UserType {
|
||||
public:
|
||||
UserType() = default;
|
||||
UserType(int i) : i(i) { }
|
||||
explicit UserType(int i) : i(i) { }
|
||||
|
||||
int value() const { return i; }
|
||||
void set(int set) { i = set; }
|
||||
|
||||
@@ -7,11 +7,11 @@ addopts =
|
||||
-rs
|
||||
# capture only Python print and C++ py::print, but not C output (low-level Python errors)
|
||||
--capture=sys
|
||||
# enable all warnings
|
||||
-Wa
|
||||
filterwarnings =
|
||||
# make warnings into errors but ignore certain third-party extension issues
|
||||
error
|
||||
# somehow, some DeprecationWarnings do not get turned into errors
|
||||
always::DeprecationWarning
|
||||
# importing scipy submodules on some version of Python
|
||||
ignore::ImportWarning
|
||||
# bogus numpy ABI warning (see numpy/#432)
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
--extra-index-url https://antocuni.github.io/pypy-wheels/manylinux2010/
|
||||
numpy==1.16.6; python_version<"3.6" and sys_platform!="win32"
|
||||
numpy==1.18.0; platform_python_implementation=="PyPy" and sys_platform=="darwin" and python_version>="3.6"
|
||||
numpy==1.19.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.6" and python_version<"3.10"
|
||||
numpy==1.16.6; python_version<"3.6" and sys_platform!="win32" and platform_python_implementation!="PyPy"
|
||||
numpy==1.19.0; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.6"
|
||||
numpy==1.20.0; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7"
|
||||
numpy==1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6"
|
||||
numpy==1.21.3; platform_python_implementation!="PyPy" and python_version>="3.7"
|
||||
py @ git+https://github.com/pytest-dev/py; python_version>="3.11"
|
||||
pytest==4.6.9; python_version<"3.5"
|
||||
pytest==6.1.2; python_version=="3.5"
|
||||
pytest==6.2.1; python_version>="3.6"
|
||||
pytest==6.2.4; python_version>="3.6"
|
||||
pytest-timeout
|
||||
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.2.3; platform_python_implementation!="PyPy" and python_version<"3.6"
|
||||
scipy==1.5.4; platform_python_implementation!="PyPy" and python_version>="3.6" and python_version<"3.10"
|
||||
|
||||
@@ -27,7 +27,7 @@ TEST_SUBMODULE(buffers, m) {
|
||||
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);
|
||||
s.m_rows = 0;
|
||||
s.m_cols = 0;
|
||||
@@ -40,7 +40,11 @@ TEST_SUBMODULE(buffers, m) {
|
||||
}
|
||||
|
||||
Matrix &operator=(const Matrix &s) {
|
||||
print_copy_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
|
||||
if (this == &s) {
|
||||
return *this;
|
||||
}
|
||||
print_copy_assigned(this,
|
||||
std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
|
||||
delete[] m_data;
|
||||
m_rows = s.m_rows;
|
||||
m_cols = s.m_cols;
|
||||
@@ -49,7 +53,7 @@ TEST_SUBMODULE(buffers, m) {
|
||||
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");
|
||||
if (&s != this) {
|
||||
delete[] m_data;
|
||||
@@ -79,7 +83,7 @@ TEST_SUBMODULE(buffers, m) {
|
||||
py::class_<Matrix>(m, "Matrix", py::buffer_protocol())
|
||||
.def(py::init<py::ssize_t, py::ssize_t>())
|
||||
/// Construct from a buffer
|
||||
.def(py::init([](py::buffer const b) {
|
||||
.def(py::init([](const py::buffer &b) {
|
||||
py::buffer_info info = b.request();
|
||||
if (info.format != py::format_descriptor<float>::format() || info.ndim != 2)
|
||||
throw std::runtime_error("Incompatible buffer format!");
|
||||
@@ -89,36 +93,36 @@ TEST_SUBMODULE(buffers, m) {
|
||||
return v;
|
||||
}))
|
||||
|
||||
.def("rows", &Matrix::rows)
|
||||
.def("cols", &Matrix::cols)
|
||||
.def("rows", &Matrix::rows)
|
||||
.def("cols", &Matrix::cols)
|
||||
|
||||
/// Bare bones interface
|
||||
.def("__getitem__", [](const Matrix &m, std::pair<py::ssize_t, py::ssize_t> i) {
|
||||
if (i.first >= m.rows() || i.second >= m.cols())
|
||||
throw py::index_error();
|
||||
return m(i.first, i.second);
|
||||
})
|
||||
.def("__setitem__", [](Matrix &m, std::pair<py::ssize_t, py::ssize_t> i, float v) {
|
||||
if (i.first >= m.rows() || i.second >= m.cols())
|
||||
throw py::index_error();
|
||||
m(i.first, i.second) = v;
|
||||
})
|
||||
/// Provide buffer access
|
||||
.def_buffer([](Matrix &m) -> py::buffer_info {
|
||||
.def("__getitem__",
|
||||
[](const Matrix &m, std::pair<py::ssize_t, py::ssize_t> i) {
|
||||
if (i.first >= m.rows() || i.second >= m.cols())
|
||||
throw py::index_error();
|
||||
return m(i.first, i.second);
|
||||
})
|
||||
.def("__setitem__",
|
||||
[](Matrix &m, std::pair<py::ssize_t, py::ssize_t> i, float v) {
|
||||
if (i.first >= m.rows() || i.second >= m.cols())
|
||||
throw py::index_error();
|
||||
m(i.first, i.second) = v;
|
||||
})
|
||||
/// Provide buffer access
|
||||
.def_buffer([](Matrix &m) -> py::buffer_info {
|
||||
return py::buffer_info(
|
||||
m.data(), /* Pointer to buffer */
|
||||
{ m.rows(), m.cols() }, /* Buffer dimensions */
|
||||
{ sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */
|
||||
sizeof(float) }
|
||||
);
|
||||
})
|
||||
;
|
||||
|
||||
});
|
||||
|
||||
// test_inherited_protocol
|
||||
class SquareMatrix : public Matrix {
|
||||
public:
|
||||
SquareMatrix(py::ssize_t n) : Matrix(n, n) { }
|
||||
explicit SquareMatrix(py::ssize_t n) : Matrix(n, n) {}
|
||||
};
|
||||
// Derived classes inherit the buffer protocol and the buffer access function
|
||||
py::class_<SquareMatrix, Matrix>(m, "SquareMatrix")
|
||||
@@ -154,7 +158,7 @@ TEST_SUBMODULE(buffers, m) {
|
||||
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())
|
||||
.def(py::init<>())
|
||||
@@ -169,7 +173,7 @@ TEST_SUBMODULE(buffers, m) {
|
||||
|
||||
struct BufferReadOnly {
|
||||
const uint8_t value = 0;
|
||||
BufferReadOnly(uint8_t value): value(value) {}
|
||||
explicit BufferReadOnly(uint8_t value) : value(value) {}
|
||||
|
||||
py::buffer_info get_buffer_info() {
|
||||
return py::buffer_info(&value, 1);
|
||||
@@ -208,7 +212,5 @@ TEST_SUBMODULE(buffers, m) {
|
||||
})
|
||||
;
|
||||
|
||||
m.def("get_buffer_info", [](py::buffer buffer) {
|
||||
return buffer.request();
|
||||
});
|
||||
m.def("get_buffer_info", [](const py::buffer &buffer) { return buffer.request(); });
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import ctypes
|
||||
import io
|
||||
import struct
|
||||
import ctypes
|
||||
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
|
||||
from pybind11_tests import buffers as m
|
||||
import env
|
||||
from pybind11_tests import ConstructorStats
|
||||
from pybind11_tests import buffers as m
|
||||
|
||||
np = pytest.importorskip("numpy")
|
||||
|
||||
|
||||
@@ -10,11 +10,6 @@
|
||||
#include "pybind11_tests.h"
|
||||
#include <pybind11/complex.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
||||
#endif
|
||||
|
||||
struct ConstRefCasted {
|
||||
int tag;
|
||||
};
|
||||
@@ -30,12 +25,28 @@ class type_caster<ConstRefCasted> {
|
||||
// cast operator.
|
||||
bool load(handle, bool) { return true; }
|
||||
|
||||
operator ConstRefCasted&&() { value = {1}; return std::move(value); }
|
||||
operator ConstRefCasted&() { value = {2}; return value; }
|
||||
operator ConstRefCasted*() { value = {3}; return &value; }
|
||||
explicit operator ConstRefCasted &&() {
|
||||
value = {1};
|
||||
// NOLINTNEXTLINE(performance-move-const-arg)
|
||||
return std::move(value);
|
||||
}
|
||||
explicit operator ConstRefCasted &() {
|
||||
value = {2};
|
||||
return value;
|
||||
}
|
||||
explicit operator ConstRefCasted *() {
|
||||
value = {3};
|
||||
return &value;
|
||||
}
|
||||
|
||||
operator const ConstRefCasted&() { value = {4}; return value; }
|
||||
operator const ConstRefCasted*() { value = {5}; return &value; }
|
||||
explicit operator const ConstRefCasted &() {
|
||||
value = {4};
|
||||
return value;
|
||||
}
|
||||
explicit operator const ConstRefCasted *() {
|
||||
value = {5};
|
||||
return &value;
|
||||
}
|
||||
|
||||
// custom cast_op to explicitly propagate types to the conversion operators.
|
||||
template <typename T_>
|
||||
@@ -69,7 +80,7 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
std::wstring wstr;
|
||||
wstr.push_back(0x61); // a
|
||||
wstr.push_back(0x2e18); // ⸘
|
||||
if (sizeof(wchar_t) == 2) { wstr.push_back(mathbfA16_1); wstr.push_back(mathbfA16_2); } // 𝐀, utf16
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) { wstr.push_back(mathbfA16_1); wstr.push_back(mathbfA16_2); } // 𝐀, utf16
|
||||
else { wstr.push_back((wchar_t) mathbfA32); } // 𝐀, utf32
|
||||
wstr.push_back(0x7a); // z
|
||||
|
||||
@@ -79,11 +90,12 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
m.def("good_wchar_string", [=]() { return wstr; }); // a‽𝐀z
|
||||
m.def("bad_utf8_string", []() { return std::string("abc\xd0" "def"); });
|
||||
m.def("bad_utf16_string", [=]() { return std::u16string({ b16, char16_t(0xd800), z16 }); });
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
// Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger UnicodeDecodeError
|
||||
if (PY_MAJOR_VERSION >= 3)
|
||||
m.def("bad_utf32_string", [=]() { return std::u32string({ a32, char32_t(0xd800), z32 }); });
|
||||
if (PY_MAJOR_VERSION >= 3 || sizeof(wchar_t) == 2)
|
||||
m.def("bad_utf32_string", [=]() { return std::u32string({ a32, char32_t(0xd800), z32 }); });
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2))
|
||||
m.def("bad_wchar_string", [=]() { return std::wstring({ wchar_t(0x61), wchar_t(0xd800) }); });
|
||||
#endif
|
||||
m.def("u8_Z", []() -> char { return 'Z'; });
|
||||
m.def("u8_eacute", []() -> char { return '\xe9'; });
|
||||
m.def("u16_ibang", [=]() -> char16_t { return ib16; });
|
||||
@@ -101,7 +113,7 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
|
||||
// test_bytes_to_string
|
||||
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
|
||||
m.attr("has_u8string") = true;
|
||||
@@ -146,9 +158,12 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
m.def("int_passthrough_noconvert", [](int arg) { return arg; }, py::arg{}.noconvert());
|
||||
|
||||
// test_tuple
|
||||
m.def("pair_passthrough", [](std::pair<bool, std::string> input) {
|
||||
return std::make_pair(input.second, input.first);
|
||||
}, "Return a pair in reversed order");
|
||||
m.def(
|
||||
"pair_passthrough",
|
||||
[](const std::pair<bool, std::string> &input) {
|
||||
return std::make_pair(input.second, input.first);
|
||||
},
|
||||
"Return a pair in reversed order");
|
||||
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 a triple in reversed order");
|
||||
@@ -177,11 +192,11 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
|
||||
// test_none_deferred
|
||||
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", [](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", [](py::none) { return false; });
|
||||
m.def("nodefer_none_void", [](const py::none &) { return false; });
|
||||
|
||||
// test_void_caster
|
||||
m.def("load_nullptr_t", [](std::nullptr_t) {}); // not useful, but it should still compile
|
||||
@@ -231,7 +246,7 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
}, "copy"_a);
|
||||
|
||||
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;
|
||||
l.append(f(std::ref(w)));
|
||||
l.append(f(std::cref(w)));
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
|
||||
import env
|
||||
from pybind11_tests import IncType, UserType
|
||||
from pybind11_tests import builtin_casters as m
|
||||
from pybind11_tests import UserType, IncType
|
||||
|
||||
|
||||
def test_simple_string():
|
||||
@@ -50,7 +49,7 @@ def test_single_char_arguments():
|
||||
"""Tests failures for passing invalid inputs to char-accepting functions"""
|
||||
|
||||
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"
|
||||
|
||||
@@ -299,7 +298,12 @@ def test_int_convert():
|
||||
assert convert(7) == 7
|
||||
assert noconvert(7) == 7
|
||||
cant_convert(3.14159)
|
||||
assert convert(Int()) == 42
|
||||
# TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar)
|
||||
if (3, 8) <= env.PY < (3, 10):
|
||||
with env.deprecated_call():
|
||||
assert convert(Int()) == 42
|
||||
else:
|
||||
assert convert(Int()) == 42
|
||||
requires_conversion(Int())
|
||||
cant_convert(NotInt())
|
||||
cant_convert(Float())
|
||||
@@ -329,7 +333,12 @@ def test_numpy_int_convert():
|
||||
assert noconvert(np.intc(42)) == 42
|
||||
|
||||
# The implicit conversion from np.float32 is undesirable but currently accepted.
|
||||
assert convert(np.float32(3.14159)) == 3
|
||||
# TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar)
|
||||
if (3, 8) <= env.PY < (3, 10):
|
||||
with env.deprecated_call():
|
||||
assert convert(np.float32(3.14159)) == 3
|
||||
else:
|
||||
assert convert(np.float32(3.14159)) == 3
|
||||
require_implicit(np.float32(3.14159))
|
||||
|
||||
|
||||
@@ -511,7 +520,7 @@ def test_void_caster_2():
|
||||
|
||||
def test_const_ref_caster():
|
||||
"""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.
|
||||
"""
|
||||
x = False
|
||||
|
||||
@@ -51,6 +51,7 @@ TEST_SUBMODULE(call_policies, m) {
|
||||
void addChild(Child *) { }
|
||||
Child *returnChild() { return new Child(); }
|
||||
Child *returnNullChild() { return nullptr; }
|
||||
static Child *staticFunction(Parent*) { return new Child(); }
|
||||
};
|
||||
py::class_<Parent>(m, "Parent")
|
||||
.def(py::init<>())
|
||||
@@ -60,7 +61,12 @@ TEST_SUBMODULE(call_policies, m) {
|
||||
.def("returnChild", &Parent::returnChild)
|
||||
.def("returnChildKeepAlive", &Parent::returnChild, 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)
|
||||
// test_alive_gc
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
|
||||
from pybind11_tests import call_policies as m
|
||||
from pybind11_tests import ConstructorStats
|
||||
from pybind11_tests import call_policies as m
|
||||
|
||||
|
||||
@pytest.mark.xfail("env.PYPY", reason="sometimes comes out 1 off on PyPy", strict=False)
|
||||
@@ -46,6 +45,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):
|
||||
n_inst = ConstructorStats.detail_reg_inst()
|
||||
@@ -85,6 +97,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
|
||||
@pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented")
|
||||
|
||||
@@ -17,8 +17,8 @@ int dummy_function(int i) { return i + 1; }
|
||||
|
||||
TEST_SUBMODULE(callbacks, m) {
|
||||
// test_callbacks, test_function_signatures
|
||||
m.def("test_callback1", [](py::object func) { return func(); });
|
||||
m.def("test_callback2", [](py::object func) { return func("Hello", 'x', true, 5); });
|
||||
m.def("test_callback1", [](const py::object &func) { return func(); });
|
||||
m.def("test_callback2", [](const py::object &func) { return func("Hello", 'x', true, 5); });
|
||||
m.def("test_callback3", [](const std::function<int(int)> &func) {
|
||||
return "func(43) = " + std::to_string(func(43)); });
|
||||
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
|
||||
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 t2 = py::make_tuple(5, 6);
|
||||
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 d2 = py::dict();
|
||||
auto d3 = py::dict("b"_a=2);
|
||||
return f("positional", 1, **d1, **d2, **d3);
|
||||
});
|
||||
|
||||
m.def("test_keyword_args", [](py::function f) {
|
||||
return f("x"_a=10, "y"_a=20);
|
||||
});
|
||||
m.def("test_keyword_args", [](const py::function &f) { 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 kwargs = py::dict("d"_a=4);
|
||||
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 kwargs2 = py::dict("c"_a=3, "d"_a=4);
|
||||
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);
|
||||
});
|
||||
|
||||
m.def("test_unpacking_error1", [](py::function f) {
|
||||
m.def("test_unpacking_error1", [](const py::function &f) {
|
||||
auto kwargs = py::dict("x"_a=3);
|
||||
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);
|
||||
return f(**kwargs, "x"_a=1); // duplicate keyword after **
|
||||
});
|
||||
|
||||
m.def("test_arg_conversion_error1", [](py::function f) {
|
||||
f(234, UnregisteredType(), "kw"_a=567);
|
||||
});
|
||||
m.def("test_arg_conversion_error1",
|
||||
[](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);
|
||||
});
|
||||
|
||||
@@ -80,23 +77,64 @@ TEST_SUBMODULE(callbacks, m) {
|
||||
Payload() { print_default_created(this); }
|
||||
~Payload() { print_destroyed(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:
|
||||
m.def("payload_cstats", &ConstructorStats::get<Payload>);
|
||||
/* Test cleanup of lambda closure */
|
||||
m.def("test_cleanup", []() -> std::function<void(void)> {
|
||||
m.def("test_lambda_closure_cleanup", []() -> std::function<void()> {
|
||||
Payload p;
|
||||
|
||||
// In this situation, `Func` in the implementation of
|
||||
// `cpp_function::initialize` is NOT trivially destructible.
|
||||
return [p]() {
|
||||
/* p should be cleaned up when the returned function is garbage collected */
|
||||
(void) p;
|
||||
};
|
||||
});
|
||||
|
||||
class CppCallable {
|
||||
public:
|
||||
CppCallable() { track_default_created(this); }
|
||||
~CppCallable() { track_destroyed(this); }
|
||||
CppCallable(const CppCallable &) { track_copy_created(this); }
|
||||
CppCallable(CppCallable &&) noexcept { track_move_created(this); }
|
||||
void operator()() {}
|
||||
};
|
||||
|
||||
m.def("test_cpp_callable_cleanup", []() {
|
||||
// Related issue: https://github.com/pybind/pybind11/issues/3228
|
||||
// Related PR: https://github.com/pybind/pybind11/pull/3229
|
||||
py::list alive_counts;
|
||||
ConstructorStats &stat = ConstructorStats::get<CppCallable>();
|
||||
alive_counts.append(stat.alive());
|
||||
{
|
||||
CppCallable cpp_callable;
|
||||
alive_counts.append(stat.alive());
|
||||
{
|
||||
// In this situation, `Func` in the implementation of
|
||||
// `cpp_function::initialize` IS trivially destructible,
|
||||
// only `capture` is not.
|
||||
py::cpp_function py_func(cpp_callable);
|
||||
py::detail::silence_unused_warnings(py_func);
|
||||
alive_counts.append(stat.alive());
|
||||
}
|
||||
alive_counts.append(stat.alive());
|
||||
{
|
||||
py::cpp_function py_func(std::move(cpp_callable));
|
||||
py::detail::silence_unused_warnings(py_func);
|
||||
alive_counts.append(stat.alive());
|
||||
}
|
||||
alive_counts.append(stat.alive());
|
||||
}
|
||||
alive_counts.append(stat.alive());
|
||||
return alive_counts;
|
||||
});
|
||||
|
||||
// test_cpp_function_roundtrip
|
||||
/* Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer */
|
||||
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("roundtrip", [](std::function<int(int)> f, bool expect_none = false) {
|
||||
if (expect_none && f)
|
||||
@@ -109,12 +147,13 @@ TEST_SUBMODULE(callbacks, m) {
|
||||
if (!result) {
|
||||
auto r = f(1);
|
||||
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);
|
||||
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 {
|
||||
@@ -122,10 +161,11 @@ TEST_SUBMODULE(callbacks, m) {
|
||||
// [workaround(intel)] = default does not work here
|
||||
// Defaulting this destructor results in linking errors with the Intel compiler
|
||||
// (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;
|
||||
};
|
||||
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 {
|
||||
bool valid = true;
|
||||
@@ -133,8 +173,8 @@ TEST_SUBMODULE(callbacks, m) {
|
||||
MovableObject() = default;
|
||||
MovableObject(const MovableObject &) = default;
|
||||
MovableObject &operator=(const MovableObject &) = default;
|
||||
MovableObject(MovableObject &&o) : valid(o.valid) { o.valid = false; }
|
||||
MovableObject &operator=(MovableObject &&o) {
|
||||
MovableObject(MovableObject &&o) noexcept : valid(o.valid) { o.valid = false; }
|
||||
MovableObject &operator=(MovableObject &&o) noexcept {
|
||||
valid = o.valid;
|
||||
o.valid = false;
|
||||
return *this;
|
||||
@@ -143,7 +183,7 @@ TEST_SUBMODULE(callbacks, m) {
|
||||
py::class_<MovableObject>(m, "MovableObject");
|
||||
|
||||
// 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();
|
||||
f(x); // lvalue reference shouldn't move out object
|
||||
return x.valid; // must still return `true`
|
||||
@@ -155,9 +195,15 @@ TEST_SUBMODULE(callbacks, m) {
|
||||
.def(py::init<>())
|
||||
.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
|
||||
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
|
||||
auto start_f = [f](int j) {
|
||||
auto invoke_f = [f, j] {
|
||||
@@ -172,4 +218,10 @@ TEST_SUBMODULE(callbacks, m) {
|
||||
for (auto i : work)
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
from pybind11_tests import callbacks as m
|
||||
import time
|
||||
from threading import Thread
|
||||
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import callbacks as m
|
||||
|
||||
|
||||
def test_callbacks():
|
||||
from functools import partial
|
||||
@@ -75,13 +79,18 @@ def test_keyword_args_and_generalized_unpacking():
|
||||
|
||||
|
||||
def test_lambda_closure_cleanup():
|
||||
m.test_cleanup()
|
||||
m.test_lambda_closure_cleanup()
|
||||
cstats = m.payload_cstats()
|
||||
assert cstats.alive() == 0
|
||||
assert cstats.copy_constructions == 1
|
||||
assert cstats.move_constructions >= 1
|
||||
|
||||
|
||||
def test_cpp_callable_cleanup():
|
||||
alive_counts = m.test_cpp_callable_cleanup()
|
||||
assert alive_counts == [0, 1, 2, 1, 2, 1, 0]
|
||||
|
||||
|
||||
def test_cpp_function_roundtrip():
|
||||
"""Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer"""
|
||||
|
||||
@@ -92,6 +101,10 @@ def test_cpp_function_roundtrip():
|
||||
m.test_dummy_function(m.roundtrip(m.dummy_function))
|
||||
== "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.test_dummy_function(lambda x: x + 2)
|
||||
@@ -119,6 +132,16 @@ def test_movable_object():
|
||||
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():
|
||||
# serves as state for async callback
|
||||
class Item:
|
||||
@@ -139,10 +162,41 @@ def test_async_callbacks():
|
||||
from time import sleep
|
||||
|
||||
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():
|
||||
t = Thread(target=test_async_callbacks)
|
||||
t.start()
|
||||
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)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pybind11_tests import chrono as m
|
||||
import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import chrono as m
|
||||
|
||||
|
||||
def test_chrono_system_clock():
|
||||
@@ -39,9 +40,7 @@ def test_chrono_system_clock_roundtrip():
|
||||
|
||||
# They should be identical (no information lost on roundtrip)
|
||||
diff = abs(date1 - date2)
|
||||
assert diff.days == 0
|
||||
assert diff.seconds == 0
|
||||
assert diff.microseconds == 0
|
||||
assert diff == datetime.timedelta(0)
|
||||
|
||||
|
||||
def test_chrono_system_clock_roundtrip_date():
|
||||
@@ -64,9 +63,7 @@ def test_chrono_system_clock_roundtrip_date():
|
||||
assert diff.microseconds == 0
|
||||
|
||||
# Year, Month & Day should be the same after the round trip
|
||||
assert date1.year == date2.year
|
||||
assert date1.month == date2.month
|
||||
assert date1.day == date2.day
|
||||
assert date1 == date2
|
||||
|
||||
# There should be no time information
|
||||
assert time2.hour == 0
|
||||
@@ -117,10 +114,7 @@ def test_chrono_system_clock_roundtrip_time(time1, tz, monkeypatch):
|
||||
assert isinstance(time2, datetime.time)
|
||||
|
||||
# Hour, Minute, Second & Microsecond should be the same after the round trip
|
||||
assert time1.hour == time2.hour
|
||||
assert time1.minute == time2.minute
|
||||
assert time1.second == time2.second
|
||||
assert time1.microsecond == time2.microsecond
|
||||
assert time1 == time2
|
||||
|
||||
# There should be no date information (i.e. date = python base date)
|
||||
assert date2.year == 1970
|
||||
@@ -140,9 +134,13 @@ def test_chrono_duration_roundtrip():
|
||||
|
||||
cpp_diff = m.test_chrono3(diff)
|
||||
|
||||
assert cpp_diff.days == diff.days
|
||||
assert cpp_diff.seconds == diff.seconds
|
||||
assert cpp_diff.microseconds == diff.microseconds
|
||||
assert cpp_diff == diff
|
||||
|
||||
# Negative timedelta roundtrip
|
||||
diff = datetime.timedelta(microseconds=-1)
|
||||
cpp_diff = m.test_chrono3(diff)
|
||||
|
||||
assert cpp_diff == diff
|
||||
|
||||
|
||||
def test_chrono_duration_subtraction_equivalence():
|
||||
@@ -153,9 +151,7 @@ def test_chrono_duration_subtraction_equivalence():
|
||||
diff = date2 - date1
|
||||
cpp_diff = m.test_chrono4(date2, date1)
|
||||
|
||||
assert cpp_diff.days == diff.days
|
||||
assert cpp_diff.seconds == diff.seconds
|
||||
assert cpp_diff.microseconds == diff.microseconds
|
||||
assert cpp_diff == diff
|
||||
|
||||
|
||||
def test_chrono_duration_subtraction_equivalence_date():
|
||||
@@ -166,9 +162,7 @@ def test_chrono_duration_subtraction_equivalence_date():
|
||||
diff = date2 - date1
|
||||
cpp_diff = m.test_chrono4(date2, date1)
|
||||
|
||||
assert cpp_diff.days == diff.days
|
||||
assert cpp_diff.seconds == diff.seconds
|
||||
assert cpp_diff.microseconds == diff.microseconds
|
||||
assert cpp_diff == diff
|
||||
|
||||
|
||||
def test_chrono_steady_clock():
|
||||
@@ -183,9 +177,7 @@ def test_chrono_steady_clock_roundtrip():
|
||||
assert isinstance(time2, datetime.timedelta)
|
||||
|
||||
# They should be identical (no information lost on roundtrip)
|
||||
assert time1.days == time2.days
|
||||
assert time1.seconds == time2.seconds
|
||||
assert time1.microseconds == time2.microseconds
|
||||
assert time1 == time2
|
||||
|
||||
|
||||
def test_floating_point_duration():
|
||||
|
||||
@@ -19,13 +19,15 @@
|
||||
#include "local_bindings.h"
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(disable: 4324) // warning C4324: structure was padded due to alignment specifier
|
||||
#endif
|
||||
|
||||
// test_brace_initialization
|
||||
struct NoBraceInitialization {
|
||||
NoBraceInitialization(std::vector<int> v) : vec{std::move(v)} {}
|
||||
explicit NoBraceInitialization(std::vector<int> v) : vec{std::move(v)} {}
|
||||
template <typename T>
|
||||
NoBraceInitialization(std::initializer_list<T> l) : vec(l) {}
|
||||
|
||||
@@ -45,10 +47,26 @@ TEST_SUBMODULE(class_, m) {
|
||||
}
|
||||
~NoConstructor() { print_destroyed(this); }
|
||||
};
|
||||
struct NoConstructorNew {
|
||||
NoConstructorNew() = default;
|
||||
NoConstructorNew(const NoConstructorNew &) = default;
|
||||
NoConstructorNew(NoConstructorNew &&) = default;
|
||||
static NoConstructorNew *new_instance() {
|
||||
auto *ptr = new NoConstructorNew();
|
||||
print_created(ptr, "via new_instance");
|
||||
return ptr;
|
||||
}
|
||||
~NoConstructorNew() { print_destroyed(this); }
|
||||
};
|
||||
|
||||
py::class_<NoConstructor>(m, "NoConstructor")
|
||||
.def_static("new_instance", &NoConstructor::new_instance, "Return an instance");
|
||||
|
||||
py::class_<NoConstructorNew>(m, "NoConstructorNew")
|
||||
.def(py::init([](const NoConstructorNew &self) { return self; })) // Need a NOOP __init__
|
||||
.def_static("__new__",
|
||||
[](const py::object &) { return NoConstructorNew::new_instance(); });
|
||||
|
||||
// test_inheritance
|
||||
class Pet {
|
||||
public:
|
||||
@@ -63,18 +81,18 @@ TEST_SUBMODULE(class_, m) {
|
||||
|
||||
class Dog : public Pet {
|
||||
public:
|
||||
Dog(const std::string &name) : Pet(name, "dog") {}
|
||||
explicit Dog(const std::string &name) : Pet(name, "dog") {}
|
||||
std::string bark() const { return "Woof!"; }
|
||||
};
|
||||
|
||||
class Rabbit : public Pet {
|
||||
public:
|
||||
Rabbit(const std::string &name) : Pet(name, "parrot") {}
|
||||
explicit Rabbit(const std::string &name) : Pet(name, "parrot") {}
|
||||
};
|
||||
|
||||
class Hamster : public Pet {
|
||||
public:
|
||||
Hamster(const std::string &name) : Pet(name, "rodent") {}
|
||||
explicit Hamster(const std::string &name) : Pet(name, "rodent") {}
|
||||
};
|
||||
|
||||
class Chimera : public Pet {
|
||||
@@ -129,7 +147,7 @@ TEST_SUBMODULE(class_, m) {
|
||||
m.def("return_none", []() -> BaseClass* { return nullptr; });
|
||||
|
||||
// test_isinstance
|
||||
m.def("check_instances", [](py::list l) {
|
||||
m.def("check_instances", [](const py::list &l) {
|
||||
return py::make_tuple(
|
||||
py::isinstance<py::tuple>(l[0]),
|
||||
py::isinstance<py::dict>(l[1]),
|
||||
@@ -151,21 +169,16 @@ TEST_SUBMODULE(class_, m) {
|
||||
// return py::type::of<int>();
|
||||
if (category == 1)
|
||||
return py::type::of<DerivedClass1>();
|
||||
else
|
||||
return py::type::of<Invalid>();
|
||||
return py::type::of<Invalid>();
|
||||
});
|
||||
|
||||
m.def("get_type_of", [](py::object ob) {
|
||||
return py::type::of(ob);
|
||||
});
|
||||
m.def("get_type_of", [](py::object ob) { return py::type::of(std::move(ob)); });
|
||||
|
||||
m.def("get_type_classic", [](py::handle h) {
|
||||
return h.get_type();
|
||||
});
|
||||
|
||||
m.def("as_type", [](py::object ob) {
|
||||
return py::type(ob);
|
||||
});
|
||||
m.def("as_type", [](const py::object &ob) { return py::type(ob); });
|
||||
|
||||
// test_mismatched_holder
|
||||
struct MismatchBase1 { };
|
||||
@@ -211,7 +224,7 @@ TEST_SUBMODULE(class_, m) {
|
||||
struct ConvertibleFromUserType {
|
||||
int i;
|
||||
|
||||
ConvertibleFromUserType(UserType u) : i(u.value()) { }
|
||||
explicit ConvertibleFromUserType(UserType u) : i(u.value()) {}
|
||||
};
|
||||
|
||||
py::class_<ConvertibleFromUserType>(m, "AcceptsUserType")
|
||||
@@ -219,7 +232,7 @@ TEST_SUBMODULE(class_, m) {
|
||||
py::implicitly_convertible<UserType, ConvertibleFromUserType>();
|
||||
|
||||
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
|
||||
// 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.
|
||||
@@ -266,7 +279,7 @@ TEST_SUBMODULE(class_, m) {
|
||||
};
|
||||
struct PyAliasedHasOpNewDelSize : AliasedHasOpNewDelSize {
|
||||
PyAliasedHasOpNewDelSize() = default;
|
||||
PyAliasedHasOpNewDelSize(int) { }
|
||||
explicit PyAliasedHasOpNewDelSize(int) {}
|
||||
std::uint64_t j;
|
||||
};
|
||||
struct HasOpNewDelBoth {
|
||||
@@ -397,7 +410,7 @@ TEST_SUBMODULE(class_, m) {
|
||||
struct StringWrapper { std::string str; };
|
||||
m.def("test_error_after_conversions", [](int) {});
|
||||
m.def("test_error_after_conversions",
|
||||
[](StringWrapper) -> NotRegistered { return {}; });
|
||||
[](const StringWrapper &) -> NotRegistered { return {}; });
|
||||
py::class_<StringWrapper>(m, "StringWrapper").def(py::init<std::string>());
|
||||
py::implicitly_convertible<std::string, StringWrapper>();
|
||||
|
||||
@@ -434,8 +447,7 @@ TEST_SUBMODULE(class_, m) {
|
||||
struct SamePointer {};
|
||||
static SamePointer samePointer;
|
||||
py::class_<SamePointer, std::unique_ptr<SamePointer, py::nodelete>>(m, "SamePointer")
|
||||
.def(py::init([]() { return &samePointer; }))
|
||||
.def("__del__", [](SamePointer&) { py::print("__del__ called"); });
|
||||
.def(py::init([]() { return &samePointer; }));
|
||||
|
||||
struct Empty {};
|
||||
py::class_<Empty>(m, "Empty")
|
||||
@@ -462,19 +474,20 @@ TEST_SUBMODULE(class_, m) {
|
||||
struct OtherDuplicate {};
|
||||
struct DuplicateNested {};
|
||||
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_<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, "YetAnotherDuplicate");
|
||||
});
|
||||
m.def("register_duplicate_nested_class_name", [](py::object gt) {
|
||||
m.def("register_duplicate_nested_class_name", [](const py::object >) {
|
||||
py::class_<DuplicateNested>(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 >) {
|
||||
py::class_<OtherDuplicateNested>(gt, "OtherDuplicateNested");
|
||||
py::class_<OtherDuplicateNested>(gt, "YetAnotherDuplicateNested");
|
||||
});
|
||||
@@ -495,15 +508,15 @@ using DoesntBreak5 = py::class_<BreaksBase<5>>;
|
||||
using DoesntBreak6 = py::class_<BreaksBase<6>, std::shared_ptr<BreaksBase<6>>, BreaksTramp<6>>;
|
||||
using DoesntBreak7 = py::class_<BreaksBase<7>, BreaksTramp<7>, std::shared_ptr<BreaksBase<7>>>;
|
||||
using DoesntBreak8 = py::class_<BreaksBase<8>, std::shared_ptr<BreaksBase<8>>>;
|
||||
#define CHECK_BASE(N) static_assert(std::is_same<typename DoesntBreak##N::type, BreaksBase<N>>::value, \
|
||||
#define CHECK_BASE(N) static_assert(std::is_same<typename DoesntBreak##N::type, BreaksBase<(N)>>::value, \
|
||||
"DoesntBreak" #N " has wrong type!")
|
||||
CHECK_BASE(1); CHECK_BASE(2); CHECK_BASE(3); CHECK_BASE(4); CHECK_BASE(5); CHECK_BASE(6); CHECK_BASE(7); CHECK_BASE(8);
|
||||
#define CHECK_ALIAS(N) static_assert(DoesntBreak##N::has_alias && std::is_same<typename DoesntBreak##N::type_alias, BreaksTramp<N>>::value, \
|
||||
#define CHECK_ALIAS(N) static_assert(DoesntBreak##N::has_alias && std::is_same<typename DoesntBreak##N::type_alias, BreaksTramp<(N)>>::value, \
|
||||
"DoesntBreak" #N " has wrong type_alias!")
|
||||
#define CHECK_NOALIAS(N) static_assert(!DoesntBreak##N::has_alias && std::is_void<typename DoesntBreak##N::type_alias>::value, \
|
||||
"DoesntBreak" #N " has type alias, but shouldn't!")
|
||||
CHECK_ALIAS(1); CHECK_ALIAS(2); CHECK_NOALIAS(3); CHECK_ALIAS(4); CHECK_NOALIAS(5); CHECK_ALIAS(6); CHECK_ALIAS(7); CHECK_NOALIAS(8);
|
||||
#define CHECK_HOLDER(N, TYPE) static_assert(std::is_same<typename DoesntBreak##N::holder_type, std::TYPE##_ptr<BreaksBase<N>>>::value, \
|
||||
#define CHECK_HOLDER(N, TYPE) static_assert(std::is_same<typename DoesntBreak##N::holder_type, std::TYPE##_ptr<BreaksBase<(N)>>>::value, \
|
||||
"DoesntBreak" #N " has wrong holder_type!")
|
||||
CHECK_HOLDER(1, unique); CHECK_HOLDER(2, unique); CHECK_HOLDER(3, unique); CHECK_HOLDER(4, unique); CHECK_HOLDER(5, unique);
|
||||
CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared);
|
||||
@@ -513,7 +526,7 @@ CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared);
|
||||
// failures occurs).
|
||||
|
||||
// We have to actually look into the type: the typedef alone isn't enough to instantiate the type:
|
||||
#define CHECK_BROKEN(N) static_assert(std::is_same<typename Breaks##N::type, BreaksBase<-N>>::value, \
|
||||
#define CHECK_BROKEN(N) static_assert(std::is_same<typename Breaks##N::type, BreaksBase<-(N)>>::value, \
|
||||
"Breaks1 has wrong type!");
|
||||
|
||||
//// Two holder classes:
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
|
||||
from pybind11_tests import ConstructorStats, UserType
|
||||
from pybind11_tests import class_ as m
|
||||
from pybind11_tests import UserType, ConstructorStats
|
||||
|
||||
|
||||
def test_repr():
|
||||
@@ -26,6 +25,14 @@ def test_instance(msg):
|
||||
assert cstats.alive() == 0
|
||||
|
||||
|
||||
def test_instance_new(msg):
|
||||
instance = m.NoConstructorNew() # .__new__(m.NoConstructor.__class__)
|
||||
cstats = ConstructorStats.get(m.NoConstructorNew)
|
||||
assert cstats.alive() == 1
|
||||
del instance
|
||||
assert cstats.alive() == 0
|
||||
|
||||
|
||||
def test_type():
|
||||
assert m.check_type(1) == m.DerivedClass1
|
||||
with pytest.raises(RuntimeError) as execinfo:
|
||||
@@ -321,7 +328,7 @@ def test_bind_protected_functions():
|
||||
|
||||
|
||||
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")
|
||||
assert a.field1 == 123
|
||||
assert a.field2 == "test"
|
||||
|
||||
@@ -22,5 +22,7 @@ set_target_properties(test_installed_embed PROPERTIES OUTPUT_NAME test_cmake_bui
|
||||
# This may be needed to resolve header conflicts, e.g. between Python release and debug headers.
|
||||
set_target_properties(test_installed_embed PROPERTIES NO_SYSTEM_FROM_IMPORTED ON)
|
||||
|
||||
add_custom_target(check_installed_embed $<TARGET_FILE:test_installed_embed>
|
||||
${PROJECT_SOURCE_DIR}/../test.py)
|
||||
add_custom_target(
|
||||
check_installed_embed
|
||||
$<TARGET_FILE:test_installed_embed> ${PROJECT_SOURCE_DIR}/../test.py
|
||||
DEPENDS test_installed_embed)
|
||||
|
||||
@@ -35,4 +35,5 @@ add_custom_target(
|
||||
PYTHONPATH=$<TARGET_FILE_DIR:test_installed_function>
|
||||
${_Python_EXECUTABLE}
|
||||
${PROJECT_SOURCE_DIR}/../test.py
|
||||
${PROJECT_NAME})
|
||||
${PROJECT_NAME}
|
||||
DEPENDS test_installed_function)
|
||||
|
||||
@@ -42,4 +42,5 @@ add_custom_target(
|
||||
PYTHONPATH=$<TARGET_FILE_DIR:test_installed_target>
|
||||
${_Python_EXECUTABLE}
|
||||
${PROJECT_SOURCE_DIR}/../test.py
|
||||
${PROJECT_NAME})
|
||||
${PROJECT_NAME}
|
||||
DEPENDS test_installed_target)
|
||||
|
||||
@@ -23,8 +23,10 @@ add_executable(test_subdirectory_embed ../embed.cpp)
|
||||
target_link_libraries(test_subdirectory_embed PRIVATE pybind11::embed)
|
||||
set_target_properties(test_subdirectory_embed PROPERTIES OUTPUT_NAME test_cmake_build)
|
||||
|
||||
add_custom_target(check_subdirectory_embed $<TARGET_FILE:test_subdirectory_embed>
|
||||
"${PROJECT_SOURCE_DIR}/../test.py")
|
||||
add_custom_target(
|
||||
check_subdirectory_embed
|
||||
$<TARGET_FILE:test_subdirectory_embed> "${PROJECT_SOURCE_DIR}/../test.py"
|
||||
DEPENDS test_subdirectory_embed)
|
||||
|
||||
# Test custom export group -- PYBIND11_EXPORT_NAME
|
||||
add_library(test_embed_lib ../embed.cpp)
|
||||
|
||||
@@ -31,4 +31,5 @@ add_custom_target(
|
||||
PYTHONPATH=$<TARGET_FILE_DIR:test_subdirectory_function>
|
||||
${_Python_EXECUTABLE}
|
||||
${PROJECT_SOURCE_DIR}/../test.py
|
||||
${PROJECT_NAME})
|
||||
${PROJECT_NAME}
|
||||
DEPENDS test_subdirectory_function)
|
||||
|
||||
@@ -37,4 +37,5 @@ add_custom_target(
|
||||
PYTHONPATH=$<TARGET_FILE_DIR:test_subdirectory_target>
|
||||
${_Python_EXECUTABLE}
|
||||
${PROJECT_SOURCE_DIR}/../test.py
|
||||
${PROJECT_NAME})
|
||||
${PROJECT_NAME}
|
||||
DEPENDS test_subdirectory_target)
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
|
||||
import test_cmake_build
|
||||
|
||||
if str is not bytes: # If not Python2
|
||||
assert isinstance(__file__, str) # Test this is properly set
|
||||
|
||||
assert test_cmake_build.add(1, 2) == 3
|
||||
print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1]))
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -33,7 +34,7 @@ py::bytes return_bytes() {
|
||||
return std::string(data, 4);
|
||||
}
|
||||
|
||||
std::string print_bytes(py::bytes bytes) {
|
||||
std::string print_bytes(const py::bytes &bytes) {
|
||||
std::string ret = "bytes[";
|
||||
const auto value = static_cast<std::string>(bytes);
|
||||
for (size_t i = 0; i < value.length(); ++i) {
|
||||
@@ -56,12 +57,13 @@ int f1(int x) noexcept { return x+1; }
|
||||
#endif
|
||||
int f2(int x) noexcept(true) { return x+2; }
|
||||
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 ignored "-Wdeprecated"
|
||||
#endif
|
||||
// NOLINTNEXTLINE(modernize-use-noexcept)
|
||||
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
|
||||
#endif
|
||||
struct C {
|
||||
@@ -71,13 +73,15 @@ struct C {
|
||||
int m4(int x) const noexcept(true) { return x-4; }
|
||||
int m5(int x) noexcept(false) { return x-5; }
|
||||
int m6(int x) const noexcept(false) { return x-6; }
|
||||
#if defined(__GNUG__)
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated"
|
||||
#endif
|
||||
int m7(int x) throw() { return x-7; }
|
||||
int m8(int x) const throw() { return x-8; }
|
||||
#if defined(__GNUG__)
|
||||
// NOLINTNEXTLINE(modernize-use-noexcept)
|
||||
int m7(int x) throw() { return x - 7; }
|
||||
// NOLINTNEXTLINE(modernize-use-noexcept)
|
||||
int m8(int x) const throw() { return x - 8; }
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
};
|
||||
@@ -129,7 +133,14 @@ TEST_SUBMODULE(constants_and_functions, m) {
|
||||
;
|
||||
m.def("f1", f1);
|
||||
m.def("f2", f2);
|
||||
#if defined(__INTEL_COMPILER)
|
||||
# pragma warning push
|
||||
# pragma warning disable 878 // incompatible exception specifications
|
||||
#endif
|
||||
m.def("f3", f3);
|
||||
#if defined(__INTEL_COMPILER)
|
||||
# pragma warning pop
|
||||
#endif
|
||||
m.def("f4", f4);
|
||||
|
||||
// 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
|
||||
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("should_raise", [](int, int, py::object) { return 42; }, "some docstring",
|
||||
py::arg_v("x", 42), py::arg_v("y", 42, "<the answer>"), py::arg_v("z", default_value));
|
||||
m.def("register_with_raising_repr", [](py::module_ m, const py::object &default_value) {
|
||||
m.def(
|
||||
"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));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -37,9 +37,16 @@ template <> lacking_move_ctor empty<lacking_move_ctor>::instance_ = {};
|
||||
class MoveOnlyInt {
|
||||
public:
|
||||
MoveOnlyInt() { print_default_created(this); }
|
||||
MoveOnlyInt(int v) : value{std::move(v)} { print_created(this, value); }
|
||||
MoveOnlyInt(MoveOnlyInt &&m) { print_move_created(this, m.value); std::swap(value, m.value); }
|
||||
MoveOnlyInt &operator=(MoveOnlyInt &&m) { print_move_assigned(this, m.value); std::swap(value, m.value); return *this; }
|
||||
explicit MoveOnlyInt(int v) : value{v} { print_created(this, value); }
|
||||
MoveOnlyInt(MoveOnlyInt &&m) noexcept {
|
||||
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 &operator=(const MoveOnlyInt &) = delete;
|
||||
~MoveOnlyInt() { print_destroyed(this); }
|
||||
@@ -49,9 +56,16 @@ public:
|
||||
class MoveOrCopyInt {
|
||||
public:
|
||||
MoveOrCopyInt() { print_default_created(this); }
|
||||
MoveOrCopyInt(int v) : value{std::move(v)} { print_created(this, value); }
|
||||
MoveOrCopyInt(MoveOrCopyInt &&m) { print_move_created(this, m.value); std::swap(value, m.value); }
|
||||
MoveOrCopyInt &operator=(MoveOrCopyInt &&m) { print_move_assigned(this, m.value); std::swap(value, m.value); return *this; }
|
||||
explicit MoveOrCopyInt(int v) : value{v} { print_created(this, value); }
|
||||
MoveOrCopyInt(MoveOrCopyInt &&m) noexcept {
|
||||
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 &operator=(const MoveOrCopyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; }
|
||||
~MoveOrCopyInt() { print_destroyed(this); }
|
||||
@@ -61,7 +75,7 @@ public:
|
||||
class CopyOnlyInt {
|
||||
public:
|
||||
CopyOnlyInt() { print_default_created(this); }
|
||||
CopyOnlyInt(int v) : value{std::move(v)} { print_created(this, value); }
|
||||
explicit CopyOnlyInt(int v) : value{v} { print_created(this, 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() { print_destroyed(this); }
|
||||
@@ -93,8 +107,8 @@ public:
|
||||
if (!src) return none().release();
|
||||
return cast(*src, policy, parent);
|
||||
}
|
||||
operator CopyOnlyInt*() { return &value; }
|
||||
operator CopyOnlyInt&() { return value; }
|
||||
explicit operator CopyOnlyInt *() { return &value; }
|
||||
explicit operator CopyOnlyInt &() { return value; }
|
||||
template <typename T> using cast_op_type = pybind11::detail::cast_op_type<T>;
|
||||
};
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
@@ -111,7 +125,8 @@ TEST_SUBMODULE(copy_move_policies, m) {
|
||||
py::return_value_policy::move);
|
||||
|
||||
// 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;
|
||||
r += py::cast<MoveOrCopyInt>(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
|
||||
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; });
|
||||
// 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("move_pair", [](std::pair<MoveOnlyInt, MoveOrCopyInt> p) {
|
||||
return p.first.value + p.second.value;
|
||||
@@ -186,8 +205,7 @@ TEST_SUBMODULE(copy_move_policies, m) {
|
||||
void *ptr = std::malloc(bytes);
|
||||
if (ptr)
|
||||
return ptr;
|
||||
else
|
||||
throw std::bad_alloc{};
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
};
|
||||
py::class_<PrivateOpNew>(m, "PrivateOpNew").def_readonly("value", &PrivateOpNew::value);
|
||||
@@ -201,7 +219,7 @@ TEST_SUBMODULE(copy_move_policies, m) {
|
||||
// #389: rvp::move should fall-through to copy on non-movable objects
|
||||
struct MoveIssue1 {
|
||||
int v;
|
||||
MoveIssue1(int v) : v{v} {}
|
||||
explicit MoveIssue1(int v) : v{v} {}
|
||||
MoveIssue1(const MoveIssue1 &c) = default;
|
||||
MoveIssue1(MoveIssue1 &&) = delete;
|
||||
};
|
||||
@@ -209,7 +227,7 @@ TEST_SUBMODULE(copy_move_policies, m) {
|
||||
|
||||
struct MoveIssue2 {
|
||||
int v;
|
||||
MoveIssue2(int v) : v{v} {}
|
||||
explicit MoveIssue2(int v) : v{v} {}
|
||||
MoveIssue2(MoveIssue2 &&) = default;
|
||||
};
|
||||
py::class_<MoveIssue2>(m, "MoveIssue2").def(py::init<int>()).def_readwrite("value", &MoveIssue2::v);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import copy_move_policies as m
|
||||
|
||||
|
||||
|
||||
@@ -67,9 +67,12 @@ public:
|
||||
DestructionTester() { print_default_created(this); }
|
||||
~DestructionTester() { print_destroyed(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=(DestructionTester &&) { print_move_assigned(this); return *this; }
|
||||
DestructionTester &operator=(DestructionTester &&) noexcept {
|
||||
print_move_assigned(this);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
namespace pybind11 { namespace detail {
|
||||
template <> struct type_caster<DestructionTester> {
|
||||
@@ -94,7 +97,11 @@ TEST_SUBMODULE(custom_type_casters, m) {
|
||||
class ArgInspector {
|
||||
public:
|
||||
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;
|
||||
}
|
||||
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_static("h", &ArgInspector::h, py::arg{}.noconvert(), py::arg() = ArgAlwaysConverts())
|
||||
;
|
||||
m.def("arg_inspect_func", [](ArgInspector2 a, ArgInspector1 b, ArgAlwaysConverts) { return a.arg + "\n" + b.arg; },
|
||||
py::arg{}.noconvert(false), py::arg_v(nullptr, ArgInspector1()).noconvert(true), py::arg() = ArgAlwaysConverts());
|
||||
m.def(
|
||||
"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_only", [](double f) { return 0.5 * f; }, "f"_a.noconvert());
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import custom_type_casters as m
|
||||
|
||||
|
||||
|
||||
41
ext/pybind11/tests/test_custom_type_setup.cpp
Normal file
41
ext/pybind11/tests/test_custom_type_setup.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
tests/test_custom_type_setup.cpp -- Tests `pybind11::custom_type_setup`
|
||||
|
||||
Copyright (c) Google LLC
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
namespace {
|
||||
|
||||
struct OwnsPythonObjects {
|
||||
py::object value = py::none();
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_SUBMODULE(custom_type_setup, m) {
|
||||
py::class_<OwnsPythonObjects> cls(
|
||||
m, "OwnsPythonObjects", py::custom_type_setup([](PyHeapTypeObject *heap_type) {
|
||||
auto *type = &heap_type->ht_type;
|
||||
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
|
||||
type->tp_traverse = [](PyObject *self_base, visitproc visit, void *arg) {
|
||||
auto &self = py::cast<OwnsPythonObjects &>(py::handle(self_base));
|
||||
Py_VISIT(self.value.ptr());
|
||||
return 0;
|
||||
};
|
||||
type->tp_clear = [](PyObject *self_base) {
|
||||
auto &self = py::cast<OwnsPythonObjects &>(py::handle(self_base));
|
||||
self.value = py::none();
|
||||
return 0;
|
||||
};
|
||||
}));
|
||||
cls.def(py::init<>());
|
||||
cls.def_readwrite("value", &OwnsPythonObjects::value);
|
||||
}
|
||||
50
ext/pybind11/tests/test_custom_type_setup.py
Normal file
50
ext/pybind11/tests/test_custom_type_setup.py
Normal file
@@ -0,0 +1,50 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import gc
|
||||
import weakref
|
||||
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import custom_type_setup as m
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def gc_tester():
|
||||
"""Tests that an object is garbage collected.
|
||||
|
||||
Assumes that any unreferenced objects are fully collected after calling
|
||||
`gc.collect()`. That is true on CPython, but does not appear to reliably
|
||||
hold on PyPy.
|
||||
"""
|
||||
|
||||
weak_refs = []
|
||||
|
||||
def add_ref(obj):
|
||||
# PyPy does not support `gc.is_tracked`.
|
||||
if hasattr(gc, "is_tracked"):
|
||||
assert gc.is_tracked(obj)
|
||||
weak_refs.append(weakref.ref(obj))
|
||||
|
||||
yield add_ref
|
||||
|
||||
gc.collect()
|
||||
for ref in weak_refs:
|
||||
assert ref() is None
|
||||
|
||||
|
||||
# PyPy does not seem to reliably garbage collect.
|
||||
@pytest.mark.skipif("env.PYPY")
|
||||
def test_self_cycle(gc_tester):
|
||||
obj = m.OwnsPythonObjects()
|
||||
obj.value = obj
|
||||
gc_tester(obj)
|
||||
|
||||
|
||||
# PyPy does not seem to reliably garbage collect.
|
||||
@pytest.mark.skipif("env.PYPY")
|
||||
def test_indirect_cycle(gc_tester):
|
||||
obj = m.OwnsPythonObjects()
|
||||
obj_list = [obj]
|
||||
obj.value = obj_list
|
||||
gc_tester(obj)
|
||||
@@ -13,6 +13,9 @@
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#if _MSC_VER < 1910 // VS 2015's MSVC
|
||||
# pragma warning(disable: 4127) // C4127: conditional expression is constant
|
||||
#endif
|
||||
# pragma warning(disable: 4996) // C4996: std::unary_negation is deprecated
|
||||
#endif
|
||||
|
||||
@@ -54,8 +57,7 @@ void reset_refs() {
|
||||
}
|
||||
|
||||
// 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
|
||||
// reference is referencing rows/columns correctly).
|
||||
@@ -94,15 +96,18 @@ TEST_SUBMODULE(eigen, m) {
|
||||
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_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_rm", [](DenseMatrixR x) -> DenseMatrixR { return 2.0f * x; });
|
||||
m.def("double_mat_cm", [](const Eigen::MatrixXf &x) -> Eigen::MatrixXf { return 2.0f * x; });
|
||||
m.def("double_mat_rm", [](const DenseMatrixR &x) -> DenseMatrixR { return 2.0f * x; });
|
||||
|
||||
// test_eigen_ref_to_python
|
||||
// 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("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
|
||||
// Mutators: these add some value to the given element using Eigen, but Eigen should be mapping into
|
||||
@@ -176,6 +181,7 @@ TEST_SUBMODULE(eigen, m) {
|
||||
ReturnTester() { print_created(this); }
|
||||
~ReturnTester() { print_destroyed(this); }
|
||||
static Eigen::MatrixXd create() { return Eigen::MatrixXd::Ones(10, 10); }
|
||||
// NOLINTNEXTLINE(readability-const-return-type)
|
||||
static const Eigen::MatrixXd createConst() { return Eigen::MatrixXd::Ones(10, 10); }
|
||||
Eigen::MatrixXd &get() { return mat; }
|
||||
Eigen::MatrixXd *getPtr() { return &mat; }
|
||||
@@ -242,21 +248,27 @@ TEST_SUBMODULE(eigen, m) {
|
||||
|
||||
// test_fixed, and various other tests
|
||||
m.def("fixed_r", [mat]() -> FixedMatrixR { return FixedMatrixR(mat); });
|
||||
// Our Eigen does a hack which respects constness through the numpy writeable flag.
|
||||
// Therefore, the const return actually affects this type despite being an rvalue.
|
||||
// NOLINTNEXTLINE(readability-const-return-type)
|
||||
m.def("fixed_r_const", [mat]() -> const FixedMatrixR { return FixedMatrixR(mat); });
|
||||
m.def("fixed_c", [mat]() -> FixedMatrixC { return FixedMatrixC(mat); });
|
||||
m.def("fixed_copy_r", [](const FixedMatrixR &m) -> FixedMatrixR { return m; });
|
||||
m.def("fixed_copy_c", [](const FixedMatrixC &m) -> FixedMatrixC { return m; });
|
||||
// test_mutator_descriptors
|
||||
m.def("fixed_mutator_r", [](Eigen::Ref<FixedMatrixR>) {});
|
||||
m.def("fixed_mutator_c", [](Eigen::Ref<FixedMatrixC>) {});
|
||||
m.def("fixed_mutator_a", [](py::EigenDRef<FixedMatrixC>) {});
|
||||
m.def("fixed_mutator_r", [](const Eigen::Ref<FixedMatrixR> &) {});
|
||||
m.def("fixed_mutator_c", [](const Eigen::Ref<FixedMatrixC> &) {});
|
||||
m.def("fixed_mutator_a", [](const py::EigenDRef<FixedMatrixC> &) {});
|
||||
// test_dense
|
||||
m.def("dense_r", [mat]() -> DenseMatrixR { return DenseMatrixR(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_c", [](const DenseMatrixC &m) -> DenseMatrixC { return m; });
|
||||
// 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_copy_r", [](const SparseMatrixR &m) -> SparseMatrixR { return m; });
|
||||
m.def("sparse_copy_c", [](const SparseMatrixC &m) -> SparseMatrixC { return m; });
|
||||
@@ -280,8 +292,10 @@ TEST_SUBMODULE(eigen, m) {
|
||||
// that would allow copying (if types or strides don't match) for comparison:
|
||||
m.def("get_elem", &get_elem);
|
||||
// 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); },
|
||||
py::arg{}.noconvert());
|
||||
m.def(
|
||||
"get_elem_nocopy",
|
||||
[](const Eigen::Ref<const Eigen::MatrixXd> &m) -> double { return get_elem(m); },
|
||||
py::arg{}.noconvert());
|
||||
// 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); },
|
||||
py::arg{}.noconvert());
|
||||
@@ -295,18 +309,23 @@ TEST_SUBMODULE(eigen, m) {
|
||||
|
||||
// test_issue1105
|
||||
// Issue #1105: when converting from a numpy two-dimensional (Nx1) or (1xN) value into a dense
|
||||
// eigen Vector or RowVector, the argument would fail to load because the numpy copy would fail:
|
||||
// numpy won't broadcast a Nx1 into a 1-dimensional vector.
|
||||
m.def("iss1105_col", [](Eigen::VectorXd) { return true; });
|
||||
m.def("iss1105_row", [](Eigen::RowVectorXd) { return true; });
|
||||
// eigen Vector or RowVector, the argument would fail to load because the numpy copy would
|
||||
// fail: numpy won't broadcast a Nx1 into a 1-dimensional vector.
|
||||
m.def("iss1105_col", [](const Eigen::VectorXd &) { return true; });
|
||||
m.def("iss1105_row", [](const Eigen::RowVectorXd &) { return true; });
|
||||
|
||||
// test_named_arguments
|
||||
// Make sure named arguments are working properly:
|
||||
m.def("matrix_multiply", [](const py::EigenDRef<const Eigen::MatrixXd> A, const py::EigenDRef<const Eigen::MatrixXd> B)
|
||||
-> Eigen::MatrixXd {
|
||||
if (A.cols() != B.rows()) throw std::domain_error("Nonconformable matrices!");
|
||||
return A * B;
|
||||
}, py::arg("A"), py::arg("B"));
|
||||
m.def(
|
||||
"matrix_multiply",
|
||||
[](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;
|
||||
},
|
||||
py::arg("A"),
|
||||
py::arg("B"));
|
||||
|
||||
// test_custom_operator_new
|
||||
py::class_<CustomOperatorNew>(m, "CustomOperatorNew")
|
||||
@@ -318,7 +337,7 @@ TEST_SUBMODULE(eigen, m) {
|
||||
// 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
|
||||
// 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);
|
||||
return v(5);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import ConstructorStats
|
||||
|
||||
np = pytest.importorskip("numpy")
|
||||
|
||||
@@ -31,6 +31,7 @@ endif()
|
||||
add_custom_target(
|
||||
cpptest
|
||||
COMMAND "$<TARGET_FILE:test_embed>"
|
||||
DEPENDS test_embed
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
|
||||
pybind11_add_module(external_module THIN_LTO external_module.cpp)
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace py = pybind11;
|
||||
PYBIND11_MODULE(external_module, m) {
|
||||
class A {
|
||||
public:
|
||||
A(int value) : v{value} {};
|
||||
explicit A(int value) : v{value} {};
|
||||
int v;
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user