mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-24 14:15:11 +00:00
Merge branch 'master' into master_with_dummy_change
This commit is contained in:
commit
cc1dbb9847
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -1111,7 +1111,7 @@ jobs:
|
|||||||
uses: jwlawson/actions-setup-cmake@v2.0
|
uses: jwlawson/actions-setup-cmake@v2.0
|
||||||
|
|
||||||
- name: Install ninja-build tool
|
- name: Install ninja-build tool
|
||||||
uses: seanmiddleditch/gha-setup-ninja@v4
|
uses: seanmiddleditch/gha-setup-ninja@v5
|
||||||
|
|
||||||
- name: Run pip installs
|
- name: Run pip installs
|
||||||
run: |
|
run: |
|
||||||
|
@ -3,15 +3,123 @@
|
|||||||
Build systems
|
Build systems
|
||||||
#############
|
#############
|
||||||
|
|
||||||
|
For an overview of Python packaging including compiled packaging with a pybind11
|
||||||
|
example, along with a cookiecutter that includes several pybind11 options, see
|
||||||
|
the `Scientific Python Development Guide`_.
|
||||||
|
|
||||||
|
.. _Scientific Python Development Guide: https://learn.scientific-python.org/development/guides/packaging-compiled/
|
||||||
|
|
||||||
|
.. scikit-build-core:
|
||||||
|
|
||||||
|
Modules with CMake
|
||||||
|
==================
|
||||||
|
|
||||||
|
A Python extension module can be created with just a few lines of code:
|
||||||
|
|
||||||
|
.. code-block:: cmake
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.15...3.29)
|
||||||
|
project(example LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(PYBIND11_FINDPYTHON ON)
|
||||||
|
find_package(pybind11 CONFIG REQUIRED)
|
||||||
|
|
||||||
|
pybind11_add_module(example example.cpp)
|
||||||
|
install(TARGET example DESTINATION .)
|
||||||
|
|
||||||
|
(You use the ``add_subdirectory`` instead, see the example in :ref:`cmake`.) In
|
||||||
|
this example, the code is located in a file named :file:`example.cpp`. Either
|
||||||
|
method will import the pybind11 project which provides the
|
||||||
|
``pybind11_add_module`` function. It will take care of all the details needed
|
||||||
|
to build a Python extension module on any platform.
|
||||||
|
|
||||||
|
To build with pip, build, cibuildwheel, uv, or other Python tools, you can
|
||||||
|
add a ``pyproject.toml`` file like this:
|
||||||
|
|
||||||
|
.. code-block:: toml
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["scikit-build-core", "pybind11"]
|
||||||
|
build-backend = "scikit_build_core.build"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "example"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
You don't need setuptools files like ``MANIFEST.in``, ``setup.py``, or
|
||||||
|
``setup.cfg``, as this is not setuptools. See `scikit-build-core`_ for details.
|
||||||
|
For projects you plan to upload to PyPI, be sure to fill out the ``[project]``
|
||||||
|
table with other important metadata as well (see `Writing pyproject.toml`_).
|
||||||
|
|
||||||
|
A working sample project can be found in the [scikit_build_example]_
|
||||||
|
repository. An older and harder-to-maintain method is in [cmake_example]_. More
|
||||||
|
details about our cmake support can be found below in :ref:`cmake`.
|
||||||
|
|
||||||
|
.. _scikit-build-core: https://scikit-build-core.readthedocs.io
|
||||||
|
|
||||||
|
.. [scikit_build_example] https://github.com/pybind/scikit_build_example
|
||||||
|
|
||||||
|
.. [cmake_example] https://github.com/pybind/cmake_example
|
||||||
|
|
||||||
|
.. _modules-meson-python:
|
||||||
|
|
||||||
|
Modules with meson-python
|
||||||
|
=========================
|
||||||
|
|
||||||
|
You can also build a package with `Meson`_ using `meson-python`_, if you prefer
|
||||||
|
that. Your ``meson.build`` file would look something like this:
|
||||||
|
|
||||||
|
.. _meson-example:
|
||||||
|
|
||||||
|
.. code-block:: meson
|
||||||
|
|
||||||
|
project(
|
||||||
|
'example',
|
||||||
|
'cpp',
|
||||||
|
version: '0.1.0',
|
||||||
|
default_options: [
|
||||||
|
'cpp_std=c++11',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
py = import('python').find_installation(pure: false)
|
||||||
|
pybind11_dep = dependency('pybind11')
|
||||||
|
|
||||||
|
py.extension_module('example',
|
||||||
|
'example.cpp',
|
||||||
|
install: true,
|
||||||
|
dependencies : [pybind11_dep],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
And you would need a ``pyproject.toml`` file like this:
|
||||||
|
|
||||||
|
.. code-block:: toml
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["meson-python", "pybind11"]
|
||||||
|
build-backend = "mesonpy"
|
||||||
|
|
||||||
|
Meson-python *requires* your project to be in git (or mercurial) as it uses it
|
||||||
|
for the SDist creation. For projects you plan to upload to PyPI, be sure to fill out the
|
||||||
|
``[project]`` table as well (see `Writing pyproject.toml`_).
|
||||||
|
|
||||||
|
|
||||||
|
.. _Writing pyproject.toml: https://packaging.python.org/en/latest/guides/writing-pyproject-toml
|
||||||
|
|
||||||
|
.. _meson: https://mesonbuild.com
|
||||||
|
|
||||||
|
.. _meson-python: https://meson-python.readthedocs.io/en/latest
|
||||||
|
|
||||||
.. _build-setuptools:
|
.. _build-setuptools:
|
||||||
|
|
||||||
Building with setuptools
|
Modules with setuptools
|
||||||
========================
|
=======================
|
||||||
|
|
||||||
For projects on PyPI, building with setuptools is the way to go. Sylvain Corlay
|
For projects on PyPI, a historically popular option is setuptools. Sylvain
|
||||||
has kindly provided an example project which shows how to set up everything,
|
Corlay has kindly provided an example project which shows how to set up
|
||||||
including automatic generation of documentation using Sphinx. Please refer to
|
everything, including automatic generation of documentation using Sphinx.
|
||||||
the [python_example]_ repository.
|
Please refer to the [python_example]_ repository.
|
||||||
|
|
||||||
.. [python_example] https://github.com/pybind/python_example
|
.. [python_example] https://github.com/pybind/python_example
|
||||||
|
|
||||||
@ -21,11 +129,11 @@ To use pybind11 inside your ``setup.py``, you have to have some system to
|
|||||||
ensure that ``pybind11`` is installed when you build your package. There are
|
ensure that ``pybind11`` is installed when you build your package. There are
|
||||||
four possible ways to do this, and pybind11 supports all four: You can ask all
|
four possible ways to do this, and pybind11 supports all four: You can ask all
|
||||||
users to install pybind11 beforehand (bad), you can use
|
users to install pybind11 beforehand (bad), you can use
|
||||||
:ref:`setup_helpers-pep518` (good, but very new and requires Pip 10),
|
:ref:`setup_helpers-pep518` (good), ``setup_requires=`` (discouraged), or you
|
||||||
:ref:`setup_helpers-setup_requires` (discouraged by Python packagers now that
|
can :ref:`setup_helpers-copy-manually` (works but you have to manually sync
|
||||||
PEP 518 is available, but it still works everywhere), or you can
|
your copy to get updates). Third party packagers like conda-forge generally
|
||||||
:ref:`setup_helpers-copy-manually` (always works but you have to manually sync
|
strongly prefer the ``pyproject.toml`` method, as it gives them control over
|
||||||
your copy to get updates).
|
the ``pybind11`` version, and they may apply patches, etc.
|
||||||
|
|
||||||
An example of a ``setup.py`` using pybind11's helpers:
|
An example of a ``setup.py`` using pybind11's helpers:
|
||||||
|
|
||||||
@ -122,70 +230,41 @@ version number that includes the number of commits since your last tag and a
|
|||||||
hash for a dirty directory. Another way to force a rebuild is purge your cache
|
hash for a dirty directory. Another way to force a rebuild is purge your cache
|
||||||
or use Pip's ``--no-cache-dir`` option.
|
or use Pip's ``--no-cache-dir`` option.
|
||||||
|
|
||||||
|
You also need a ``MANIFEST.in`` file to include all relevant files so that you
|
||||||
|
can make an SDist. If you use `pypa-build`_, that will build an SDist then a
|
||||||
|
wheel from that SDist by default, so you can look inside those files (wheels
|
||||||
|
are just zip files with a ``.whl`` extension) to make sure you aren't missing
|
||||||
|
files. `check-manifest`_ (setuptools specific) or `check-sdist`_ (general) are
|
||||||
|
CLI tools that can compare the SDist contents with your source control.
|
||||||
|
|
||||||
.. [Ccache] https://ccache.dev
|
.. [Ccache] https://ccache.dev
|
||||||
|
|
||||||
.. [setuptools_scm] https://github.com/pypa/setuptools_scm
|
.. [setuptools_scm] https://github.com/pypa/setuptools_scm
|
||||||
|
|
||||||
.. _setup_helpers-pep518:
|
.. _setup_helpers-pep518:
|
||||||
|
|
||||||
PEP 518 requirements (Pip 10+ required)
|
Build requirements
|
||||||
---------------------------------------
|
------------------
|
||||||
|
|
||||||
If you use `PEP 518's <https://www.python.org/dev/peps/pep-0518/>`_
|
With a ``pyproject.toml`` file, you can ensure that ``pybind11`` is available
|
||||||
``pyproject.toml`` file, you can ensure that ``pybind11`` is available during
|
during the compilation of your project. When this file exists, Pip will make a
|
||||||
the compilation of your project. When this file exists, Pip will make a new
|
new virtual environment, download just the packages listed here in
|
||||||
virtual environment, download just the packages listed here in ``requires=``,
|
``requires=``, and build a wheel (binary Python package). It will then throw
|
||||||
and build a wheel (binary Python package). It will then throw away the
|
away the environment, and install your wheel.
|
||||||
environment, and install your wheel.
|
|
||||||
|
|
||||||
Your ``pyproject.toml`` file will likely look something like this:
|
Your ``pyproject.toml`` file will likely look something like this:
|
||||||
|
|
||||||
.. code-block:: toml
|
.. code-block:: toml
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["setuptools>=42", "pybind11>=2.6.1"]
|
requires = ["setuptools", "pybind11"]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The main drawback to this method is that a `PEP 517`_ compliant build tool,
|
|
||||||
such as Pip 10+, is required for this approach to work; older versions of
|
|
||||||
Pip completely ignore this file. If you distribute binaries (called wheels
|
|
||||||
in Python) using something like `cibuildwheel`_, remember that ``setup.py``
|
|
||||||
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 official tool.
|
|
||||||
|
|
||||||
.. _PEP 517: https://www.python.org/dev/peps/pep-0517/
|
.. _PEP 517: https://www.python.org/dev/peps/pep-0517/
|
||||||
.. _cibuildwheel: https://cibuildwheel.readthedocs.io
|
.. _cibuildwheel: https://cibuildwheel.pypa.io
|
||||||
.. _pypa-build: https://pypa-build.readthedocs.io/en/latest/
|
.. _pypa-build: https://build.pypa.io/en/latest/
|
||||||
|
.. _check-manifest: https://pypi.io/project/check-manifest
|
||||||
.. _setup_helpers-setup_requires:
|
.. _check-sdist: https://pypi.io/project/check-sdist
|
||||||
|
|
||||||
Classic ``setup_requires``
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
If you want to support old versions of Pip with the classic
|
|
||||||
``setup_requires=["pybind11"]`` keyword argument to setup, which triggers a
|
|
||||||
two-phase ``setup.py`` run, then you will need to use something like this to
|
|
||||||
ensure the first pass works (which has not yet installed the ``setup_requires``
|
|
||||||
packages, since it can't install something it does not know about):
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
try:
|
|
||||||
from pybind11.setup_helpers import Pybind11Extension
|
|
||||||
except ImportError:
|
|
||||||
from setuptools import Extension as Pybind11Extension
|
|
||||||
|
|
||||||
|
|
||||||
It doesn't matter that the Extension class is not the enhanced subclass for the
|
|
||||||
first pass run; and the second pass will have the ``setup_requires``
|
|
||||||
requirements.
|
|
||||||
|
|
||||||
This is obviously more of a hack than the PEP 518 method, but it supports
|
|
||||||
ancient versions of Pip.
|
|
||||||
|
|
||||||
.. _setup_helpers-copy-manually:
|
.. _setup_helpers-copy-manually:
|
||||||
|
|
||||||
@ -231,32 +310,22 @@ the C++ source file. Python is then able to find the module and load it.
|
|||||||
|
|
||||||
.. [cppimport] https://github.com/tbenthompson/cppimport
|
.. [cppimport] https://github.com/tbenthompson/cppimport
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. _cmake:
|
.. _cmake:
|
||||||
|
|
||||||
Building with CMake
|
Building with CMake
|
||||||
===================
|
===================
|
||||||
|
|
||||||
For C++ codebases that have an existing CMake-based build system, a Python
|
For C++ codebases that have an existing CMake-based build system, a Python
|
||||||
extension module can be created with just a few lines of code:
|
extension module can be created with just a few lines of code, as seen above in
|
||||||
|
the module section. Pybind11 currently supports a lower minimum if you don't
|
||||||
|
use the modern FindPython, though be aware that CMake 3.27 removed the old
|
||||||
|
mechanism, so pybind11 will automatically switch if the old mechanism is not
|
||||||
|
available. Please opt into the new mechanism if at all possible. Our default
|
||||||
|
may change in future versions. This is the minimum required:
|
||||||
|
|
||||||
.. code-block:: cmake
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.5...3.29)
|
|
||||||
project(example LANGUAGES CXX)
|
|
||||||
|
|
||||||
add_subdirectory(pybind11)
|
|
||||||
pybind11_add_module(example example.cpp)
|
|
||||||
|
|
||||||
This assumes that the pybind11 repository is located in a subdirectory named
|
|
||||||
:file:`pybind11` and that the code is located in a file named :file:`example.cpp`.
|
|
||||||
The CMake command ``add_subdirectory`` will import the pybind11 project which
|
|
||||||
provides the ``pybind11_add_module`` function. It will take care of all the
|
|
||||||
details needed to build a Python extension module on any platform.
|
|
||||||
|
|
||||||
A working sample project, including a way to invoke CMake from :file:`setup.py` for
|
|
||||||
PyPI integration, can be found in the [cmake_example]_ repository.
|
|
||||||
|
|
||||||
.. [cmake_example] https://github.com/pybind/cmake_example
|
|
||||||
|
|
||||||
.. versionchanged:: 2.6
|
.. versionchanged:: 2.6
|
||||||
CMake 3.4+ is required.
|
CMake 3.4+ is required.
|
||||||
@ -264,6 +333,7 @@ PyPI integration, can be found in the [cmake_example]_ repository.
|
|||||||
.. versionchanged:: 2.11
|
.. versionchanged:: 2.11
|
||||||
CMake 3.5+ is required.
|
CMake 3.5+ is required.
|
||||||
|
|
||||||
|
|
||||||
Further information can be found at :doc:`cmake/index`.
|
Further information can be found at :doc:`cmake/index`.
|
||||||
|
|
||||||
pybind11_add_module
|
pybind11_add_module
|
||||||
@ -616,6 +686,13 @@ Building with Bazel
|
|||||||
You can build with the Bazel build system using the `pybind11_bazel
|
You can build with the Bazel build system using the `pybind11_bazel
|
||||||
<https://github.com/pybind/pybind11_bazel>`_ repository.
|
<https://github.com/pybind/pybind11_bazel>`_ repository.
|
||||||
|
|
||||||
|
Building with Meson
|
||||||
|
===================
|
||||||
|
|
||||||
|
You can use Meson, which has support for ``pybind11`` as a dependency (internally
|
||||||
|
relying on our ``pkg-config`` support). See the :ref:`module example above <meson-example>`.
|
||||||
|
|
||||||
|
|
||||||
Generating binding code automatically
|
Generating binding code automatically
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
@ -269,7 +269,7 @@ sphinxcontrib-svg2pdfconverter==1.2.2 \
|
|||||||
--hash=sha256:04ec767b55780a6b18d89cc1a8ada6d900c6efde9d1683abdb98a49b144465ca \
|
--hash=sha256:04ec767b55780a6b18d89cc1a8ada6d900c6efde9d1683abdb98a49b144465ca \
|
||||||
--hash=sha256:80a55ca61f70eae93efc65f3814f2f177c86ba55934a9f6c5022f1778b62146b
|
--hash=sha256:80a55ca61f70eae93efc65f3814f2f177c86ba55934a9f6c5022f1778b62146b
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
urllib3==2.2.1 \
|
urllib3==2.2.2 \
|
||||||
--hash=sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d \
|
--hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \
|
||||||
--hash=sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19
|
--hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168
|
||||||
# via requests
|
# via requests
|
||||||
|
@ -1339,13 +1339,24 @@ enable_if_t<!cast_is_temporary_value_reference<T>::value, T> cast_ref(object &&,
|
|||||||
// static_assert, even though if it's in dead code, so we provide a "trampoline" to pybind11::cast
|
// static_assert, even though if it's in dead code, so we provide a "trampoline" to pybind11::cast
|
||||||
// that only does anything in cases where pybind11::cast is valid.
|
// that only does anything in cases where pybind11::cast is valid.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
enable_if_t<cast_is_temporary_value_reference<T>::value, T> cast_safe(object &&) {
|
enable_if_t<cast_is_temporary_value_reference<T>::value
|
||||||
|
&& !detail::is_same_ignoring_cvref<T, PyObject *>::value,
|
||||||
|
T>
|
||||||
|
cast_safe(object &&) {
|
||||||
pybind11_fail("Internal error: cast_safe fallback invoked");
|
pybind11_fail("Internal error: cast_safe fallback invoked");
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
enable_if_t<std::is_void<T>::value, void> cast_safe(object &&) {}
|
enable_if_t<std::is_void<T>::value, void> cast_safe(object &&) {}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
enable_if_t<detail::none_of<cast_is_temporary_value_reference<T>, std::is_void<T>>::value, T>
|
enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value, PyObject *>
|
||||||
|
cast_safe(object &&o) {
|
||||||
|
return o.release().ptr();
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
enable_if_t<detail::none_of<cast_is_temporary_value_reference<T>,
|
||||||
|
detail::is_same_ignoring_cvref<T, PyObject *>,
|
||||||
|
std::is_void<T>>::value,
|
||||||
|
T>
|
||||||
cast_safe(object &&o) {
|
cast_safe(object &&o) {
|
||||||
return pybind11::cast<T>(std::move(o));
|
return pybind11::cast<T>(std::move(o));
|
||||||
}
|
}
|
||||||
|
@ -205,8 +205,8 @@ extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, P
|
|||||||
|
|
||||||
/// Cleanup the type-info for a pybind11-registered type.
|
/// Cleanup the type-info for a pybind11-registered type.
|
||||||
extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
|
extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
|
||||||
|
with_internals([obj](internals &internals) {
|
||||||
auto *type = (PyTypeObject *) obj;
|
auto *type = (PyTypeObject *) obj;
|
||||||
auto &internals = get_internals();
|
|
||||||
|
|
||||||
// A pybind11-registered type will:
|
// A pybind11-registered type will:
|
||||||
// 1) be found in internals.registered_types_py
|
// 1) be found in internals.registered_types_py
|
||||||
@ -238,6 +238,7 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
|
|||||||
|
|
||||||
delete tinfo;
|
delete tinfo;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
PyType_Type.tp_dealloc(obj);
|
PyType_Type.tp_dealloc(obj);
|
||||||
}
|
}
|
||||||
@ -310,19 +311,20 @@ inline void traverse_offset_bases(void *valueptr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline bool register_instance_impl(void *ptr, instance *self) {
|
inline bool register_instance_impl(void *ptr, instance *self) {
|
||||||
get_internals().registered_instances.emplace(ptr, self);
|
with_instance_map(ptr, [&](instance_map &instances) { instances.emplace(ptr, self); });
|
||||||
return true; // unused, but gives the same signature as the deregister func
|
return true; // unused, but gives the same signature as the deregister func
|
||||||
}
|
}
|
||||||
inline bool deregister_instance_impl(void *ptr, instance *self) {
|
inline bool deregister_instance_impl(void *ptr, instance *self) {
|
||||||
auto ®istered_instances = get_internals().registered_instances;
|
return with_instance_map(ptr, [&](instance_map &instances) {
|
||||||
auto range = registered_instances.equal_range(ptr);
|
auto range = instances.equal_range(ptr);
|
||||||
for (auto it = range.first; it != range.second; ++it) {
|
for (auto it = range.first; it != range.second; ++it) {
|
||||||
if (self == it->second) {
|
if (self == it->second) {
|
||||||
registered_instances.erase(it);
|
instances.erase(it);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void register_instance(instance *self, void *valptr, const type_info *tinfo) {
|
inline void register_instance(instance *self, void *valptr, const type_info *tinfo) {
|
||||||
@ -377,27 +379,32 @@ extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void add_patient(PyObject *nurse, PyObject *patient) {
|
inline void add_patient(PyObject *nurse, PyObject *patient) {
|
||||||
auto &internals = get_internals();
|
|
||||||
auto *instance = reinterpret_cast<detail::instance *>(nurse);
|
auto *instance = reinterpret_cast<detail::instance *>(nurse);
|
||||||
instance->has_patients = true;
|
instance->has_patients = true;
|
||||||
Py_INCREF(patient);
|
Py_INCREF(patient);
|
||||||
internals.patients[nurse].push_back(patient);
|
|
||||||
|
with_internals([&](internals &internals) { internals.patients[nurse].push_back(patient); });
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void clear_patients(PyObject *self) {
|
inline void clear_patients(PyObject *self) {
|
||||||
auto *instance = reinterpret_cast<detail::instance *>(self);
|
auto *instance = reinterpret_cast<detail::instance *>(self);
|
||||||
auto &internals = get_internals();
|
std::vector<PyObject *> patients;
|
||||||
|
|
||||||
|
with_internals([&](internals &internals) {
|
||||||
auto pos = internals.patients.find(self);
|
auto pos = internals.patients.find(self);
|
||||||
|
|
||||||
if (pos == internals.patients.end()) {
|
if (pos == internals.patients.end()) {
|
||||||
pybind11_fail("FATAL: Internal consistency check failed: Invalid clear_patients() call.");
|
pybind11_fail(
|
||||||
|
"FATAL: Internal consistency check failed: Invalid clear_patients() call.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clearing the patients can cause more Python code to run, which
|
// Clearing the patients can cause more Python code to run, which
|
||||||
// can invalidate the iterator. Extract the vector of patients
|
// can invalidate the iterator. Extract the vector of patients
|
||||||
// from the unordered_map first.
|
// from the unordered_map first.
|
||||||
auto patients = std::move(pos->second);
|
patients = std::move(pos->second);
|
||||||
internals.patients.erase(pos);
|
internals.patients.erase(pos);
|
||||||
|
});
|
||||||
|
|
||||||
instance->has_patients = false;
|
instance->has_patients = false;
|
||||||
for (PyObject *&patient : patients) {
|
for (PyObject *&patient : patients) {
|
||||||
Py_CLEAR(patient);
|
Py_CLEAR(patient);
|
||||||
@ -662,10 +669,13 @@ inline PyObject *make_new_python_type(const type_record &rec) {
|
|||||||
|
|
||||||
char *tp_doc = nullptr;
|
char *tp_doc = nullptr;
|
||||||
if (rec.doc && options::show_user_defined_docstrings()) {
|
if (rec.doc && options::show_user_defined_docstrings()) {
|
||||||
/* Allocate memory for docstring (using PyObject_MALLOC, since
|
/* Allocate memory for docstring (Python will free this later on) */
|
||||||
Python will free this later on) */
|
|
||||||
size_t size = std::strlen(rec.doc) + 1;
|
size_t size = std::strlen(rec.doc) + 1;
|
||||||
|
#if PY_VERSION_HEX >= 0x030D0000
|
||||||
|
tp_doc = (char *) PyMem_MALLOC(size);
|
||||||
|
#else
|
||||||
tp_doc = (char *) PyObject_MALLOC(size);
|
tp_doc = (char *) PyObject_MALLOC(size);
|
||||||
|
#endif
|
||||||
std::memcpy((void *) tp_doc, rec.doc, size);
|
std::memcpy((void *) tp_doc, rec.doc, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,7 +464,7 @@ PYBIND11_WARNING_POP
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
\endrst */
|
\endrst */
|
||||||
#define PYBIND11_MODULE(name, variable) \
|
#define PYBIND11_MODULE(name, variable, ...) \
|
||||||
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name) \
|
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name) \
|
||||||
PYBIND11_MAYBE_UNUSED; \
|
PYBIND11_MAYBE_UNUSED; \
|
||||||
PYBIND11_MAYBE_UNUSED \
|
PYBIND11_MAYBE_UNUSED \
|
||||||
@ -473,7 +473,10 @@ PYBIND11_WARNING_POP
|
|||||||
PYBIND11_CHECK_PYTHON_VERSION \
|
PYBIND11_CHECK_PYTHON_VERSION \
|
||||||
PYBIND11_ENSURE_INTERNALS_READY \
|
PYBIND11_ENSURE_INTERNALS_READY \
|
||||||
auto m = ::pybind11::module_::create_extension_module( \
|
auto m = ::pybind11::module_::create_extension_module( \
|
||||||
PYBIND11_TOSTRING(name), nullptr, &PYBIND11_CONCAT(pybind11_module_def_, name)); \
|
PYBIND11_TOSTRING(name), \
|
||||||
|
nullptr, \
|
||||||
|
&PYBIND11_CONCAT(pybind11_module_def_, name), \
|
||||||
|
##__VA_ARGS__); \
|
||||||
try { \
|
try { \
|
||||||
PYBIND11_CONCAT(pybind11_init_, name)(m); \
|
PYBIND11_CONCAT(pybind11_init_, name)(m); \
|
||||||
return m.ptr(); \
|
return m.ptr(); \
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
#include "../pytypes.h"
|
#include "../pytypes.h"
|
||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
/// Tracks the `internals` and `type_info` ABI version independent of the main library version.
|
/// Tracks the `internals` and `type_info` ABI version independent of the main library version.
|
||||||
///
|
///
|
||||||
@ -168,15 +170,37 @@ struct override_hash {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using instance_map = std::unordered_multimap<const void *, instance *>;
|
||||||
|
|
||||||
|
// ignore: structure was padded due to alignment specifier
|
||||||
|
PYBIND11_WARNING_PUSH
|
||||||
|
PYBIND11_WARNING_DISABLE_MSVC(4324)
|
||||||
|
|
||||||
|
// Instance map shards are used to reduce mutex contention in free-threaded Python.
|
||||||
|
struct alignas(64) instance_map_shard {
|
||||||
|
std::mutex mutex;
|
||||||
|
instance_map registered_instances;
|
||||||
|
};
|
||||||
|
|
||||||
|
PYBIND11_WARNING_POP
|
||||||
|
|
||||||
/// Internal data structure used to track registered instances and types.
|
/// Internal data structure used to track registered instances and types.
|
||||||
/// Whenever binary incompatible changes are made to this structure,
|
/// Whenever binary incompatible changes are made to this structure,
|
||||||
/// `PYBIND11_INTERNALS_VERSION` must be incremented.
|
/// `PYBIND11_INTERNALS_VERSION` must be incremented.
|
||||||
struct internals {
|
struct internals {
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
std::mutex mutex;
|
||||||
|
#endif
|
||||||
// std::type_index -> pybind11's type information
|
// std::type_index -> pybind11's type information
|
||||||
type_map<type_info *> registered_types_cpp;
|
type_map<type_info *> registered_types_cpp;
|
||||||
// PyTypeObject* -> base type_info(s)
|
// PyTypeObject* -> base type_info(s)
|
||||||
std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py;
|
std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py;
|
||||||
std::unordered_multimap<const void *, instance *> registered_instances; // void * -> instance*
|
#ifdef Py_GIL_DISABLED
|
||||||
|
std::unique_ptr<instance_map_shard[]> instance_shards; // void * -> instance*
|
||||||
|
size_t instance_shards_mask;
|
||||||
|
#else
|
||||||
|
instance_map registered_instances; // void * -> instance*
|
||||||
|
#endif
|
||||||
std::unordered_set<std::pair<const PyObject *, const char *>, override_hash>
|
std::unordered_set<std::pair<const PyObject *, const char *>, override_hash>
|
||||||
inactive_override_cache;
|
inactive_override_cache;
|
||||||
type_map<std::vector<bool (*)(PyObject *, void *&)>> direct_conversions;
|
type_map<std::vector<bool (*)(PyObject *, void *&)>> direct_conversions;
|
||||||
@ -462,7 +486,8 @@ inline object get_python_state_dict() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline object get_internals_obj_from_state_dict(handle state_dict) {
|
inline object get_internals_obj_from_state_dict(handle state_dict) {
|
||||||
return reinterpret_borrow<object>(dict_getitemstring(state_dict.ptr(), PYBIND11_INTERNALS_ID));
|
return reinterpret_steal<object>(
|
||||||
|
dict_getitemstringref(state_dict.ptr(), PYBIND11_INTERNALS_ID));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline internals **get_internals_pp_from_capsule(handle obj) {
|
inline internals **get_internals_pp_from_capsule(handle obj) {
|
||||||
@ -474,6 +499,20 @@ inline internals **get_internals_pp_from_capsule(handle obj) {
|
|||||||
return static_cast<internals **>(raw_ptr);
|
return static_cast<internals **>(raw_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline uint64_t round_up_to_next_pow2(uint64_t x) {
|
||||||
|
// Round-up to the next power of two.
|
||||||
|
// See https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
|
||||||
|
x--;
|
||||||
|
x |= (x >> 1);
|
||||||
|
x |= (x >> 2);
|
||||||
|
x |= (x >> 4);
|
||||||
|
x |= (x >> 8);
|
||||||
|
x |= (x >> 16);
|
||||||
|
x |= (x >> 32);
|
||||||
|
x++;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
/// Return a reference to the current `internals` data
|
/// Return a reference to the current `internals` data
|
||||||
PYBIND11_NOINLINE internals &get_internals() {
|
PYBIND11_NOINLINE internals &get_internals() {
|
||||||
auto **&internals_pp = get_internals_pp();
|
auto **&internals_pp = get_internals_pp();
|
||||||
@ -542,6 +581,16 @@ PYBIND11_NOINLINE internals &get_internals() {
|
|||||||
internals_ptr->static_property_type = make_static_property_type();
|
internals_ptr->static_property_type = make_static_property_type();
|
||||||
internals_ptr->default_metaclass = make_default_metaclass();
|
internals_ptr->default_metaclass = make_default_metaclass();
|
||||||
internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass);
|
internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass);
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
// Scale proportional to the number of cores. 2x is a heuristic to reduce contention.
|
||||||
|
auto num_shards
|
||||||
|
= static_cast<size_t>(round_up_to_next_pow2(2 * std::thread::hardware_concurrency()));
|
||||||
|
if (num_shards == 0) {
|
||||||
|
num_shards = 1;
|
||||||
|
}
|
||||||
|
internals_ptr->instance_shards.reset(new instance_map_shard[num_shards]);
|
||||||
|
internals_ptr->instance_shards_mask = num_shards - 1;
|
||||||
|
#endif // Py_GIL_DISABLED
|
||||||
}
|
}
|
||||||
return **internals_pp;
|
return **internals_pp;
|
||||||
}
|
}
|
||||||
@ -602,13 +651,80 @@ inline local_internals &get_local_internals() {
|
|||||||
return *locals;
|
return *locals;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
# define PYBIND11_LOCK_INTERNALS(internals) std::unique_lock<std::mutex> lock((internals).mutex)
|
||||||
|
#else
|
||||||
|
# define PYBIND11_LOCK_INTERNALS(internals)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
inline auto with_internals(const F &cb) -> decltype(cb(get_internals())) {
|
||||||
|
auto &internals = get_internals();
|
||||||
|
PYBIND11_LOCK_INTERNALS(internals);
|
||||||
|
return cb(internals);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::uint64_t mix64(std::uint64_t z) {
|
||||||
|
// David Stafford's variant 13 of the MurmurHash3 finalizer popularized
|
||||||
|
// by the SplitMix PRNG.
|
||||||
|
// https://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html
|
||||||
|
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
|
||||||
|
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
|
||||||
|
return z ^ (z >> 31);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
inline auto with_instance_map(const void *ptr,
|
||||||
|
const F &cb) -> decltype(cb(std::declval<instance_map &>())) {
|
||||||
|
auto &internals = get_internals();
|
||||||
|
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
// Hash address to compute shard, but ignore low bits. We'd like allocations
|
||||||
|
// from the same thread/core to map to the same shard and allocations from
|
||||||
|
// other threads/cores to map to other shards. Using the high bits is a good
|
||||||
|
// heuristic because memory allocators often have a per-thread
|
||||||
|
// arena/superblock/segment from which smaller allocations are served.
|
||||||
|
auto addr = reinterpret_cast<std::uintptr_t>(ptr);
|
||||||
|
auto hash = mix64(static_cast<std::uint64_t>(addr >> 20));
|
||||||
|
auto idx = static_cast<size_t>(hash & internals.instance_shards_mask);
|
||||||
|
|
||||||
|
auto &shard = internals.instance_shards[idx];
|
||||||
|
std::unique_lock<std::mutex> lock(shard.mutex);
|
||||||
|
return cb(shard.registered_instances);
|
||||||
|
#else
|
||||||
|
(void) ptr;
|
||||||
|
return cb(internals.registered_instances);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the number of registered instances for testing purposes. The result may not be
|
||||||
|
// consistent if other threads are registering or unregistering instances concurrently.
|
||||||
|
inline size_t num_registered_instances() {
|
||||||
|
auto &internals = get_internals();
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
size_t count = 0;
|
||||||
|
for (size_t i = 0; i <= internals.instance_shards_mask; ++i) {
|
||||||
|
auto &shard = internals.instance_shards[i];
|
||||||
|
std::unique_lock<std::mutex> lock(shard.mutex);
|
||||||
|
count += shard.registered_instances.size();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
#else
|
||||||
|
return internals.registered_instances.size();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its
|
/// 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
|
/// `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
|
/// cleared when the program exits or after interpreter shutdown (when embedding), and so are
|
||||||
/// suitable for c-style strings needed by Python internals (such as PyTypeObject's tp_name).
|
/// suitable for c-style strings needed by Python internals (such as PyTypeObject's tp_name).
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
const char *c_str(Args &&...args) {
|
const char *c_str(Args &&...args) {
|
||||||
auto &strings = get_internals().static_strings;
|
// GCC 4.8 doesn't like parameter unpack within lambda capture, so use
|
||||||
|
// PYBIND11_LOCK_INTERNALS.
|
||||||
|
auto &internals = get_internals();
|
||||||
|
PYBIND11_LOCK_INTERNALS(internals);
|
||||||
|
auto &strings = internals.static_strings;
|
||||||
strings.emplace_front(std::forward<Args>(args)...);
|
strings.emplace_front(std::forward<Args>(args)...);
|
||||||
return strings.front().c_str();
|
return strings.front().c_str();
|
||||||
}
|
}
|
||||||
@ -638,15 +754,18 @@ PYBIND11_NAMESPACE_END(detail)
|
|||||||
/// pybind11 version) running in the current interpreter. Names starting with underscores
|
/// pybind11 version) running in the current interpreter. Names starting with underscores
|
||||||
/// are reserved for internal usage. Returns `nullptr` if no matching entry was found.
|
/// are reserved for internal usage. Returns `nullptr` if no matching entry was found.
|
||||||
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();
|
return detail::with_internals([&](detail::internals &internals) {
|
||||||
auto it = internals.shared_data.find(name);
|
auto it = internals.shared_data.find(name);
|
||||||
return it != internals.shared_data.end() ? it->second : nullptr;
|
return it != internals.shared_data.end() ? it->second : nullptr;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the shared data that can be later recovered by `get_shared_data()`.
|
/// Set the shared data that can be later recovered by `get_shared_data()`.
|
||||||
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 detail::with_internals([&](detail::internals &internals) {
|
||||||
|
internals.shared_data[name] = data;
|
||||||
return data;
|
return data;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if
|
/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if
|
||||||
@ -654,14 +773,15 @@ PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) {
|
|||||||
/// added to the shared data under the given name and a reference to it is returned.
|
/// added to the shared data under the given name and a reference to it is returned.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T &get_or_create_shared_data(const std::string &name) {
|
T &get_or_create_shared_data(const std::string &name) {
|
||||||
auto &internals = detail::get_internals();
|
return *detail::with_internals([&](detail::internals &internals) {
|
||||||
auto it = internals.shared_data.find(name);
|
auto it = internals.shared_data.find(name);
|
||||||
T *ptr = (T *) (it != internals.shared_data.end() ? it->second : nullptr);
|
T *ptr = (T *) (it != internals.shared_data.end() ? it->second : nullptr);
|
||||||
if (!ptr) {
|
if (!ptr) {
|
||||||
ptr = new T();
|
ptr = new T();
|
||||||
internals.shared_data[name] = ptr;
|
internals.shared_data[name] = ptr;
|
||||||
}
|
}
|
||||||
return *ptr;
|
return ptr;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||||
|
@ -217,12 +217,15 @@ inline detail::type_info *get_local_type_info(const std::type_index &tp) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline detail::type_info *get_global_type_info(const std::type_index &tp) {
|
inline detail::type_info *get_global_type_info(const std::type_index &tp) {
|
||||||
auto &types = get_internals().registered_types_cpp;
|
return with_internals([&](internals &internals) {
|
||||||
|
detail::type_info *type_info = nullptr;
|
||||||
|
auto &types = internals.registered_types_cpp;
|
||||||
auto it = types.find(tp);
|
auto it = types.find(tp);
|
||||||
if (it != types.end()) {
|
if (it != types.end()) {
|
||||||
return it->second;
|
type_info = it->second;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return type_info;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the type info for a given C++ type; on lookup failure can either throw or return
|
/// Return the type info for a given C++ type; on lookup failure can either throw or return
|
||||||
@ -253,7 +256,8 @@ PYBIND11_NOINLINE handle get_type_handle(const std::type_info &tp, bool throw_if
|
|||||||
// Searches the inheritance graph for a registered Python instance, using all_type_info().
|
// Searches the inheritance graph for a registered Python instance, using all_type_info().
|
||||||
PYBIND11_NOINLINE handle find_registered_python_instance(void *src,
|
PYBIND11_NOINLINE handle find_registered_python_instance(void *src,
|
||||||
const detail::type_info *tinfo) {
|
const detail::type_info *tinfo) {
|
||||||
auto it_instances = get_internals().registered_instances.equal_range(src);
|
return with_instance_map(src, [&](instance_map &instances) {
|
||||||
|
auto it_instances = instances.equal_range(src);
|
||||||
for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) {
|
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))) {
|
for (auto *instance_type : detail::all_type_info(Py_TYPE(it_i->second))) {
|
||||||
if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype)) {
|
if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype)) {
|
||||||
@ -262,6 +266,7 @@ PYBIND11_NOINLINE handle find_registered_python_instance(void *src,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return handle();
|
return handle();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
struct value_and_holder {
|
struct value_and_holder {
|
||||||
@ -506,7 +511,7 @@ PYBIND11_NOINLINE bool isinstance_generic(handle obj, const std::type_info &tp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_info *type) {
|
PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_info *type) {
|
||||||
auto &instances = get_internals().registered_instances;
|
return with_instance_map(ptr, [&](instance_map &instances) {
|
||||||
auto range = instances.equal_range(ptr);
|
auto range = instances.equal_range(ptr);
|
||||||
for (auto it = range.first; it != range.second; ++it) {
|
for (auto it = range.first; it != range.second; ++it) {
|
||||||
for (const auto &vh : values_and_holders(it->second)) {
|
for (const auto &vh : values_and_holders(it->second)) {
|
||||||
@ -516,6 +521,7 @@ PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return handle();
|
return handle();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline PyThreadState *get_thread_state_unchecked() {
|
inline PyThreadState *get_thread_state_unchecked() {
|
||||||
|
@ -1553,7 +1553,9 @@ PYBIND11_NOINLINE void register_structured_dtype(any_container<field_descriptor>
|
|||||||
|
|
||||||
auto tindex = std::type_index(tinfo);
|
auto tindex = std::type_index(tinfo);
|
||||||
numpy_internals.registered_dtypes[tindex] = {dtype_ptr, std::move(format_str)};
|
numpy_internals.registered_dtypes[tindex] = {dtype_ptr, std::move(format_str)};
|
||||||
get_internals().direct_conversions[tindex].push_back(direct_converter);
|
with_internals([tindex, &direct_converter](internals &internals) {
|
||||||
|
internals.direct_conversions[tindex].push_back(direct_converter);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename SFINAE>
|
template <typename T, typename SFINAE>
|
||||||
|
@ -1054,13 +1054,20 @@ protected:
|
|||||||
- delegate translation to the next translator by throwing a new type of exception.
|
- delegate translation to the next translator by throwing a new type of exception.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
bool handled = with_internals([&](internals &internals) {
|
||||||
auto &local_exception_translators
|
auto &local_exception_translators
|
||||||
= get_local_internals().registered_exception_translators;
|
= get_local_internals().registered_exception_translators;
|
||||||
if (detail::apply_exception_translators(local_exception_translators)) {
|
if (detail::apply_exception_translators(local_exception_translators)) {
|
||||||
return nullptr;
|
return true;
|
||||||
}
|
}
|
||||||
auto &exception_translators = get_internals().registered_exception_translators;
|
auto &exception_translators = internals.registered_exception_translators;
|
||||||
if (detail::apply_exception_translators(exception_translators)) {
|
if (detail::apply_exception_translators(exception_translators)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (handled) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1199,6 +1206,16 @@ struct handle_type_name<cpp_function> {
|
|||||||
|
|
||||||
PYBIND11_NAMESPACE_END(detail)
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
|
||||||
|
// Use to activate Py_MOD_GIL_NOT_USED.
|
||||||
|
class mod_gil_not_used {
|
||||||
|
public:
|
||||||
|
explicit mod_gil_not_used(bool flag = true) : flag_(flag) {}
|
||||||
|
bool flag() const { return flag_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool flag_;
|
||||||
|
};
|
||||||
|
|
||||||
/// Wrapper for Python extension modules
|
/// Wrapper for Python extension modules
|
||||||
class module_ : public object {
|
class module_ : public object {
|
||||||
public:
|
public:
|
||||||
@ -1299,7 +1316,11 @@ public:
|
|||||||
|
|
||||||
``def`` should point to a statically allocated module_def.
|
``def`` should point to a statically allocated module_def.
|
||||||
\endrst */
|
\endrst */
|
||||||
static module_ create_extension_module(const char *name, const char *doc, module_def *def) {
|
static module_ create_extension_module(const char *name,
|
||||||
|
const char *doc,
|
||||||
|
module_def *def,
|
||||||
|
mod_gil_not_used gil_not_used
|
||||||
|
= mod_gil_not_used(false)) {
|
||||||
// module_def is PyModuleDef
|
// module_def is PyModuleDef
|
||||||
// Placement new (not an allocation).
|
// Placement new (not an allocation).
|
||||||
def = new (def)
|
def = new (def)
|
||||||
@ -1319,6 +1340,11 @@ public:
|
|||||||
}
|
}
|
||||||
pybind11_fail("Internal error in module_::create_extension_module()");
|
pybind11_fail("Internal error in module_::create_extension_module()");
|
||||||
}
|
}
|
||||||
|
if (gil_not_used.flag()) {
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
// TODO: Should be reinterpret_steal for Python 3, but Python also steals it again when
|
// TODO: Should be reinterpret_steal for Python 3, but Python also steals it again when
|
||||||
// returned from PyInit_...
|
// returned from PyInit_...
|
||||||
// For Python 2, reinterpret_borrow was correct.
|
// For Python 2, reinterpret_borrow was correct.
|
||||||
@ -1397,7 +1423,7 @@ protected:
|
|||||||
tinfo->default_holder = rec.default_holder;
|
tinfo->default_holder = rec.default_holder;
|
||||||
tinfo->module_local = rec.module_local;
|
tinfo->module_local = rec.module_local;
|
||||||
|
|
||||||
auto &internals = get_internals();
|
with_internals([&](internals &internals) {
|
||||||
auto tindex = std::type_index(*rec.type);
|
auto tindex = std::type_index(*rec.type);
|
||||||
tinfo->direct_conversions = &internals.direct_conversions[tindex];
|
tinfo->direct_conversions = &internals.direct_conversions[tindex];
|
||||||
if (rec.module_local) {
|
if (rec.module_local) {
|
||||||
@ -1406,6 +1432,7 @@ protected:
|
|||||||
internals.registered_types_cpp[tindex] = tinfo;
|
internals.registered_types_cpp[tindex] = tinfo;
|
||||||
}
|
}
|
||||||
internals.registered_types_py[(PyTypeObject *) m_ptr] = {tinfo};
|
internals.registered_types_py[(PyTypeObject *) m_ptr] = {tinfo};
|
||||||
|
});
|
||||||
|
|
||||||
if (rec.bases.size() > 1 || rec.multiple_inheritance) {
|
if (rec.bases.size() > 1 || rec.multiple_inheritance) {
|
||||||
mark_parents_nonsimple(tinfo->type);
|
mark_parents_nonsimple(tinfo->type);
|
||||||
@ -1618,10 +1645,12 @@ public:
|
|||||||
generic_type::initialize(record);
|
generic_type::initialize(record);
|
||||||
|
|
||||||
if (has_alias) {
|
if (has_alias) {
|
||||||
|
with_internals([&](internals &internals) {
|
||||||
auto &instances = record.module_local ? get_local_internals().registered_types_cpp
|
auto &instances = record.module_local ? get_local_internals().registered_types_cpp
|
||||||
: get_internals().registered_types_cpp;
|
: internals.registered_types_cpp;
|
||||||
instances[std::type_index(typeid(type_alias))]
|
instances[std::type_index(typeid(type_alias))]
|
||||||
= instances[std::type_index(typeid(type))];
|
= instances[std::type_index(typeid(type))];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2336,21 +2365,24 @@ keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret) {
|
|||||||
|
|
||||||
inline std::pair<decltype(internals::registered_types_py)::iterator, bool>
|
inline std::pair<decltype(internals::registered_types_py)::iterator, bool>
|
||||||
all_type_info_get_cache(PyTypeObject *type) {
|
all_type_info_get_cache(PyTypeObject *type) {
|
||||||
auto res = get_internals()
|
auto res = with_internals([type](internals &internals) {
|
||||||
|
return internals
|
||||||
.registered_types_py
|
.registered_types_py
|
||||||
#ifdef __cpp_lib_unordered_map_try_emplace
|
#ifdef __cpp_lib_unordered_map_try_emplace
|
||||||
.try_emplace(type);
|
.try_emplace(type);
|
||||||
#else
|
#else
|
||||||
.emplace(type, std::vector<detail::type_info *>());
|
.emplace(type, std::vector<detail::type_info *>());
|
||||||
#endif
|
#endif
|
||||||
|
});
|
||||||
if (res.second) {
|
if (res.second) {
|
||||||
// New cache entry created; set up a weak reference to automatically remove it if the type
|
// New cache entry created; set up a weak reference to automatically remove it if the type
|
||||||
// gets destroyed:
|
// gets destroyed:
|
||||||
weakref((PyObject *) type, cpp_function([type](handle wr) {
|
weakref((PyObject *) type, cpp_function([type](handle wr) {
|
||||||
get_internals().registered_types_py.erase(type);
|
with_internals([type](internals &internals) {
|
||||||
|
internals.registered_types_py.erase(type);
|
||||||
|
|
||||||
// TODO consolidate the erasure code in pybind11_meta_dealloc() in class.h
|
// TODO consolidate the erasure code in pybind11_meta_dealloc() in class.h
|
||||||
auto &cache = get_internals().inactive_override_cache;
|
auto &cache = internals.inactive_override_cache;
|
||||||
for (auto it = cache.begin(), last = cache.end(); it != last;) {
|
for (auto it = cache.begin(), last = cache.end(); it != last;) {
|
||||||
if (it->first == reinterpret_cast<PyObject *>(type)) {
|
if (it->first == reinterpret_cast<PyObject *>(type)) {
|
||||||
it = cache.erase(it);
|
it = cache.erase(it);
|
||||||
@ -2358,6 +2390,7 @@ all_type_info_get_cache(PyTypeObject *type) {
|
|||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
wr.dec_ref();
|
wr.dec_ref();
|
||||||
}))
|
}))
|
||||||
@ -2562,7 +2595,11 @@ void implicitly_convertible() {
|
|||||||
~set_flag() { flag = false; }
|
~set_flag() { flag = false; }
|
||||||
};
|
};
|
||||||
auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * {
|
auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * {
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
thread_local bool currently_used = false;
|
||||||
|
#else
|
||||||
static bool currently_used = false;
|
static bool currently_used = false;
|
||||||
|
#endif
|
||||||
if (currently_used) { // implicit conversions are non-reentrant
|
if (currently_used) { // implicit conversions are non-reentrant
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -2587,8 +2624,10 @@ void implicitly_convertible() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void register_exception_translator(ExceptionTranslator &&translator) {
|
inline void register_exception_translator(ExceptionTranslator &&translator) {
|
||||||
detail::get_internals().registered_exception_translators.push_front(
|
detail::with_internals([&](detail::internals &internals) {
|
||||||
|
internals.registered_exception_translators.push_front(
|
||||||
std::forward<ExceptionTranslator>(translator));
|
std::forward<ExceptionTranslator>(translator));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2598,8 +2637,11 @@ inline void register_exception_translator(ExceptionTranslator &&translator) {
|
|||||||
* the exception.
|
* the exception.
|
||||||
*/
|
*/
|
||||||
inline void register_local_exception_translator(ExceptionTranslator &&translator) {
|
inline void register_local_exception_translator(ExceptionTranslator &&translator) {
|
||||||
|
detail::with_internals([&](detail::internals &internals) {
|
||||||
|
(void) internals;
|
||||||
detail::get_local_internals().registered_exception_translators.push_front(
|
detail::get_local_internals().registered_exception_translators.push_front(
|
||||||
std::forward<ExceptionTranslator>(translator));
|
std::forward<ExceptionTranslator>(translator));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2756,14 +2798,19 @@ get_type_override(const void *this_ptr, const type_info *this_type, const char *
|
|||||||
|
|
||||||
/* Cache functions that aren't overridden in Python to avoid
|
/* Cache functions that aren't overridden in Python to avoid
|
||||||
many costly Python dictionary lookups below */
|
many costly Python dictionary lookups below */
|
||||||
auto &cache = get_internals().inactive_override_cache;
|
bool not_overridden = with_internals([&key](internals &internals) {
|
||||||
if (cache.find(key) != cache.end()) {
|
auto &cache = internals.inactive_override_cache;
|
||||||
|
return cache.find(key) != cache.end();
|
||||||
|
});
|
||||||
|
if (not_overridden) {
|
||||||
return function();
|
return function();
|
||||||
}
|
}
|
||||||
|
|
||||||
function override = getattr(self, name, function());
|
function override = getattr(self, name, function());
|
||||||
if (override.is_cpp_function()) {
|
if (override.is_cpp_function()) {
|
||||||
cache.insert(std::move(key));
|
with_internals([&](internals &internals) {
|
||||||
|
internals.inactive_override_cache.insert(std::move(key));
|
||||||
|
});
|
||||||
return function();
|
return function();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2868,10 +2915,14 @@ function get_override(const T *this_ptr, const char *name) {
|
|||||||
= pybind11::get_override(static_cast<const cname *>(this), name); \
|
= pybind11::get_override(static_cast<const cname *>(this), name); \
|
||||||
if (override) { \
|
if (override) { \
|
||||||
auto o = override(__VA_ARGS__); \
|
auto o = override(__VA_ARGS__); \
|
||||||
if (pybind11::detail::cast_is_temporary_value_reference<ret_type>::value) { \
|
PYBIND11_WARNING_PUSH \
|
||||||
|
PYBIND11_WARNING_DISABLE_MSVC(4127) \
|
||||||
|
if (pybind11::detail::cast_is_temporary_value_reference<ret_type>::value \
|
||||||
|
&& !pybind11::detail::is_same_ignoring_cvref<ret_type, PyObject *>::value) { \
|
||||||
static pybind11::detail::override_caster_t<ret_type> caster; \
|
static pybind11::detail::override_caster_t<ret_type> caster; \
|
||||||
return pybind11::detail::cast_ref<ret_type>(std::move(o), caster); \
|
return pybind11::detail::cast_ref<ret_type>(std::move(o), caster); \
|
||||||
} \
|
} \
|
||||||
|
PYBIND11_WARNING_POP \
|
||||||
return pybind11::detail::cast_safe<ret_type>(std::move(o)); \
|
return pybind11::detail::cast_safe<ret_type>(std::move(o)); \
|
||||||
} \
|
} \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
@ -980,6 +980,23 @@ inline PyObject *dict_getitem(PyObject *v, PyObject *key) {
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline PyObject *dict_getitemstringref(PyObject *v, const char *key) {
|
||||||
|
#if PY_VERSION_HEX >= 0x030D0000
|
||||||
|
PyObject *rv;
|
||||||
|
if (PyDict_GetItemStringRef(v, key, &rv) < 0) {
|
||||||
|
throw error_already_set();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
#else
|
||||||
|
PyObject *rv = dict_getitemstring(v, key);
|
||||||
|
if (rv == nullptr && PyErr_Occurred()) {
|
||||||
|
throw error_already_set();
|
||||||
|
}
|
||||||
|
Py_XINCREF(rv);
|
||||||
|
return rv;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Helper aliases/functions to support implicit casting of values given to python
|
// 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++
|
// 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`.
|
// type, the value goes through pybind11::cast(obj) to convert it to an `object`.
|
||||||
|
@ -63,6 +63,21 @@ class Callable<Return(Args...)> : public function {
|
|||||||
using function::function;
|
using function::function;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class Type : public type {
|
||||||
|
using type::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Types>
|
||||||
|
class Union : public object {
|
||||||
|
using object::object;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class Optional : public object {
|
||||||
|
using object::object;
|
||||||
|
};
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_END(typing)
|
PYBIND11_NAMESPACE_END(typing)
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
@ -121,5 +136,22 @@ struct handle_type_name<typing::Callable<Return(Args...)>> {
|
|||||||
+ const_name("], ") + make_caster<retval_type>::name + const_name("]");
|
+ const_name("], ") + make_caster<retval_type>::name + const_name("]");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct handle_type_name<typing::Type<T>> {
|
||||||
|
static constexpr auto name = const_name("type[") + make_caster<T>::name + const_name("]");
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Types>
|
||||||
|
struct handle_type_name<typing::Union<Types...>> {
|
||||||
|
static constexpr auto name = const_name("Union[")
|
||||||
|
+ ::pybind11::detail::concat(make_caster<Types>::name...)
|
||||||
|
+ const_name("]");
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct handle_type_name<typing::Optional<T>> {
|
||||||
|
static constexpr auto name = const_name("Optional[") + make_caster<T>::name + const_name("]");
|
||||||
|
};
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_END(detail)
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||||
|
@ -92,6 +92,9 @@ extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_gil_utils() {
|
|||||||
if (m != nullptr) {
|
if (m != nullptr) {
|
||||||
static_assert(sizeof(&gil_acquire) == sizeof(void *),
|
static_assert(sizeof(&gil_acquire) == sizeof(void *),
|
||||||
"Function pointer must have the same size as void*");
|
"Function pointer must have the same size as void*");
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
|
||||||
|
#endif
|
||||||
ADD_FUNCTION("gil_acquire_funcaddr", gil_acquire)
|
ADD_FUNCTION("gil_acquire_funcaddr", gil_acquire)
|
||||||
ADD_FUNCTION("gil_multi_acquire_release_funcaddr", gil_multi_acquire_release)
|
ADD_FUNCTION("gil_multi_acquire_release_funcaddr", gil_multi_acquire_release)
|
||||||
ADD_FUNCTION("gil_acquire_inner_custom_funcaddr",
|
ADD_FUNCTION("gil_acquire_inner_custom_funcaddr",
|
||||||
|
@ -42,6 +42,9 @@ extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_interleaved_error_alrea
|
|||||||
if (m != nullptr) {
|
if (m != nullptr) {
|
||||||
static_assert(sizeof(&interleaved_error_already_set) == sizeof(void *),
|
static_assert(sizeof(&interleaved_error_already_set) == sizeof(void *),
|
||||||
"Function pointer must have the same size as void *");
|
"Function pointer must have the same size as void *");
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
|
||||||
|
#endif
|
||||||
PyModule_AddObject(
|
PyModule_AddObject(
|
||||||
m,
|
m,
|
||||||
"funcaddr",
|
"funcaddr",
|
||||||
|
@ -11,4 +11,6 @@
|
|||||||
|
|
||||||
#include "test_eigen_tensor.inl"
|
#include "test_eigen_tensor.inl"
|
||||||
|
|
||||||
PYBIND11_MODULE(eigen_tensor_avoid_stl_array, m) { eigen_tensor_test::test_module(m); }
|
PYBIND11_MODULE(eigen_tensor_avoid_stl_array, m, pybind11::mod_gil_not_used()) {
|
||||||
|
eigen_tensor_test::test_module(m);
|
||||||
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
|
import sysconfig
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -9,6 +10,7 @@ WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin")
|
|||||||
|
|
||||||
CPYTHON = platform.python_implementation() == "CPython"
|
CPYTHON = platform.python_implementation() == "CPython"
|
||||||
PYPY = platform.python_implementation() == "PyPy"
|
PYPY = platform.python_implementation() == "PyPy"
|
||||||
|
PY_GIL_DISABLED = bool(sysconfig.get_config_var("Py_GIL_DISABLED"))
|
||||||
|
|
||||||
|
|
||||||
def deprecated_call():
|
def deprecated_call():
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
PYBIND11_MODULE(pybind11_cross_module_tests, m) {
|
PYBIND11_MODULE(pybind11_cross_module_tests, m, py::mod_gil_not_used()) {
|
||||||
m.doc() = "pybind11 cross-module test module";
|
m.doc() = "pybind11 cross-module test module";
|
||||||
|
|
||||||
// test_local_bindings.py tests:
|
// test_local_bindings.py tests:
|
||||||
|
@ -58,7 +58,7 @@ void bind_ConstructorStats(py::module_ &m) {
|
|||||||
// registered instances to allow instance cleanup checks (invokes a GC first)
|
// registered instances to allow instance cleanup checks (invokes a GC first)
|
||||||
.def_static("detail_reg_inst", []() {
|
.def_static("detail_reg_inst", []() {
|
||||||
ConstructorStats::gc();
|
ConstructorStats::gc();
|
||||||
return py::detail::get_internals().registered_instances.size();
|
return py::detail::num_registered_instances();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ const char *cpp_std() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
PYBIND11_MODULE(pybind11_tests, m) {
|
PYBIND11_MODULE(pybind11_tests, m, py::mod_gil_not_used()) {
|
||||||
m.doc() = "pybind11 test module";
|
m.doc() = "pybind11 test module";
|
||||||
|
|
||||||
// Intentionally kept minimal to not create a maintenance chore
|
// Intentionally kept minimal to not create a maintenance chore
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include <pybind11/pybind11.h>
|
#include <pybind11/pybind11.h>
|
||||||
namespace py = pybind11;
|
namespace py = pybind11;
|
||||||
|
|
||||||
PYBIND11_MODULE(test_cmake_build, m) {
|
PYBIND11_MODULE(test_cmake_build, m, py::mod_gil_not_used()) {
|
||||||
m.def("add", [](int i, int j) { return i + j; });
|
m.def("add", [](int i, int j) { return i + j; });
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ namespace py = pybind11;
|
|||||||
* modules aren't preserved over a finalize/initialize.
|
* modules aren't preserved over a finalize/initialize.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
PYBIND11_MODULE(external_module, m) {
|
PYBIND11_MODULE(external_module, m, py::mod_gil_not_used()) {
|
||||||
class A {
|
class A {
|
||||||
public:
|
public:
|
||||||
explicit A(int value) : v{value} {};
|
explicit A(int value) : v{value} {};
|
||||||
|
@ -844,4 +844,27 @@ TEST_SUBMODULE(pytypes, m) {
|
|||||||
m.def("annotate_iterator_int", [](const py::typing::Iterator<int> &) {});
|
m.def("annotate_iterator_int", [](const py::typing::Iterator<int> &) {});
|
||||||
m.def("annotate_fn",
|
m.def("annotate_fn",
|
||||||
[](const py::typing::Callable<int(py::typing::List<py::str>, py::str)> &) {});
|
[](const py::typing::Callable<int(py::typing::List<py::str>, py::str)> &) {});
|
||||||
|
m.def("annotate_type", [](const py::typing::Type<int> &) {});
|
||||||
|
|
||||||
|
m.def("annotate_union",
|
||||||
|
[](py::typing::List<py::typing::Union<py::str, py::int_, py::object>> l,
|
||||||
|
py::str a,
|
||||||
|
py::int_ b,
|
||||||
|
py::object c) -> py::typing::List<py::typing::Union<py::str, py::int_, py::object>> {
|
||||||
|
l.append(a);
|
||||||
|
l.append(b);
|
||||||
|
l.append(c);
|
||||||
|
return l;
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("union_typing_only",
|
||||||
|
[](py::typing::List<py::typing::Union<py::str>> &l)
|
||||||
|
-> py::typing::List<py::typing::Union<py::int_>> { return l; });
|
||||||
|
|
||||||
|
m.def("annotate_optional",
|
||||||
|
[](py::list &list) -> py::typing::List<py::typing::Optional<py::str>> {
|
||||||
|
list.append(py::str("hi"));
|
||||||
|
list.append(py::none());
|
||||||
|
return list;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -955,3 +955,28 @@ def test_fn_annotations(doc):
|
|||||||
doc(m.annotate_fn)
|
doc(m.annotate_fn)
|
||||||
== "annotate_fn(arg0: Callable[[list[str], str], int]) -> None"
|
== "annotate_fn(arg0: Callable[[list[str], str], int]) -> None"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_type_annotation(doc):
|
||||||
|
assert doc(m.annotate_type) == "annotate_type(arg0: type[int]) -> None"
|
||||||
|
|
||||||
|
|
||||||
|
def test_union_annotations(doc):
|
||||||
|
assert (
|
||||||
|
doc(m.annotate_union)
|
||||||
|
== "annotate_union(arg0: list[Union[str, int, object]], arg1: str, arg2: int, arg3: object) -> list[Union[str, int, object]]"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_union_typing_only(doc):
|
||||||
|
assert (
|
||||||
|
doc(m.union_typing_only)
|
||||||
|
== "union_typing_only(arg0: list[Union[str]]) -> list[Union[int]]"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_optional_annotations(doc):
|
||||||
|
assert (
|
||||||
|
doc(m.annotate_optional)
|
||||||
|
== "annotate_optional(arg0: list) -> list[Optional[str]]"
|
||||||
|
)
|
||||||
|
@ -5,9 +5,10 @@
|
|||||||
#include "pybind11_tests.h"
|
#include "pybind11_tests.h"
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace {
|
namespace test_type_caster_pyobject_ptr {
|
||||||
|
|
||||||
std::vector<PyObject *> make_vector_pyobject_ptr(const py::object &ValueHolder) {
|
std::vector<PyObject *> make_vector_pyobject_ptr(const py::object &ValueHolder) {
|
||||||
std::vector<PyObject *> vec_obj;
|
std::vector<PyObject *> vec_obj;
|
||||||
@ -18,9 +19,39 @@ std::vector<PyObject *> make_vector_pyobject_ptr(const py::object &ValueHolder)
|
|||||||
return vec_obj;
|
return vec_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
struct WithPyObjectPtrReturn {
|
||||||
|
#if defined(__clang_major__) && __clang_major__ < 4
|
||||||
|
WithPyObjectPtrReturn() = default;
|
||||||
|
WithPyObjectPtrReturn(const WithPyObjectPtrReturn &) = default;
|
||||||
|
#endif
|
||||||
|
virtual ~WithPyObjectPtrReturn() = default;
|
||||||
|
virtual PyObject *return_pyobject_ptr() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WithPyObjectPtrReturnTrampoline : WithPyObjectPtrReturn {
|
||||||
|
PyObject *return_pyobject_ptr() const override {
|
||||||
|
PYBIND11_OVERRIDE_PURE(PyObject *, WithPyObjectPtrReturn, return_pyobject_ptr,
|
||||||
|
/* no arguments */);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string call_return_pyobject_ptr(const WithPyObjectPtrReturn *base_class_ptr) {
|
||||||
|
PyObject *returned_obj = base_class_ptr->return_pyobject_ptr();
|
||||||
|
#if !defined(PYPY_VERSION) // It is not worth the trouble doing something special for PyPy.
|
||||||
|
if (Py_REFCNT(returned_obj) != 1) {
|
||||||
|
py::pybind11_fail(__FILE__ ":" PYBIND11_TOSTRING(__LINE__));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
auto ret_val = py::repr(returned_obj).cast<std::string>();
|
||||||
|
Py_DECREF(returned_obj);
|
||||||
|
return ret_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test_type_caster_pyobject_ptr
|
||||||
|
|
||||||
TEST_SUBMODULE(type_caster_pyobject_ptr, m) {
|
TEST_SUBMODULE(type_caster_pyobject_ptr, m) {
|
||||||
|
using namespace test_type_caster_pyobject_ptr;
|
||||||
|
|
||||||
m.def("cast_from_pyobject_ptr", []() {
|
m.def("cast_from_pyobject_ptr", []() {
|
||||||
PyObject *ptr = PyLong_FromLongLong(6758L);
|
PyObject *ptr = PyLong_FromLongLong(6758L);
|
||||||
return py::cast(ptr, py::return_value_policy::take_ownership);
|
return py::cast(ptr, py::return_value_policy::take_ownership);
|
||||||
@ -127,4 +158,10 @@ TEST_SUBMODULE(type_caster_pyobject_ptr, m) {
|
|||||||
(void) py::cast(*ptr);
|
(void) py::cast(*ptr);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
py::class_<WithPyObjectPtrReturn, WithPyObjectPtrReturnTrampoline>(m, "WithPyObjectPtrReturn")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def("return_pyobject_ptr", &WithPyObjectPtrReturn::return_pyobject_ptr);
|
||||||
|
|
||||||
|
m.def("call_return_pyobject_ptr", call_return_pyobject_ptr);
|
||||||
}
|
}
|
||||||
|
@ -102,3 +102,19 @@ def test_return_list_pyobject_ptr_reference():
|
|||||||
def test_type_caster_name_via_incompatible_function_arguments_type_error():
|
def test_type_caster_name_via_incompatible_function_arguments_type_error():
|
||||||
with pytest.raises(TypeError, match=r"1\. \(arg0: object, arg1: int\) -> None"):
|
with pytest.raises(TypeError, match=r"1\. \(arg0: object, arg1: int\) -> None"):
|
||||||
m.pass_pyobject_ptr_and_int(ValueHolder(101), ValueHolder(202))
|
m.pass_pyobject_ptr_and_int(ValueHolder(101), ValueHolder(202))
|
||||||
|
|
||||||
|
|
||||||
|
def test_trampoline_with_pyobject_ptr_return():
|
||||||
|
class Drvd(m.WithPyObjectPtrReturn):
|
||||||
|
def return_pyobject_ptr(self):
|
||||||
|
return ["11", "22", "33"]
|
||||||
|
|
||||||
|
# Basic health check: First make sure this works as expected.
|
||||||
|
d = Drvd()
|
||||||
|
assert d.return_pyobject_ptr() == ["11", "22", "33"]
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# This failed before PR #5156: AddressSanitizer: heap-use-after-free ... in Py_DECREF
|
||||||
|
d_repr = m.call_return_pyobject_ptr(d)
|
||||||
|
assert d_repr == repr(["11", "22", "33"])
|
||||||
|
break # Comment out for manual leak checking.
|
||||||
|
Loading…
Reference in New Issue
Block a user