mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-11 08:03:55 +00:00
Compare commits
39 Commits
68f71f3efd
...
2bf5f5550a
Author | SHA1 | Date | |
---|---|---|---|
|
2bf5f5550a | ||
|
1f8b4a7f1a | ||
|
ad9fd39e14 | ||
|
1d9483ff73 | ||
|
a7910be630 | ||
|
0cf3a0f7b5 | ||
|
5b7c0b04b9 | ||
|
ef5a9560bb | ||
|
5efc7439d4 | ||
|
8a801bdc32 | ||
|
aeda49ed0b | ||
|
66c3774a64 | ||
|
65f4266cef | ||
|
3fb16ad175 | ||
|
e8f595bb85 | ||
|
c2291e597f | ||
|
efa2b20d69 | ||
|
9966ad409d | ||
|
2baf9d6833 | ||
|
7d85baa6a1 | ||
|
a1d00916b2 | ||
|
bd5951b691 | ||
|
28dbce4157 | ||
|
d893f9723a | ||
|
fc97cc41d5 | ||
|
0116906189 | ||
|
0d44d720cb | ||
|
4a06eca591 | ||
|
8d9f4d50cc | ||
|
1fe92c7b35 | ||
|
40f2c7863b | ||
|
8d90b83b19 | ||
|
fc59f4e6e5 | ||
|
898794488a | ||
|
20551ab3d8 | ||
|
845105383f | ||
|
916778df48 | ||
|
cbb0590ac7 | ||
|
b3c9615d2d |
10
.github/CONTRIBUTING.md
vendored
10
.github/CONTRIBUTING.md
vendored
@ -81,7 +81,7 @@ nox -s build
|
|||||||
### Full setup
|
### Full setup
|
||||||
|
|
||||||
To setup an ideal development environment, run the following commands on a
|
To setup an ideal development environment, run the following commands on a
|
||||||
system with CMake 3.14+:
|
system with CMake 3.15+:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 -m venv venv
|
python3 -m venv venv
|
||||||
@ -96,8 +96,8 @@ Tips:
|
|||||||
* You can use `virtualenv` (faster, from PyPI) instead of `venv`.
|
* You can use `virtualenv` (faster, from PyPI) instead of `venv`.
|
||||||
* You can select any name for your environment folder; if it contains "env" it
|
* You can select any name for your environment folder; if it contains "env" it
|
||||||
will be ignored by git.
|
will be ignored by git.
|
||||||
* If you don't have CMake 3.14+, just add "cmake" to the pip install command.
|
* If you don't have CMake 3.15+, just add "cmake" to the pip install command.
|
||||||
* You can use `-DPYBIND11_FINDPYTHON=ON` to use FindPython on CMake 3.12+
|
* You can use `-DPYBIND11_FINDPYTHON=ON` to use FindPython.
|
||||||
* In classic mode, you may need to set `-DPYTHON_EXECUTABLE=/path/to/python`.
|
* In classic mode, you may need to set `-DPYTHON_EXECUTABLE=/path/to/python`.
|
||||||
FindPython uses `-DPython_ROOT_DIR=/path/to` or
|
FindPython uses `-DPython_ROOT_DIR=/path/to` or
|
||||||
`-DPython_EXECUTABLE=/path/to/python`.
|
`-DPython_EXECUTABLE=/path/to/python`.
|
||||||
@ -149,8 +149,8 @@ To run the tests, you can "build" the check target:
|
|||||||
cmake --build build --target check
|
cmake --build build --target check
|
||||||
```
|
```
|
||||||
|
|
||||||
`--target` can be spelled `-t` in CMake 3.15+. You can also run individual
|
`--target` can be spelled `-t`. You can also run individual tests with these
|
||||||
tests with these targets:
|
targets:
|
||||||
|
|
||||||
* `pytest`: Python tests only, using the
|
* `pytest`: Python tests only, using the
|
||||||
[pytest](https://docs.pytest.org/en/stable/) framework
|
[pytest](https://docs.pytest.org/en/stable/) framework
|
||||||
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -243,7 +243,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Python ${{ matrix.python-version }} (deadsnakes)
|
- name: Setup Python ${{ matrix.python-version }} (deadsnakes)
|
||||||
uses: deadsnakes/action@v3.1.0
|
uses: deadsnakes/action@v3.2.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
debug: ${{ matrix.python-debug }}
|
debug: ${{ matrix.python-debug }}
|
||||||
|
4
.github/workflows/configure.yml
vendored
4
.github/workflows/configure.yml
vendored
@ -31,7 +31,7 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
- runs-on: ubuntu-20.04
|
- runs-on: ubuntu-20.04
|
||||||
arch: x64
|
arch: x64
|
||||||
cmake: "3.5"
|
cmake: "3.15"
|
||||||
|
|
||||||
- runs-on: ubuntu-20.04
|
- runs-on: ubuntu-20.04
|
||||||
arch: x64
|
arch: x64
|
||||||
@ -39,7 +39,7 @@ jobs:
|
|||||||
|
|
||||||
- runs-on: macos-13
|
- runs-on: macos-13
|
||||||
arch: x64
|
arch: x64
|
||||||
cmake: "3.8"
|
cmake: "3.15"
|
||||||
|
|
||||||
- runs-on: windows-2019
|
- runs-on: windows-2019
|
||||||
arch: x64 # x86 compilers seem to be missing on 2019 image
|
arch: x64 # x86 compilers seem to be missing on 2019 image
|
||||||
|
6
.github/workflows/emscripten.yaml
vendored
6
.github/workflows/emscripten.yaml
vendored
@ -5,6 +5,8 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
- stable
|
||||||
|
- v*
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
@ -20,11 +22,9 @@ jobs:
|
|||||||
submodules: true
|
submodules: true
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- uses: pypa/cibuildwheel@v2.19
|
- uses: pypa/cibuildwheel@v2.21
|
||||||
env:
|
env:
|
||||||
PYODIDE_BUILD_EXPORTS: whole_archive
|
PYODIDE_BUILD_EXPORTS: whole_archive
|
||||||
CFLAGS: -fexceptions
|
|
||||||
LDFLAGS: -fexceptions
|
|
||||||
with:
|
with:
|
||||||
package-dir: tests
|
package-dir: tests
|
||||||
only: cp312-pyodide_wasm32
|
only: cp312-pyodide_wasm32
|
||||||
|
9
.github/workflows/pip.yml
vendored
9
.github/workflows/pip.yml
vendored
@ -91,18 +91,19 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event_name == 'release' && github.event.action == 'published'
|
if: github.event_name == 'release' && github.event.action == 'published'
|
||||||
needs: [packaging]
|
needs: [packaging]
|
||||||
environment: pypi
|
environment:
|
||||||
|
name: pypi
|
||||||
|
url: https://pypi.org/p/pybind11
|
||||||
permissions:
|
permissions:
|
||||||
id-token: write
|
id-token: write
|
||||||
attestations: write
|
attestations: write
|
||||||
contents: read
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# Downloads all to directories matching the artifact names
|
# Downloads all to directories matching the artifact names
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v4
|
||||||
|
|
||||||
- name: Generate artifact attestation for sdist and wheel
|
- name: Generate artifact attestation for sdist and wheel
|
||||||
uses: actions/attest-build-provenance@5e9cb68e95676991667494a6a4e59b8a2f13e1d0 # v1.3.3
|
uses: actions/attest-build-provenance@1c608d11d69870c2092266b3f9a6f3abbf17002c # v1.4.3
|
||||||
with:
|
with:
|
||||||
subject-path: "*/pybind11*"
|
subject-path: "*/pybind11*"
|
||||||
|
|
||||||
@ -110,8 +111,10 @@ jobs:
|
|||||||
uses: pypa/gh-action-pypi-publish@release/v1
|
uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
with:
|
with:
|
||||||
packages-dir: standard/
|
packages-dir: standard/
|
||||||
|
attestations: true
|
||||||
|
|
||||||
- name: Publish global package
|
- name: Publish global package
|
||||||
uses: pypa/gh-action-pypi-publish@release/v1
|
uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
with:
|
with:
|
||||||
packages-dir: global/
|
packages-dir: global/
|
||||||
|
attestations: true
|
||||||
|
@ -32,7 +32,7 @@ repos:
|
|||||||
|
|
||||||
# Ruff, the Python auto-correcting linter/formatter written in Rust
|
# Ruff, the Python auto-correcting linter/formatter written in Rust
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: v0.5.0
|
rev: v0.6.3
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
args: ["--fix", "--show-fixes"]
|
args: ["--fix", "--show-fixes"]
|
||||||
@ -40,7 +40,7 @@ repos:
|
|||||||
|
|
||||||
# Check static types with mypy
|
# Check static types with mypy
|
||||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
rev: "v1.10.1"
|
rev: "v1.11.2"
|
||||||
hooks:
|
hooks:
|
||||||
- id: mypy
|
- id: mypy
|
||||||
args: []
|
args: []
|
||||||
@ -93,7 +93,7 @@ repos:
|
|||||||
|
|
||||||
# Avoid directional quotes
|
# Avoid directional quotes
|
||||||
- repo: https://github.com/sirosen/texthooks
|
- repo: https://github.com/sirosen/texthooks
|
||||||
rev: "0.6.6"
|
rev: "0.6.7"
|
||||||
hooks:
|
hooks:
|
||||||
- id: fix-ligatures
|
- id: fix-ligatures
|
||||||
- id: fix-smartquotes
|
- id: fix-smartquotes
|
||||||
@ -142,14 +142,14 @@ repos:
|
|||||||
|
|
||||||
# PyLint has native support - not always usable, but works for us
|
# PyLint has native support - not always usable, but works for us
|
||||||
- repo: https://github.com/PyCQA/pylint
|
- repo: https://github.com/PyCQA/pylint
|
||||||
rev: "v3.2.4"
|
rev: "v3.2.7"
|
||||||
hooks:
|
hooks:
|
||||||
- id: pylint
|
- id: pylint
|
||||||
files: ^pybind11
|
files: ^pybind11
|
||||||
|
|
||||||
# Check schemas on some of our YAML files
|
# Check schemas on some of our YAML files
|
||||||
- repo: https://github.com/python-jsonschema/check-jsonschema
|
- repo: https://github.com/python-jsonschema/check-jsonschema
|
||||||
rev: 0.28.6
|
rev: 0.29.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-readthedocs
|
- id: check-readthedocs
|
||||||
- id: check-github-workflows
|
- id: check-github-workflows
|
||||||
|
@ -10,16 +10,7 @@ if(NOT CMAKE_VERSION VERSION_LESS "3.27")
|
|||||||
cmake_policy(GET CMP0148 _pybind11_cmp0148)
|
cmake_policy(GET CMP0148 _pybind11_cmp0148)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.15...3.30)
|
||||||
|
|
||||||
# The `cmake_minimum_required(VERSION 3.5...3.29)` 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.29)
|
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
|
||||||
else()
|
|
||||||
cmake_policy(VERSION 3.29)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(_pybind11_cmp0148)
|
if(_pybind11_cmp0148)
|
||||||
cmake_policy(SET CMP0148 ${_pybind11_cmp0148})
|
cmake_policy(SET CMP0148 ${_pybind11_cmp0148})
|
||||||
@ -27,9 +18,7 @@ if(_pybind11_cmp0148)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Avoid infinite recursion if tests include this as a subdirectory
|
# Avoid infinite recursion if tests include this as a subdirectory
|
||||||
if(DEFINED PYBIND11_MASTER_PROJECT)
|
include_guard(GLOBAL)
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Extract project version from source
|
# Extract project version from source
|
||||||
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/pybind11/detail/common.h"
|
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/pybind11/detail/common.h"
|
||||||
@ -74,14 +63,6 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
|
|||||||
|
|
||||||
set(PYBIND11_MASTER_PROJECT ON)
|
set(PYBIND11_MASTER_PROJECT ON)
|
||||||
|
|
||||||
if(OSX AND CMAKE_VERSION VERSION_LESS 3.7)
|
|
||||||
# Bug in macOS CMake < 3.7 is unable to download catch
|
|
||||||
message(WARNING "CMAKE 3.7+ needed on macOS to download catch, and newer HIGHLY recommended")
|
|
||||||
elseif(WINDOWS AND CMAKE_VERSION VERSION_LESS 3.8)
|
|
||||||
# Only tested with 3.8+ in CI.
|
|
||||||
message(WARNING "CMAKE 3.8+ tested on Windows, previous versions untested")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
message(STATUS "CMake ${CMAKE_VERSION}")
|
message(STATUS "CMake ${CMAKE_VERSION}")
|
||||||
|
|
||||||
if(CMAKE_CXX_STANDARD)
|
if(CMAKE_CXX_STANDARD)
|
||||||
@ -133,8 +114,7 @@ cmake_dependent_option(
|
|||||||
"Install pybind11 headers in Python include directory instead of default installation prefix"
|
"Install pybind11 headers in Python include directory instead of default installation prefix"
|
||||||
OFF "PYBIND11_INSTALL" OFF)
|
OFF "PYBIND11_INSTALL" OFF)
|
||||||
|
|
||||||
cmake_dependent_option(PYBIND11_FINDPYTHON "Force new FindPython" ${_pybind11_findpython_default}
|
option(PYBIND11_FINDPYTHON "Force new FindPython" ${_pybind11_findpython_default})
|
||||||
"NOT CMAKE_VERSION VERSION_LESS 3.12" OFF)
|
|
||||||
|
|
||||||
# Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests
|
# Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests
|
||||||
# (makes transition easier while we support both modes).
|
# (makes transition easier while we support both modes).
|
||||||
@ -149,12 +129,14 @@ endif()
|
|||||||
set(PYBIND11_HEADERS
|
set(PYBIND11_HEADERS
|
||||||
include/pybind11/detail/class.h
|
include/pybind11/detail/class.h
|
||||||
include/pybind11/detail/common.h
|
include/pybind11/detail/common.h
|
||||||
|
include/pybind11/detail/cpp_conduit.h
|
||||||
include/pybind11/detail/descr.h
|
include/pybind11/detail/descr.h
|
||||||
include/pybind11/detail/init.h
|
include/pybind11/detail/init.h
|
||||||
include/pybind11/detail/internals.h
|
include/pybind11/detail/internals.h
|
||||||
include/pybind11/detail/type_caster_base.h
|
include/pybind11/detail/type_caster_base.h
|
||||||
include/pybind11/detail/typeid.h
|
include/pybind11/detail/typeid.h
|
||||||
include/pybind11/detail/value_and_holder.h
|
include/pybind11/detail/value_and_holder.h
|
||||||
|
include/pybind11/detail/exception_translation.h
|
||||||
include/pybind11/attr.h
|
include/pybind11/attr.h
|
||||||
include/pybind11/buffer_info.h
|
include/pybind11/buffer_info.h
|
||||||
include/pybind11/cast.h
|
include/pybind11/cast.h
|
||||||
@ -180,10 +162,11 @@ set(PYBIND11_HEADERS
|
|||||||
include/pybind11/stl_bind.h
|
include/pybind11/stl_bind.h
|
||||||
include/pybind11/stl/filesystem.h
|
include/pybind11/stl/filesystem.h
|
||||||
include/pybind11/type_caster_pyobject_ptr.h
|
include/pybind11/type_caster_pyobject_ptr.h
|
||||||
include/pybind11/typing.h)
|
include/pybind11/typing.h
|
||||||
|
include/pybind11/warnings.h)
|
||||||
|
|
||||||
# Compare with grep and warn if mismatched
|
# Compare with grep and warn if mismatched
|
||||||
if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12)
|
if(PYBIND11_MASTER_PROJECT)
|
||||||
file(
|
file(
|
||||||
GLOB_RECURSE _pybind11_header_check
|
GLOB_RECURSE _pybind11_header_check
|
||||||
LIST_DIRECTORIES false
|
LIST_DIRECTORIES false
|
||||||
@ -201,10 +184,7 @@ if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# CMake 3.12 added list(TRANSFORM <list> PREPEND
|
list(TRANSFORM PYBIND11_HEADERS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/")
|
||||||
# But we can't use it yet
|
|
||||||
string(REPLACE "include/" "${CMAKE_CURRENT_SOURCE_DIR}/include/" PYBIND11_HEADERS
|
|
||||||
"${PYBIND11_HEADERS}")
|
|
||||||
|
|
||||||
# Cache variable so this can be used in parent projects
|
# Cache variable so this can be used in parent projects
|
||||||
set(pybind11_INCLUDE_DIR
|
set(pybind11_INCLUDE_DIR
|
||||||
@ -274,25 +254,11 @@ if(PYBIND11_INSTALL)
|
|||||||
tools/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
tools/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||||
INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR})
|
INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR})
|
||||||
|
|
||||||
if(CMAKE_VERSION VERSION_LESS 3.14)
|
# CMake natively supports header-only libraries
|
||||||
# Remove CMAKE_SIZEOF_VOID_P from ConfigVersion.cmake since the library does
|
write_basic_package_version_file(
|
||||||
# not depend on architecture specific settings or libraries.
|
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
|
||||||
set(_PYBIND11_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P})
|
VERSION ${PROJECT_VERSION}
|
||||||
unset(CMAKE_SIZEOF_VOID_P)
|
COMPATIBILITY AnyNewerVersion ARCH_INDEPENDENT)
|
||||||
|
|
||||||
write_basic_package_version_file(
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
|
|
||||||
VERSION ${PROJECT_VERSION}
|
|
||||||
COMPATIBILITY AnyNewerVersion)
|
|
||||||
|
|
||||||
set(CMAKE_SIZEOF_VOID_P ${_PYBIND11_CMAKE_SIZEOF_VOID_P})
|
|
||||||
else()
|
|
||||||
# CMake 3.14+ natively supports header-only libraries
|
|
||||||
write_basic_package_version_file(
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
|
|
||||||
VERSION ${PROJECT_VERSION}
|
|
||||||
COMPATIBILITY AnyNewerVersion ARCH_INDEPENDENT)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
install(
|
install(
|
||||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
|
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
|
||||||
|
@ -259,7 +259,7 @@ copying to take place:
|
|||||||
"small"_a // <- This one can be copied if needed
|
"small"_a // <- This one can be copied if needed
|
||||||
);
|
);
|
||||||
|
|
||||||
With the above binding code, attempting to call the the ``some_method(m)``
|
With the above binding code, attempting to call the ``some_method(m)``
|
||||||
method on a ``MyClass`` object, or attempting to call ``some_function(m, m2)``
|
method on a ``MyClass`` object, or attempting to call ``some_function(m, m2)``
|
||||||
will raise a ``RuntimeError`` rather than making a temporary copy of the array.
|
will raise a ``RuntimeError`` rather than making a temporary copy of the array.
|
||||||
It will, however, allow the ``m2`` argument to be copied into a temporary if
|
It will, however, allow the ``m2`` argument to be copied into a temporary if
|
||||||
|
@ -162,7 +162,7 @@ the declaration
|
|||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
PYBIND11_MAKE_OPAQUE(std::vector<int>);
|
PYBIND11_MAKE_OPAQUE(std::vector<int>)
|
||||||
|
|
||||||
before any binding code (e.g. invocations to ``class_::def()``, etc.). This
|
before any binding code (e.g. invocations to ``class_::def()``, etc.). This
|
||||||
macro must be specified at the top level (and outside of any namespaces), since
|
macro must be specified at the top level (and outside of any namespaces), since
|
||||||
@ -207,8 +207,8 @@ The following example showcases usage of :file:`pybind11/stl_bind.h`:
|
|||||||
// Don't forget this
|
// Don't forget this
|
||||||
#include <pybind11/stl_bind.h>
|
#include <pybind11/stl_bind.h>
|
||||||
|
|
||||||
PYBIND11_MAKE_OPAQUE(std::vector<int>);
|
PYBIND11_MAKE_OPAQUE(std::vector<int>)
|
||||||
PYBIND11_MAKE_OPAQUE(std::map<std::string, double>);
|
PYBIND11_MAKE_OPAQUE(std::map<std::string, double>)
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
|
@ -866,6 +866,8 @@ which should look as follows:
|
|||||||
|
|
||||||
.. [#f5] https://docs.python.org/3/library/copy.html
|
.. [#f5] https://docs.python.org/3/library/copy.html
|
||||||
|
|
||||||
|
.. _multiple_inheritance:
|
||||||
|
|
||||||
Multiple Inheritance
|
Multiple Inheritance
|
||||||
====================
|
====================
|
||||||
|
|
||||||
@ -902,6 +904,121 @@ are listed.
|
|||||||
|
|
||||||
.. _module_local:
|
.. _module_local:
|
||||||
|
|
||||||
|
For more complex multiple-inheritance class architectures with virtual methods, it might be necessary to combine
|
||||||
|
two different Trampoline class hierarchies. A templating trick can be used in this case to interleaf another
|
||||||
|
trampoline class (-hierarchy):
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
class Animal {
|
||||||
|
public:
|
||||||
|
virtual ~Animal() { }
|
||||||
|
virtual std::string go(int n_times) = 0;
|
||||||
|
};
|
||||||
|
template <class AnimalBase = Animal>
|
||||||
|
class PyAnimal : public AnimalBase {
|
||||||
|
public:
|
||||||
|
using AnimalBase::AnimalBase; // Inherit constructors
|
||||||
|
std::string go(int n_times) override { PYBIND11_OVERRIDE_PURE(std::string, AnimalBase, go, n_times); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Dog : public Animal {
|
||||||
|
public:
|
||||||
|
std::string go(int n_times) override;
|
||||||
|
};
|
||||||
|
template <class DogBase = Dog>
|
||||||
|
class PyDog : public PyAnimal<DogBase> {
|
||||||
|
public:
|
||||||
|
using PyAnimal<DogBase>::PyAnimal; // Inherit constructors
|
||||||
|
std::string go(int n_times) override { PYBIND11_OVERRIDE(std::string, DogBase, go, n_times); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Mutant {
|
||||||
|
public:
|
||||||
|
virtual ~Mutant() { }
|
||||||
|
virtual void transform();
|
||||||
|
};
|
||||||
|
template <class MutantBase = Mutant, class PyMutantBase = MutantBase>
|
||||||
|
class PyMutant : public PyMutantBase {
|
||||||
|
public:
|
||||||
|
using PyMutantBase::PyMutantBase; // Inherit constructors
|
||||||
|
void transform() override { PYBIND11_OVERRIDE_PURE(void, MutantBase, transform, ); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Chimera : public Dog, public Mutant {
|
||||||
|
public:
|
||||||
|
virtual ~Chimera() { }
|
||||||
|
};
|
||||||
|
template <class ChimeraBase = Chimera>
|
||||||
|
class PyChimera : public PyMutant<ChimeraBase, PyDog<ChimeraBase>> {
|
||||||
|
public:
|
||||||
|
using PyMutant<ChimeraBase, PyDog<ChimeraBase>>::PyMutant; // Inherit constructors
|
||||||
|
};
|
||||||
|
|
||||||
|
The class ``Chimera`` inherits from both ``Dog`` and ``Mutant`` both of which feature virtual methods and
|
||||||
|
trampoline classes for binding. However, the ``Mutant`` trampoline class uses a second template parameter
|
||||||
|
``PyMutantBase`` so it can be injected into the single inheritance structure required by a trampoline class.
|
||||||
|
In effect, this mechanism enforces that the actual class the trampolines are using is only inherited from once.
|
||||||
|
Since the trampolines only need to add their respective trampoline function registrations, the order of the
|
||||||
|
inheritance of the various trampoline classes does not matter.
|
||||||
|
|
||||||
|
If the base classes contain pure virtual methods, another pattern can be applied to reduce the amount of
|
||||||
|
trampoline code that needs writing. The cost is an additional ``std::same`` call for each pure-virtual
|
||||||
|
method using the macro ``PYBIND11_OVERRIDE_TEMPLATE``.
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
class Animal {
|
||||||
|
public:
|
||||||
|
virtual ~Animal() { }
|
||||||
|
virtual std::string go(int n_times) = 0;
|
||||||
|
};
|
||||||
|
class Dog : public Animal {
|
||||||
|
public:
|
||||||
|
std::string go(int n_times) override;
|
||||||
|
};
|
||||||
|
template <class AnimalBase = Animal, class PureVirtualBase = Animal>
|
||||||
|
class PyAnimal : public AnimalBase {
|
||||||
|
public:
|
||||||
|
using AnimalBase::AnimalBase; // Inherit constructors
|
||||||
|
std::string go(int n_times) override { PYBIND11_OVERRIDE_TEMPLATE(PureVirtualBase, std::string, AnimalBase, go, n_times); }
|
||||||
|
};
|
||||||
|
using PyDog = PyAnimal<Dog>
|
||||||
|
|
||||||
|
class Mutant {
|
||||||
|
public:
|
||||||
|
virtual ~Mutant() { }
|
||||||
|
virtual void transform() = 0;
|
||||||
|
};
|
||||||
|
class XMen : public Mutant{
|
||||||
|
public:
|
||||||
|
virtual ~Mutant() { }
|
||||||
|
void transform() override;
|
||||||
|
};
|
||||||
|
template <class MutantBase = Mutant, class PyMutantBase = MutantBase, class PureVirtualBase = Mutant>
|
||||||
|
class PyMutant : public PyMutantBase {
|
||||||
|
public:
|
||||||
|
using PyMutantBase::PyMutantBase; // Inherit constructors
|
||||||
|
void transform() override { PYBIND11_OVERRIDE_TEMPLATE(PureVirtualBase, void, MutantBase, transform, ); }
|
||||||
|
};
|
||||||
|
using PyXMen = PyMutant<XMen>
|
||||||
|
|
||||||
|
class Chimera : public Dog, public Mutant {
|
||||||
|
public:
|
||||||
|
virtual ~Chimera() { }
|
||||||
|
};
|
||||||
|
template <class ChimeraBase = Chimera, class PyChimeraBase = ChimeraBase>
|
||||||
|
class PyChimera : public PyMutant<ChimeraBase, PyAnimal<ChimeraBase, PyChimeraBase>> {
|
||||||
|
public:
|
||||||
|
using PyMutant<ChimeraBase, PyAnimal<ChimeraBase, PyChimeraBase>>::PyMutant; // Inherit constructors
|
||||||
|
};
|
||||||
|
|
||||||
|
The first parameter of the :c:macro:`PYBIND11_OVERRIDE_TEMPLATE` is the base class containing
|
||||||
|
the pure virtual method. Together with the cname parameter, an ``std::same`` call is used to
|
||||||
|
invoke either :c:macro:`PYBIND11_OVERRIDE_PURE` or :c:macro:`PYBIND11_OVERRIDE`. A corresponding
|
||||||
|
:c:macro:`PYBIND11_OVERRIDE_TEMPLATE_NAME` implementation is also available. The template parameter
|
||||||
|
``PureVirtualBase`` can be used in case the pure virtual methods are not implemented in a child class.
|
||||||
|
|
||||||
Module-local class bindings
|
Module-local class bindings
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ information, see :doc:`/compiling`.
|
|||||||
|
|
||||||
.. code-block:: cmake
|
.. code-block:: cmake
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.5...3.29)
|
cmake_minimum_required(VERSION 3.15...3.30)
|
||||||
project(example)
|
project(example)
|
||||||
|
|
||||||
find_package(pybind11 REQUIRED) # or `add_subdirectory(pybind11)`
|
find_package(pybind11 REQUIRED) # or `add_subdirectory(pybind11)`
|
||||||
|
@ -124,7 +124,7 @@ top namespace level before any binding code:
|
|||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>);
|
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>)
|
||||||
|
|
||||||
The first argument of :func:`PYBIND11_DECLARE_HOLDER_TYPE` should be a
|
The first argument of :func:`PYBIND11_DECLARE_HOLDER_TYPE` should be a
|
||||||
placeholder name that is used as a template parameter of the second argument.
|
placeholder name that is used as a template parameter of the second argument.
|
||||||
@ -136,7 +136,7 @@ by default. Specify
|
|||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>, true);
|
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>, true)
|
||||||
|
|
||||||
if ``SmartPtr<T>`` can always be initialized from a ``T*`` pointer without the
|
if ``SmartPtr<T>`` can always be initialized from a ``T*`` pointer without the
|
||||||
risk of inconsistencies (such as multiple independent ``SmartPtr`` instances
|
risk of inconsistencies (such as multiple independent ``SmartPtr`` instances
|
||||||
@ -154,7 +154,7 @@ specialized:
|
|||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
// Always needed for custom holder types
|
// Always needed for custom holder types
|
||||||
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>);
|
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>)
|
||||||
|
|
||||||
// Only needed if the type's `.get()` goes by another name
|
// Only needed if the type's `.get()` goes by another name
|
||||||
namespace PYBIND11_NAMESPACE { namespace detail {
|
namespace PYBIND11_NAMESPACE { namespace detail {
|
||||||
|
@ -78,6 +78,13 @@ For brevity, all code examples assume that the following two lines are present:
|
|||||||
|
|
||||||
namespace py = pybind11;
|
namespace py = pybind11;
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
``pybind11/pybind11.h`` includes ``Python.h``, as such it must be the first file
|
||||||
|
included in any source file or header for `the same reasons as Python.h`_.
|
||||||
|
|
||||||
|
.. _`the same reasons as Python.h`: https://docs.python.org/3/extending/extending.html#a-simple-example
|
||||||
|
|
||||||
Some features may require additional headers, but those will be specified as needed.
|
Some features may require additional headers, but those will be specified as needed.
|
||||||
|
|
||||||
.. _simple_example:
|
.. _simple_example:
|
||||||
|
@ -15,6 +15,146 @@ IN DEVELOPMENT
|
|||||||
|
|
||||||
Changes will be summarized here periodically.
|
Changes will be summarized here periodically.
|
||||||
|
|
||||||
|
New Features:
|
||||||
|
|
||||||
|
* Support for Python 3.7 was removed. (Official end-of-life: 2023-06-27).
|
||||||
|
`#5191 <https://github.com/pybind/pybind11/pull/5191>`_
|
||||||
|
|
||||||
|
* stl.h ``list|set|map_caster`` were made more user friendly: it is no longer
|
||||||
|
necessary to explicitly convert Python iterables to ``tuple()``, ``set()``,
|
||||||
|
or ``map()`` in many common situations.
|
||||||
|
`#4686 <https://github.com/pybind/pybind11/pull/4686>`_
|
||||||
|
|
||||||
|
* Support for CMake older than 3.15 removed. CMake 3.15-3.30 supported.
|
||||||
|
`#5304 <https://github.com/pybind/pybind11/pull/5304>`_
|
||||||
|
|
||||||
|
* The ``array_caster`` in pybind11/stl.h was enhanced to support value types that are not default-constructible.
|
||||||
|
`#5305 <https://github.com/pybind/pybind11/pull/5305>`_
|
||||||
|
|
||||||
|
* Added ``py::warnings`` namespace with ``py::warnings::warn`` and ``py::warnings::new_warning_type`` that provides the interface for Python warnings.
|
||||||
|
`#5291 <https://github.com/pybind/pybind11/pull/5291>`_
|
||||||
|
|
||||||
|
Version 2.13.6 (September 13, 2024)
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
New Features:
|
||||||
|
|
||||||
|
* A new ``self._pybind11_conduit_v1_()`` method is automatically added to all
|
||||||
|
``py::class_``-wrapped types, to enable type-safe interoperability between
|
||||||
|
different independent Python/C++ bindings systems, including pybind11
|
||||||
|
versions with different ``PYBIND11_INTERNALS_VERSION``'s. Supported on
|
||||||
|
pybind11 2.11.2, 2.12.1, and 2.13.6+.
|
||||||
|
`#5296 <https://github.com/pybind/pybind11/pull/5296>`_
|
||||||
|
|
||||||
|
|
||||||
|
Bug fixes:
|
||||||
|
|
||||||
|
* Using ``__cpp_nontype_template_args`` instead of ``__cpp_nontype_template_parameter_class``.
|
||||||
|
`#5330 <https://github.com/pybind/pybind11/pull/5330>`_
|
||||||
|
|
||||||
|
* Properly translate C++ exception to Python exception when creating Python buffer from wrapped object.
|
||||||
|
`#5324 <https://github.com/pybind/pybind11/pull/5324>`_
|
||||||
|
|
||||||
|
|
||||||
|
Documentation:
|
||||||
|
|
||||||
|
* Adds an answer (FAQ) for "What is a highly conclusive and simple way to find memory leaks?".
|
||||||
|
`#5340 <https://github.com/pybind/pybind11/pull/5340>`_
|
||||||
|
|
||||||
|
|
||||||
|
Version 2.13.5 (August 22, 2024)
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
Bug fixes:
|
||||||
|
|
||||||
|
* Fix includes when using Windows long paths (``\\?\`` prefix).
|
||||||
|
`#5321 <https://github.com/pybind/pybind11/pull/5321>`_
|
||||||
|
|
||||||
|
* Support ``-Wpedantic`` in C++20 mode.
|
||||||
|
`#5322 <https://github.com/pybind/pybind11/pull/5322>`_
|
||||||
|
|
||||||
|
* Fix and test ``<ranges>`` support for ``py::tuple`` and ``py::list``.
|
||||||
|
`#5314 <https://github.com/pybind/pybind11/pull/5314>`_
|
||||||
|
|
||||||
|
Version 2.13.4 (August 14, 2024)
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
Bug fixes:
|
||||||
|
|
||||||
|
* Fix paths with spaces, including on Windows.
|
||||||
|
(Replaces regression from `#5302 <https://github.com/pybind/pybind11/pull/5302>`_)
|
||||||
|
`#4874 <https://github.com/pybind/pybind11/pull/4874>`_
|
||||||
|
|
||||||
|
Documentation:
|
||||||
|
|
||||||
|
* Remove repetitive words.
|
||||||
|
`#5308 <https://github.com/pybind/pybind11/pull/5308>`_
|
||||||
|
|
||||||
|
|
||||||
|
Version 2.13.3 (August 13, 2024)
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
Bug fixes:
|
||||||
|
|
||||||
|
* Quote paths from pybind11-config
|
||||||
|
`#5302 <https://github.com/pybind/pybind11/pull/5302>`_
|
||||||
|
|
||||||
|
|
||||||
|
* Fix typo in Emscripten support when in config mode (CMake)
|
||||||
|
`#5301 <https://github.com/pybind/pybind11/pull/5301>`_
|
||||||
|
|
||||||
|
|
||||||
|
Version 2.13.2 (August 13, 2024)
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
New Features:
|
||||||
|
|
||||||
|
* A ``pybind11::detail::type_caster_std_function_specializations`` feature was added, to support specializations for
|
||||||
|
``std::function``'s with return types that require custom to-Python conversion behavior (to primary use case is to catch and
|
||||||
|
convert exceptions).
|
||||||
|
`#4597 <https://github.com/pybind/pybind11/pull/4597>`_
|
||||||
|
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
|
||||||
|
* Use ``PyMutex`` instead of ``std::mutex`` for internal locking in the free-threaded build.
|
||||||
|
`#5219 <https://github.com/pybind/pybind11/pull/5219>`_
|
||||||
|
|
||||||
|
* Add a special type annotation for C++ empty tuple.
|
||||||
|
`#5214 <https://github.com/pybind/pybind11/pull/5214>`_
|
||||||
|
|
||||||
|
* When compiling for WebAssembly, add the required exception flags (CMake 3.13+).
|
||||||
|
`#5298 <https://github.com/pybind/pybind11/pull/5298>`_
|
||||||
|
|
||||||
|
Bug fixes:
|
||||||
|
|
||||||
|
* Make ``gil_safe_call_once_and_store`` thread-safe in free-threaded CPython.
|
||||||
|
`#5246 <https://github.com/pybind/pybind11/pull/5246>`_
|
||||||
|
|
||||||
|
* A missing ``#include <algorithm>`` in pybind11/typing.h was added to fix build errors (in case user code does not already depend
|
||||||
|
on that include).
|
||||||
|
`#5208 <https://github.com/pybind/pybind11/pull/5208>`_
|
||||||
|
|
||||||
|
* Fix regression introduced in #5201 for GCC<10.3 in C++20 mode.
|
||||||
|
`#5205 <https://github.com/pybind/pybind11/pull/5205>`_
|
||||||
|
|
||||||
|
|
||||||
|
.. fix(cmake)
|
||||||
|
|
||||||
|
* Remove extra = when assigning flto value in the case for Clang in CMake.
|
||||||
|
`#5207 <https://github.com/pybind/pybind11/pull/5207>`_
|
||||||
|
|
||||||
|
|
||||||
|
Tests:
|
||||||
|
|
||||||
|
* Adding WASM testing to our CI (Pyodide / Emscripten via scikit-build-core).
|
||||||
|
`#4745 <https://github.com/pybind/pybind11/pull/4745>`_
|
||||||
|
|
||||||
|
* clang-tidy (in GitHub Actions) was updated from clang 15 to clang 18.
|
||||||
|
`#5272 <https://github.com/pybind/pybind11/pull/5272>`_
|
||||||
|
|
||||||
|
|
||||||
Version 2.13.1 (June 26, 2024)
|
Version 2.13.1 (June 26, 2024)
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
@ -129,6 +269,18 @@ Other:
|
|||||||
* Update docs and noxfile.
|
* Update docs and noxfile.
|
||||||
`#5071 <https://github.com/pybind/pybind11/pull/5071>`_
|
`#5071 <https://github.com/pybind/pybind11/pull/5071>`_
|
||||||
|
|
||||||
|
Version 2.12.1 (September 13, 2024)
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
New Features:
|
||||||
|
|
||||||
|
* A new ``self._pybind11_conduit_v1_()`` method is automatically added to all
|
||||||
|
``py::class_``-wrapped types, to enable type-safe interoperability between
|
||||||
|
different independent Python/C++ bindings systems, including pybind11
|
||||||
|
versions with different ``PYBIND11_INTERNALS_VERSION``'s. Supported on
|
||||||
|
pybind11 2.11.2, 2.12.1, and 2.13.6+.
|
||||||
|
`#5296 <https://github.com/pybind/pybind11/pull/5296>`_
|
||||||
|
|
||||||
|
|
||||||
Version 2.12.0 (March 27, 2024)
|
Version 2.12.0 (March 27, 2024)
|
||||||
-------------------------------
|
-------------------------------
|
||||||
@ -304,6 +456,18 @@ Other:
|
|||||||
* An ``assert()`` was added to help Coverty avoid generating a false positive.
|
* An ``assert()`` was added to help Coverty avoid generating a false positive.
|
||||||
`#4817 <https://github.com/pybind/pybind11/pull/4817>`_
|
`#4817 <https://github.com/pybind/pybind11/pull/4817>`_
|
||||||
|
|
||||||
|
Version 2.11.2 (September 13, 2024)
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
New Features:
|
||||||
|
|
||||||
|
* A new ``self._pybind11_conduit_v1_()`` method is automatically added to all
|
||||||
|
``py::class_``-wrapped types, to enable type-safe interoperability between
|
||||||
|
different independent Python/C++ bindings systems, including pybind11
|
||||||
|
versions with different ``PYBIND11_INTERNALS_VERSION``'s. Supported on
|
||||||
|
pybind11 2.11.2, 2.12.1, and 2.13.6+.
|
||||||
|
`#5296 <https://github.com/pybind/pybind11/pull/5296>`_
|
||||||
|
|
||||||
|
|
||||||
Version 2.11.1 (July 17, 2023)
|
Version 2.11.1 (July 17, 2023)
|
||||||
------------------------------
|
------------------------------
|
||||||
|
@ -18,14 +18,14 @@ A Python extension module can be created with just a few lines of code:
|
|||||||
|
|
||||||
.. code-block:: cmake
|
.. code-block:: cmake
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.15...3.29)
|
cmake_minimum_required(VERSION 3.15...3.30)
|
||||||
project(example LANGUAGES CXX)
|
project(example LANGUAGES CXX)
|
||||||
|
|
||||||
set(PYBIND11_FINDPYTHON ON)
|
set(PYBIND11_FINDPYTHON ON)
|
||||||
find_package(pybind11 CONFIG REQUIRED)
|
find_package(pybind11 CONFIG REQUIRED)
|
||||||
|
|
||||||
pybind11_add_module(example example.cpp)
|
pybind11_add_module(example example.cpp)
|
||||||
install(TARGET example DESTINATION .)
|
install(TARGETS example DESTINATION .)
|
||||||
|
|
||||||
(You use the ``add_subdirectory`` instead, see the example in :ref:`cmake`.) In
|
(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
|
this example, the code is located in a file named :file:`example.cpp`. Either
|
||||||
@ -319,11 +319,11 @@ 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, as seen above in
|
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
|
the module section. Pybind11 currently defaults to the old mechanism, though be
|
||||||
use the modern FindPython, though be aware that CMake 3.27 removed the old
|
aware that CMake 3.27 removed the old mechanism, so pybind11 will automatically
|
||||||
mechanism, so pybind11 will automatically switch if the old mechanism is not
|
switch if the old mechanism is not available. Please opt into the new mechanism
|
||||||
available. Please opt into the new mechanism if at all possible. Our default
|
if at all possible. Our default may change in future versions. This is the
|
||||||
may change in future versions. This is the minimum required:
|
minimum required:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -333,6 +333,9 @@ may change in future versions. This is the minimum required:
|
|||||||
.. versionchanged:: 2.11
|
.. versionchanged:: 2.11
|
||||||
CMake 3.5+ is required.
|
CMake 3.5+ is required.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.14
|
||||||
|
CMake 3.15+ is required.
|
||||||
|
|
||||||
|
|
||||||
Further information can be found at :doc:`cmake/index`.
|
Further information can be found at :doc:`cmake/index`.
|
||||||
|
|
||||||
@ -388,7 +391,7 @@ that will be respected instead of the built-in flag search.
|
|||||||
|
|
||||||
The ``OPT_SIZE`` flag enables size-based optimization equivalent to the
|
The ``OPT_SIZE`` flag enables size-based optimization equivalent to the
|
||||||
standard ``/Os`` or ``-Os`` compiler flags and the ``MinSizeRel`` build type,
|
standard ``/Os`` or ``-Os`` compiler flags and the ``MinSizeRel`` build type,
|
||||||
which avoid optimizations that that can substantially increase the size of the
|
which avoid optimizations that can substantially increase the size of the
|
||||||
resulting binary. This flag is particularly useful in projects that are split
|
resulting binary. This flag is particularly useful in projects that are split
|
||||||
into performance-critical parts and associated bindings. In this case, we can
|
into performance-critical parts and associated bindings. In this case, we can
|
||||||
compile the project in release mode (and hence, optimize performance globally),
|
compile the project in release mode (and hence, optimize performance globally),
|
||||||
@ -444,7 +447,7 @@ See the `Config file`_ docstring for details of relevant CMake variables.
|
|||||||
|
|
||||||
.. code-block:: cmake
|
.. code-block:: cmake
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.4...3.18)
|
cmake_minimum_required(VERSION 3.15...3.30)
|
||||||
project(example LANGUAGES CXX)
|
project(example LANGUAGES CXX)
|
||||||
|
|
||||||
find_package(pybind11 REQUIRED)
|
find_package(pybind11 REQUIRED)
|
||||||
@ -483,14 +486,13 @@ can refer to the same [cmake_example]_ repository for a full sample project
|
|||||||
FindPython mode
|
FindPython mode
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
CMake 3.12+ (3.15+ recommended, 3.18.2+ ideal) added a new module called
|
Modern CMake (3.18.2+ ideal) added a new module called FindPython that had a
|
||||||
FindPython that had a highly improved search algorithm and modern targets
|
highly improved search algorithm and modern targets and tools. If you use
|
||||||
and tools. If you use FindPython, pybind11 will detect this and use the
|
FindPython, pybind11 will detect this and use the existing targets instead:
|
||||||
existing targets instead:
|
|
||||||
|
|
||||||
.. code-block:: cmake
|
.. code-block:: cmake
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.15...3.22)
|
cmake_minimum_required(VERSION 3.15...3.30)
|
||||||
project(example LANGUAGES CXX)
|
project(example LANGUAGES CXX)
|
||||||
|
|
||||||
find_package(Python 3.8 COMPONENTS Interpreter Development REQUIRED)
|
find_package(Python 3.8 COMPONENTS Interpreter Development REQUIRED)
|
||||||
@ -541,7 +543,7 @@ available in all modes. The targets provided are:
|
|||||||
Just the "linking" part of pybind11:module
|
Just the "linking" part of pybind11:module
|
||||||
|
|
||||||
``pybind11::module``
|
``pybind11::module``
|
||||||
Everything for extension modules - ``pybind11::pybind11`` + ``Python::Module`` (FindPython CMake 3.15+) or ``pybind11::python_link_helper``
|
Everything for extension modules - ``pybind11::pybind11`` + ``Python::Module`` (FindPython) or ``pybind11::python_link_helper``
|
||||||
|
|
||||||
``pybind11::embed``
|
``pybind11::embed``
|
||||||
Everything for embedding the Python interpreter - ``pybind11::pybind11`` + ``Python::Python`` (FindPython) or Python libs
|
Everything for embedding the Python interpreter - ``pybind11::pybind11`` + ``Python::Python`` (FindPython) or Python libs
|
||||||
@ -568,7 +570,7 @@ You can use these targets to build complex applications. For example, the
|
|||||||
|
|
||||||
.. code-block:: cmake
|
.. code-block:: cmake
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.5...3.29)
|
cmake_minimum_required(VERSION 3.15...3.30)
|
||||||
project(example LANGUAGES CXX)
|
project(example LANGUAGES CXX)
|
||||||
|
|
||||||
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
|
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
|
||||||
@ -626,7 +628,7 @@ information about usage in C++, see :doc:`/advanced/embedding`.
|
|||||||
|
|
||||||
.. code-block:: cmake
|
.. code-block:: cmake
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.5...3.29)
|
cmake_minimum_required(VERSION 3.15...3.30)
|
||||||
project(example LANGUAGES CXX)
|
project(example LANGUAGES CXX)
|
||||||
|
|
||||||
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
|
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
|
||||||
@ -719,7 +721,7 @@ customizable pybind11-based wrappers by parsing C++ header files.
|
|||||||
|
|
||||||
[litgen]_ is an automatic python bindings generator with a focus on generating
|
[litgen]_ is an automatic python bindings generator with a focus on generating
|
||||||
documented and discoverable bindings: bindings will nicely reproduce the documentation
|
documented and discoverable bindings: bindings will nicely reproduce the documentation
|
||||||
found in headers. It is is based on srcML (srcml.org), a highly scalable, multi-language
|
found in headers. It is based on srcML (srcml.org), a highly scalable, multi-language
|
||||||
parsing tool with a developer centric approach. The API that you want to expose to python
|
parsing tool with a developer centric approach. The API that you want to expose to python
|
||||||
must be C++14 compatible (but your implementation can use more modern constructs).
|
must be C++14 compatible (but your implementation can use more modern constructs).
|
||||||
|
|
||||||
|
60
docs/faq.rst
60
docs/faq.rst
@ -247,6 +247,50 @@ been received, you must either explicitly interrupt execution by throwing
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
What is a highly conclusive and simple way to find memory leaks (e.g. in pybind11 bindings)?
|
||||||
|
============================================================================================
|
||||||
|
|
||||||
|
Use ``while True`` & ``top`` (Linux, macOS).
|
||||||
|
|
||||||
|
For example, locally change tests/test_type_caster_pyobject_ptr.py like this:
|
||||||
|
|
||||||
|
.. code-block:: diff
|
||||||
|
|
||||||
|
def test_return_list_pyobject_ptr_reference():
|
||||||
|
+ while True:
|
||||||
|
vec_obj = m.return_list_pyobject_ptr_reference(ValueHolder)
|
||||||
|
assert [e.value for e in vec_obj] == [93, 186]
|
||||||
|
# Commenting out the next `assert` will leak the Python references.
|
||||||
|
# An easy way to see evidence of the leaks:
|
||||||
|
# Insert `while True:` as the first line of this function and monitor the
|
||||||
|
# process RES (Resident Memory Size) with the Unix top command.
|
||||||
|
- assert m.dec_ref_each_pyobject_ptr(vec_obj) == 2
|
||||||
|
+ # assert m.dec_ref_each_pyobject_ptr(vec_obj) == 2
|
||||||
|
|
||||||
|
Then run the test as you would normally do, which will go into the infinite loop.
|
||||||
|
|
||||||
|
**In another shell, but on the same machine** run:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
top
|
||||||
|
|
||||||
|
This will show:
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
|
||||||
|
1266095 rwgk 20 0 5207496 611372 45696 R 100.0 0.3 0:08.01 test_type_caste
|
||||||
|
|
||||||
|
Look for the number under ``RES`` there. You'll see it going up very quickly.
|
||||||
|
|
||||||
|
**Don't forget to Ctrl-C the test command** before your machine becomes
|
||||||
|
unresponsive due to swapping.
|
||||||
|
|
||||||
|
This method only takes a couple minutes of effort and is very conclusive.
|
||||||
|
What you want to see is that the ``RES`` number is stable after a couple
|
||||||
|
seconds.
|
||||||
|
|
||||||
CMake doesn't detect the right Python version
|
CMake doesn't detect the right Python version
|
||||||
=============================================
|
=============================================
|
||||||
|
|
||||||
@ -258,9 +302,9 @@ CMake configure line. (Replace ``$(which python)`` with a path to python if
|
|||||||
your prefer.)
|
your prefer.)
|
||||||
|
|
||||||
You can alternatively try ``-DPYBIND11_FINDPYTHON=ON``, which will activate the
|
You can alternatively try ``-DPYBIND11_FINDPYTHON=ON``, which will activate the
|
||||||
new CMake FindPython support instead of pybind11's custom search. Requires
|
new CMake FindPython support instead of pybind11's custom search. Newer CMake,
|
||||||
CMake 3.12+, and 3.15+ or 3.18.2+ are even better. You can set this in your
|
like, 3.18.2+, is recommended. You can set this in your ``CMakeLists.txt``
|
||||||
``CMakeLists.txt`` before adding or finding pybind11, as well.
|
before adding or finding pybind11, as well.
|
||||||
|
|
||||||
Inconsistent detection of Python version in CMake and pybind11
|
Inconsistent detection of Python version in CMake and pybind11
|
||||||
==============================================================
|
==============================================================
|
||||||
@ -281,11 +325,11 @@ There are three possible solutions:
|
|||||||
from CMake and rely on pybind11 in detecting Python version. If this is not
|
from CMake and rely on pybind11 in detecting Python version. If this is not
|
||||||
possible, the CMake machinery should be called *before* including pybind11.
|
possible, the CMake machinery should be called *before* including pybind11.
|
||||||
2. Set ``PYBIND11_FINDPYTHON`` to ``True`` or use ``find_package(Python
|
2. Set ``PYBIND11_FINDPYTHON`` to ``True`` or use ``find_package(Python
|
||||||
COMPONENTS Interpreter Development)`` on modern CMake (3.12+, 3.15+ better,
|
COMPONENTS Interpreter Development)`` on modern CMake ( 3.18.2+ best).
|
||||||
3.18.2+ best). Pybind11 in these cases uses the new CMake FindPython instead
|
Pybind11 in these cases uses the new CMake FindPython instead of the old,
|
||||||
of the old, deprecated search tools, and these modules are much better at
|
deprecated search tools, and these modules are much better at finding the
|
||||||
finding the correct Python. If FindPythonLibs/Interp are not available
|
correct Python. If FindPythonLibs/Interp are not available (CMake 3.27+),
|
||||||
(CMake 3.27+), then this will be ignored and FindPython will be used.
|
then this will be ignored and FindPython will be used.
|
||||||
3. Set ``PYBIND11_NOPYTHON`` to ``TRUE``. Pybind11 will not search for Python.
|
3. Set ``PYBIND11_NOPYTHON`` to ``TRUE``. Pybind11 will not search for Python.
|
||||||
However, you will have to use the target-based system, and do more setup
|
However, you will have to use the target-based system, and do more setup
|
||||||
yourself, because it does not know about or include things that depend on
|
yourself, because it does not know about or include things that depend on
|
||||||
|
@ -9,8 +9,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../attr.h"
|
#include <pybind11/attr.h>
|
||||||
#include "../options.h"
|
#include <pybind11/options.h>
|
||||||
|
|
||||||
|
#include "exception_translation.h"
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
@ -581,7 +583,18 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
std::memset(view, 0, sizeof(Py_buffer));
|
std::memset(view, 0, sizeof(Py_buffer));
|
||||||
buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data);
|
buffer_info *info = nullptr;
|
||||||
|
try {
|
||||||
|
info = tinfo->get_buffer(obj, tinfo->get_buffer_data);
|
||||||
|
} catch (...) {
|
||||||
|
try_translate_exceptions();
|
||||||
|
raise_from(PyExc_BufferError, "Error getting buffer");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (info == nullptr) {
|
||||||
|
pybind11_fail("FATAL UNEXPECTED SITUATION: tinfo->get_buffer() returned nullptr.");
|
||||||
|
}
|
||||||
|
|
||||||
if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) {
|
if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) {
|
||||||
delete info;
|
delete info;
|
||||||
// view->obj = nullptr; // Was just memset to 0, so not necessary
|
// view->obj = nullptr; // Was just memset to 0, so not necessary
|
||||||
|
@ -479,6 +479,8 @@ PYBIND11_WARNING_POP
|
|||||||
}
|
}
|
||||||
|
|
||||||
\endrst */
|
\endrst */
|
||||||
|
PYBIND11_WARNING_PUSH
|
||||||
|
PYBIND11_WARNING_DISABLE_CLANG("-Wgnu-zero-variadic-macro-arguments")
|
||||||
#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; \
|
||||||
@ -499,6 +501,7 @@ PYBIND11_WARNING_POP
|
|||||||
PYBIND11_CATCH_INIT_EXCEPTIONS \
|
PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||||
} \
|
} \
|
||||||
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ & (variable))
|
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ & (variable))
|
||||||
|
PYBIND11_WARNING_POP
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
|
||||||
@ -554,7 +557,7 @@ enum class return_value_policy : uint8_t {
|
|||||||
object without taking ownership similar to the above
|
object without taking ownership similar to the above
|
||||||
return_value_policy::reference policy. In contrast to that policy, the
|
return_value_policy::reference policy. In contrast to that policy, the
|
||||||
function or property's implicit this argument (called the parent) is
|
function or property's implicit this argument (called the parent) is
|
||||||
considered to be the the owner of the return value (the child).
|
considered to be the owner of the return value (the child).
|
||||||
pybind11 then couples the lifetime of the parent to the child via a
|
pybind11 then couples the lifetime of the parent to the child via a
|
||||||
reference relationship that ensures that the parent cannot be garbage
|
reference relationship that ensures that the parent cannot be garbage
|
||||||
collected while Python is still using the child. More advanced
|
collected while Python is still using the child. More advanced
|
||||||
|
77
include/pybind11/detail/cpp_conduit.h
Normal file
77
include/pybind11/detail/cpp_conduit.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// Copyright (c) 2024 The pybind Community.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <pybind11/pytypes.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "internals.h"
|
||||||
|
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
// Forward declaration needed here: Refactoring opportunity.
|
||||||
|
extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *);
|
||||||
|
|
||||||
|
inline bool type_is_managed_by_our_internals(PyTypeObject *type_obj) {
|
||||||
|
#if defined(PYPY_VERSION)
|
||||||
|
auto &internals = get_internals();
|
||||||
|
return bool(internals.registered_types_py.find(type_obj)
|
||||||
|
!= internals.registered_types_py.end());
|
||||||
|
#else
|
||||||
|
return bool(type_obj->tp_new == pybind11_object_new);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_instance_method_of_type(PyTypeObject *type_obj, PyObject *attr_name) {
|
||||||
|
PyObject *descr = _PyType_Lookup(type_obj, attr_name);
|
||||||
|
return bool((descr != nullptr) && PyInstanceMethod_Check(descr));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline object try_get_cpp_conduit_method(PyObject *obj) {
|
||||||
|
if (PyType_Check(obj)) {
|
||||||
|
return object();
|
||||||
|
}
|
||||||
|
PyTypeObject *type_obj = Py_TYPE(obj);
|
||||||
|
str attr_name("_pybind11_conduit_v1_");
|
||||||
|
bool assumed_to_be_callable = false;
|
||||||
|
if (type_is_managed_by_our_internals(type_obj)) {
|
||||||
|
if (!is_instance_method_of_type(type_obj, attr_name.ptr())) {
|
||||||
|
return object();
|
||||||
|
}
|
||||||
|
assumed_to_be_callable = true;
|
||||||
|
}
|
||||||
|
PyObject *method = PyObject_GetAttr(obj, attr_name.ptr());
|
||||||
|
if (method == nullptr) {
|
||||||
|
PyErr_Clear();
|
||||||
|
return object();
|
||||||
|
}
|
||||||
|
if (!assumed_to_be_callable && PyCallable_Check(method) == 0) {
|
||||||
|
Py_DECREF(method);
|
||||||
|
return object();
|
||||||
|
}
|
||||||
|
return reinterpret_steal<object>(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void *try_raw_pointer_ephemeral_from_cpp_conduit(handle src,
|
||||||
|
const std::type_info *cpp_type_info) {
|
||||||
|
object method = try_get_cpp_conduit_method(src.ptr());
|
||||||
|
if (method) {
|
||||||
|
capsule cpp_type_info_capsule(const_cast<void *>(static_cast<const void *>(cpp_type_info)),
|
||||||
|
typeid(std::type_info).name());
|
||||||
|
object cpp_conduit = method(bytes(PYBIND11_PLATFORM_ABI_ID),
|
||||||
|
cpp_type_info_capsule,
|
||||||
|
bytes("raw_pointer_ephemeral"));
|
||||||
|
if (isinstance<capsule>(cpp_conduit)) {
|
||||||
|
return reinterpret_borrow<capsule>(cpp_conduit).get_pointer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PYBIND11_HAS_CPP_CONDUIT 1
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
71
include/pybind11/detail/exception_translation.h
Normal file
71
include/pybind11/detail/exception_translation.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
pybind11/detail/exception_translation.h: means to translate C++ exceptions to Python exceptions
|
||||||
|
|
||||||
|
Copyright (c) 2024 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 "common.h"
|
||||||
|
#include "internals.h"
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
// Apply all the extensions translators from a list
|
||||||
|
// Return true if one of the translators completed without raising an exception
|
||||||
|
// itself. Return of false indicates that if there are other translators
|
||||||
|
// available, they should be tried.
|
||||||
|
inline bool apply_exception_translators(std::forward_list<ExceptionTranslator> &translators) {
|
||||||
|
auto last_exception = std::current_exception();
|
||||||
|
|
||||||
|
for (auto &translator : translators) {
|
||||||
|
try {
|
||||||
|
translator(last_exception);
|
||||||
|
return true;
|
||||||
|
} catch (...) {
|
||||||
|
last_exception = std::current_exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void try_translate_exceptions() {
|
||||||
|
/* When an exception is caught, give each registered exception
|
||||||
|
translator a chance to translate it to a Python exception. First
|
||||||
|
all module-local translators will be tried in reverse order of
|
||||||
|
registration. If none of the module-locale translators handle
|
||||||
|
the exception (or there are no module-locale translators) then
|
||||||
|
the global translators will be tried, also in reverse order of
|
||||||
|
registration.
|
||||||
|
|
||||||
|
A translator may choose to do one of the following:
|
||||||
|
|
||||||
|
- catch the exception and call py::set_error()
|
||||||
|
to set a standard (or custom) Python exception, or
|
||||||
|
- do nothing and let the exception fall through to the next translator, or
|
||||||
|
- delegate translation to the next translator by throwing a new type of exception.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool handled = with_exception_translators(
|
||||||
|
[&](std::forward_list<ExceptionTranslator> &exception_translators,
|
||||||
|
std::forward_list<ExceptionTranslator> &local_exception_translators) {
|
||||||
|
if (detail::apply_exception_translators(local_exception_translators)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (detail::apply_exception_translators(exception_translators)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!handled) {
|
||||||
|
set_error(PyExc_SystemError, "Exception escaped from default exception translator!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
@ -12,10 +12,10 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
#if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||||
# include "../gil.h"
|
# include <pybind11/gil.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "../pytypes.h"
|
#include <pybind11/pytypes.h>
|
||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@ -39,7 +39,11 @@
|
|||||||
# if PY_VERSION_HEX >= 0x030C0000 || defined(_MSC_VER)
|
# if PY_VERSION_HEX >= 0x030C0000 || defined(_MSC_VER)
|
||||||
// Version bump for Python 3.12+, before first 3.12 beta release.
|
// Version bump for Python 3.12+, before first 3.12 beta release.
|
||||||
// Version bump for MSVC piggy-backed on PR #4779. See comments there.
|
// Version bump for MSVC piggy-backed on PR #4779. See comments there.
|
||||||
# define PYBIND11_INTERNALS_VERSION 5
|
# ifdef Py_GIL_DISABLED
|
||||||
|
# define PYBIND11_INTERNALS_VERSION 6
|
||||||
|
# else
|
||||||
|
# define PYBIND11_INTERNALS_VERSION 5
|
||||||
|
# endif
|
||||||
# else
|
# else
|
||||||
# define PYBIND11_INTERNALS_VERSION 4
|
# define PYBIND11_INTERNALS_VERSION 4
|
||||||
# endif
|
# endif
|
||||||
@ -177,6 +181,7 @@ static_assert(sizeof(instance_map_shard) % 64 == 0,
|
|||||||
struct internals {
|
struct internals {
|
||||||
#ifdef Py_GIL_DISABLED
|
#ifdef Py_GIL_DISABLED
|
||||||
pymutex mutex;
|
pymutex mutex;
|
||||||
|
pymutex exception_translator_mutex;
|
||||||
#endif
|
#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;
|
||||||
@ -321,15 +326,17 @@ struct type_info {
|
|||||||
# define PYBIND11_INTERNALS_KIND ""
|
# define PYBIND11_INTERNALS_KIND ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define PYBIND11_PLATFORM_ABI_ID \
|
||||||
|
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \
|
||||||
|
PYBIND11_BUILD_TYPE
|
||||||
|
|
||||||
#define PYBIND11_INTERNALS_ID \
|
#define PYBIND11_INTERNALS_ID \
|
||||||
"__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
|
"__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
|
||||||
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB \
|
PYBIND11_PLATFORM_ABI_ID "__"
|
||||||
PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
|
|
||||||
|
|
||||||
#define PYBIND11_MODULE_LOCAL_ID \
|
#define PYBIND11_MODULE_LOCAL_ID \
|
||||||
"__pybind11_module_local_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
|
"__pybind11_module_local_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
|
||||||
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB \
|
PYBIND11_PLATFORM_ABI_ID "__"
|
||||||
PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
|
|
||||||
|
|
||||||
/// Each module locally stores a pointer to the `internals` data. The data
|
/// Each module locally stores a pointer to the `internals` data. The data
|
||||||
/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`.
|
/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`.
|
||||||
@ -641,6 +648,19 @@ inline auto with_internals(const F &cb) -> decltype(cb(get_internals())) {
|
|||||||
return cb(internals);
|
return cb(internals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
inline auto with_exception_translators(const F &cb)
|
||||||
|
-> decltype(cb(get_internals().registered_exception_translators,
|
||||||
|
get_local_internals().registered_exception_translators)) {
|
||||||
|
auto &internals = get_internals();
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
std::unique_lock<pymutex> lock((internals).exception_translator_mutex);
|
||||||
|
#endif
|
||||||
|
auto &local_internals = get_local_internals();
|
||||||
|
return cb(internals.registered_exception_translators,
|
||||||
|
local_internals.registered_exception_translators);
|
||||||
|
}
|
||||||
|
|
||||||
inline std::uint64_t mix64(std::uint64_t z) {
|
inline std::uint64_t mix64(std::uint64_t z) {
|
||||||
// David Stafford's variant 13 of the MurmurHash3 finalizer popularized
|
// David Stafford's variant 13 of the MurmurHash3 finalizer popularized
|
||||||
// by the SplitMix PRNG.
|
// by the SplitMix PRNG.
|
||||||
|
@ -9,16 +9,20 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../pytypes.h"
|
#include <pybind11/pytypes.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "cpp_conduit.h"
|
||||||
#include "descr.h"
|
#include "descr.h"
|
||||||
#include "internals.h"
|
#include "internals.h"
|
||||||
#include "typeid.h"
|
#include "typeid.h"
|
||||||
#include "value_and_holder.h"
|
#include "value_and_holder.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <new>
|
#include <new>
|
||||||
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <typeindex>
|
#include <typeindex>
|
||||||
@ -610,6 +614,13 @@ public:
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
bool try_cpp_conduit(handle src) {
|
||||||
|
value = try_raw_pointer_ephemeral_from_cpp_conduit(src, cpptype);
|
||||||
|
if (value != nullptr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
void check_holder_compat() {}
|
void check_holder_compat() {}
|
||||||
|
|
||||||
PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) {
|
PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) {
|
||||||
@ -741,6 +752,10 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (convert && cpptype && this_.try_cpp_conduit(src)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -768,6 +783,32 @@ public:
|
|||||||
void *value = nullptr;
|
void *value = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline object cpp_conduit_method(handle self,
|
||||||
|
const bytes &pybind11_platform_abi_id,
|
||||||
|
const capsule &cpp_type_info_capsule,
|
||||||
|
const bytes &pointer_kind) {
|
||||||
|
#ifdef PYBIND11_HAS_STRING_VIEW
|
||||||
|
using cpp_str = std::string_view;
|
||||||
|
#else
|
||||||
|
using cpp_str = std::string;
|
||||||
|
#endif
|
||||||
|
if (cpp_str(pybind11_platform_abi_id) != PYBIND11_PLATFORM_ABI_ID) {
|
||||||
|
return none();
|
||||||
|
}
|
||||||
|
if (std::strcmp(cpp_type_info_capsule.name(), typeid(std::type_info).name()) != 0) {
|
||||||
|
return none();
|
||||||
|
}
|
||||||
|
if (cpp_str(pointer_kind) != "raw_pointer_ephemeral") {
|
||||||
|
throw std::runtime_error("Invalid pointer_kind: \"" + std::string(pointer_kind) + "\"");
|
||||||
|
}
|
||||||
|
const auto *cpp_type_info = cpp_type_info_capsule.get_pointer<const std::type_info>();
|
||||||
|
type_caster_generic caster(*cpp_type_info);
|
||||||
|
if (!caster.load(self, false)) {
|
||||||
|
return none();
|
||||||
|
}
|
||||||
|
return capsule(caster.value, cpp_type_info->name());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster
|
* Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster
|
||||||
* needs to provide `operator T*()` and `operator T&()` operators.
|
* needs to provide `operator T*()` and `operator T&()` operators.
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../numpy.h"
|
#include <pybind11/numpy.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
/* HINT: To suppress warnings originating from the Eigen headers, use -isystem.
|
/* HINT: To suppress warnings originating from the Eigen headers, use -isystem.
|
||||||
|
@ -7,7 +7,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../numpy.h"
|
#include <pybind11/numpy.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
|
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
|
||||||
|
@ -9,12 +9,55 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#define PYBIND11_HAS_TYPE_CASTER_STD_FUNCTION_SPECIALIZATIONS
|
||||||
|
|
||||||
#include "pybind11.h"
|
#include "pybind11.h"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(type_caster_std_function_specializations)
|
||||||
|
|
||||||
|
// ensure GIL is held during functor destruction
|
||||||
|
struct func_handle {
|
||||||
|
function 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;
|
||||||
|
function kill_f(std::move(f));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// to emulate 'move initialization capture' in C++11
|
||||||
|
struct func_wrapper_base {
|
||||||
|
func_handle hfunc;
|
||||||
|
explicit func_wrapper_base(func_handle &&hf) noexcept : hfunc(hf) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Return, typename... Args>
|
||||||
|
struct func_wrapper : func_wrapper_base {
|
||||||
|
using func_wrapper_base::func_wrapper_base;
|
||||||
|
Return operator()(Args... args) const {
|
||||||
|
gil_scoped_acquire acq;
|
||||||
|
// casts the returned object as a rvalue to the return type
|
||||||
|
return hfunc.f(std::forward<Args>(args)...).template cast<Return>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(type_caster_std_function_specializations)
|
||||||
|
|
||||||
template <typename Return, typename... Args>
|
template <typename Return, typename... Args>
|
||||||
struct type_caster<std::function<Return(Args...)>> {
|
struct type_caster<std::function<Return(Args...)>> {
|
||||||
@ -77,40 +120,8 @@ public:
|
|||||||
// See PR #1413 for full details
|
// See PR #1413 for full details
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure GIL is held during functor destruction
|
value = type_caster_std_function_specializations::func_wrapper<Return, Args...>(
|
||||||
struct func_handle {
|
type_caster_std_function_specializations::func_handle(std::move(func)));
|
||||||
function 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;
|
|
||||||
function kill_f(std::move(f));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// to emulate 'move initialization capture' in C++11
|
|
||||||
struct func_wrapper {
|
|
||||||
func_handle hfunc;
|
|
||||||
explicit func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {}
|
|
||||||
Return operator()(Args... args) const {
|
|
||||||
gil_scoped_acquire acq;
|
|
||||||
// casts the returned object as a rvalue to the return type
|
|
||||||
return hfunc.f(std::forward<Args>(args)...).template cast<Return>();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
value = func_wrapper(func_handle(std::move(func)));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "detail/class.h"
|
#include "detail/class.h"
|
||||||
|
#include "detail/exception_translation.h"
|
||||||
#include "detail/init.h"
|
#include "detail/init.h"
|
||||||
#include "attr.h"
|
#include "attr.h"
|
||||||
#include "gil.h"
|
#include "gil.h"
|
||||||
@ -23,6 +23,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -95,24 +96,6 @@ inline std::string replace_newlines_and_squash(const char *text) {
|
|||||||
return result.substr(str_begin, str_range);
|
return result.substr(str_begin, str_range);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply all the extensions translators from a list
|
|
||||||
// Return true if one of the translators completed without raising an exception
|
|
||||||
// itself. Return of false indicates that if there are other translators
|
|
||||||
// available, they should be tried.
|
|
||||||
inline bool apply_exception_translators(std::forward_list<ExceptionTranslator> &translators) {
|
|
||||||
auto last_exception = std::current_exception();
|
|
||||||
|
|
||||||
for (auto &translator : translators) {
|
|
||||||
try {
|
|
||||||
translator(last_exception);
|
|
||||||
return true;
|
|
||||||
} catch (...) {
|
|
||||||
last_exception = std::current_exception();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
# define PYBIND11_COMPAT_STRDUP _strdup
|
# define PYBIND11_COMPAT_STRDUP _strdup
|
||||||
#else
|
#else
|
||||||
@ -610,7 +593,8 @@ protected:
|
|||||||
int index = 0;
|
int index = 0;
|
||||||
/* Create a nice pydoc rec including all signatures and
|
/* Create a nice pydoc rec including all signatures and
|
||||||
docstrings of the functions in the overload chain */
|
docstrings of the functions in the overload chain */
|
||||||
if (chain && options::show_function_signatures()) {
|
if (chain && options::show_function_signatures()
|
||||||
|
&& std::strcmp(rec->name, "_pybind11_conduit_v1_") != 0) {
|
||||||
// First a generic signature
|
// First a generic signature
|
||||||
signatures += rec->name;
|
signatures += rec->name;
|
||||||
signatures += "(*args, **kwargs)\n";
|
signatures += "(*args, **kwargs)\n";
|
||||||
@ -619,7 +603,8 @@ protected:
|
|||||||
// Then specific overload signatures
|
// Then specific overload signatures
|
||||||
bool first_user_def = true;
|
bool first_user_def = true;
|
||||||
for (auto *it = chain_start; it != nullptr; it = it->next) {
|
for (auto *it = chain_start; it != nullptr; it = it->next) {
|
||||||
if (options::show_function_signatures()) {
|
if (options::show_function_signatures()
|
||||||
|
&& std::strcmp(rec->name, "_pybind11_conduit_v1_") != 0) {
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
signatures += '\n';
|
signatures += '\n';
|
||||||
}
|
}
|
||||||
@ -1038,40 +1023,7 @@ protected:
|
|||||||
throw;
|
throw;
|
||||||
#endif
|
#endif
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
/* When an exception is caught, give each registered exception
|
try_translate_exceptions();
|
||||||
translator a chance to translate it to a Python exception. First
|
|
||||||
all module-local translators will be tried in reverse order of
|
|
||||||
registration. If none of the module-locale translators handle
|
|
||||||
the exception (or there are no module-locale translators) then
|
|
||||||
the global translators will be tried, also in reverse order of
|
|
||||||
registration.
|
|
||||||
|
|
||||||
A translator may choose to do one of the following:
|
|
||||||
|
|
||||||
- catch the exception and call py::set_error()
|
|
||||||
to set a standard (or custom) Python exception, or
|
|
||||||
- do nothing and let the exception fall through to the next translator, or
|
|
||||||
- delegate translation to the next translator by throwing a new type of exception.
|
|
||||||
*/
|
|
||||||
|
|
||||||
bool handled = with_internals([&](internals &internals) {
|
|
||||||
auto &local_exception_translators
|
|
||||||
= get_local_internals().registered_exception_translators;
|
|
||||||
if (detail::apply_exception_translators(local_exception_translators)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
auto &exception_translators = internals.registered_exception_translators;
|
|
||||||
if (detail::apply_exception_translators(exception_translators)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (handled) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
set_error(PyExc_SystemError, "Exception escaped from default exception translator!");
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1431,7 +1383,17 @@ protected:
|
|||||||
} else {
|
} else {
|
||||||
internals.registered_types_cpp[tindex] = tinfo;
|
internals.registered_types_cpp[tindex] = tinfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PYBIND11_WARNING_PUSH
|
||||||
|
#if defined(__GNUC__) && __GNUC__ == 12
|
||||||
|
// When using GCC 12 these warnings are disabled as they trigger
|
||||||
|
// false positive warnings. Discussed here:
|
||||||
|
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115824.
|
||||||
|
PYBIND11_WARNING_DISABLE_GCC("-Warray-bounds")
|
||||||
|
PYBIND11_WARNING_DISABLE_GCC("-Wstringop-overread")
|
||||||
|
#endif
|
||||||
internals.registered_types_py[(PyTypeObject *) m_ptr] = {tinfo};
|
internals.registered_types_py[(PyTypeObject *) m_ptr] = {tinfo};
|
||||||
|
PYBIND11_WARNING_POP
|
||||||
});
|
});
|
||||||
|
|
||||||
if (rec.bases.size() > 1 || rec.multiple_inheritance) {
|
if (rec.bases.size() > 1 || rec.multiple_inheritance) {
|
||||||
@ -1652,6 +1614,7 @@ public:
|
|||||||
= instances[std::type_index(typeid(type))];
|
= instances[std::type_index(typeid(type))];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
def("_pybind11_conduit_v1_", cpp_conduit_method);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Base, detail::enable_if_t<is_base<Base>::value, int> = 0>
|
template <typename Base, detail::enable_if_t<is_base<Base>::value, int> = 0>
|
||||||
@ -2624,10 +2587,12 @@ void implicitly_convertible() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void register_exception_translator(ExceptionTranslator &&translator) {
|
inline void register_exception_translator(ExceptionTranslator &&translator) {
|
||||||
detail::with_internals([&](detail::internals &internals) {
|
detail::with_exception_translators(
|
||||||
internals.registered_exception_translators.push_front(
|
[&](std::forward_list<ExceptionTranslator> &exception_translators,
|
||||||
std::forward<ExceptionTranslator>(translator));
|
std::forward_list<ExceptionTranslator> &local_exception_translators) {
|
||||||
});
|
(void) local_exception_translators;
|
||||||
|
exception_translators.push_front(std::forward<ExceptionTranslator>(translator));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2637,11 +2602,12 @@ 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) {
|
detail::with_exception_translators(
|
||||||
(void) internals;
|
[&](std::forward_list<ExceptionTranslator> &exception_translators,
|
||||||
detail::get_local_internals().registered_exception_translators.push_front(
|
std::forward_list<ExceptionTranslator> &local_exception_translators) {
|
||||||
std::forward<ExceptionTranslator>(translator));
|
(void) exception_translators;
|
||||||
});
|
local_exception_translators.push_front(std::forward<ExceptionTranslator>(translator));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2998,6 +2964,51 @@ function get_override(const T *this_ptr, const char *name) {
|
|||||||
PYBIND11_OVERRIDE_PURE_NAME( \
|
PYBIND11_OVERRIDE_PURE_NAME( \
|
||||||
PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__)
|
PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__)
|
||||||
|
|
||||||
|
/** \rst
|
||||||
|
Macro to wrap :c:macro:`PYBIND11_OVERRIDE_PURE_NAME` and :c:macro:`PYBIND11_OVERRIDE_PURE`
|
||||||
|
depending on the base class and cname parameter provided.
|
||||||
|
See :ref:`_multiple_inheritance` for more information.
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
template<class AnimalBase = Animal>
|
||||||
|
class PyAnimal : public AnimalBase {
|
||||||
|
public:
|
||||||
|
// Inherit the constructors
|
||||||
|
using AnimalBase::AnimalBase;
|
||||||
|
|
||||||
|
// Trampoline (need one for each virtual function)
|
||||||
|
std::string go(int n_times) override {
|
||||||
|
PYBIND11_OVERRIDE_TEMPLATE(
|
||||||
|
Animal, // The base class containing the purely virtual implementation
|
||||||
|
std::string, // Return type (ret_type)
|
||||||
|
Dog, // Parent class (cname)
|
||||||
|
"_go", // Name of method in Python (name)
|
||||||
|
go, // Name of function in C++ (must match Python name) (fn)
|
||||||
|
n_times // Argument(s) (...)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
\endrst */
|
||||||
|
#define PYBIND11_OVERRIDE_TEMPLATE_NAME(base, ret_type, cname, name, fn, ...) \
|
||||||
|
if (std::is_same<base, cname>::value) { \
|
||||||
|
PYBIND11_OVERRIDE_PURE_NAME( \
|
||||||
|
PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, fn, __VA_ARGS__); \
|
||||||
|
} else { \
|
||||||
|
PYBIND11_OVERRIDE_NAME( \
|
||||||
|
PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, fn, __VA_ARGS__); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \rst
|
||||||
|
Macro to wrap :c:macro:`PYBIND11_OVERRIDE_NAME` and :c:macro:`PYBIND11_OVERRIDE`
|
||||||
|
depending on the base class and cname parameter provided.
|
||||||
|
Uses :c:macro:`PYBIND11_OVERRIDE_TEMPLATE_NAME` under the hood.
|
||||||
|
See :ref:`_multiple_inheritance` for more information.
|
||||||
|
\endrst */
|
||||||
|
#define PYBIND11_OVERRIDE_TEMPLATE(base, ret_type, cname, fn, ...) \
|
||||||
|
PYBIND11_OVERRIDE_TEMPLATE_NAME( \
|
||||||
|
PYBIND11_TYPE(base), PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__)
|
||||||
|
|
||||||
// Deprecated versions
|
// Deprecated versions
|
||||||
|
|
||||||
PYBIND11_DEPRECATED("get_type_overload has been deprecated")
|
PYBIND11_DEPRECATED("get_type_overload has been deprecated")
|
||||||
|
@ -1259,6 +1259,7 @@ protected:
|
|||||||
using pointer = arrow_proxy<const handle>;
|
using pointer = arrow_proxy<const handle>;
|
||||||
|
|
||||||
sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) {}
|
sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) {}
|
||||||
|
sequence_fast_readonly() = default;
|
||||||
|
|
||||||
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
|
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
|
||||||
reference dereference() const { return *ptr; }
|
reference dereference() const { return *ptr; }
|
||||||
@ -1281,6 +1282,7 @@ protected:
|
|||||||
using pointer = arrow_proxy<const sequence_accessor>;
|
using pointer = arrow_proxy<const sequence_accessor>;
|
||||||
|
|
||||||
sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) {}
|
sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) {}
|
||||||
|
sequence_slow_readwrite() = default;
|
||||||
|
|
||||||
reference dereference() const { return {obj, static_cast<size_t>(index)}; }
|
reference dereference() const { return {obj, static_cast<size_t>(index)}; }
|
||||||
void increment() { ++index; }
|
void increment() { ++index; }
|
||||||
|
@ -11,10 +11,14 @@
|
|||||||
|
|
||||||
#include "pybind11.h"
|
#include "pybind11.h"
|
||||||
#include "detail/common.h"
|
#include "detail/common.h"
|
||||||
|
#include "detail/descr.h"
|
||||||
|
#include "detail/type_caster_base.h"
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <initializer_list>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@ -35,6 +39,89 @@
|
|||||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Begin: Equivalent of
|
||||||
|
// https://github.com/google/clif/blob/ae4eee1de07cdf115c0c9bf9fec9ff28efce6f6c/clif/python/runtime.cc#L388-L438
|
||||||
|
/*
|
||||||
|
The three `PyObjectTypeIsConvertibleTo*()` functions below are
|
||||||
|
the result of converging the behaviors of pybind11 and PyCLIF
|
||||||
|
(http://github.com/google/clif).
|
||||||
|
|
||||||
|
Originally PyCLIF was extremely far on the permissive side of the spectrum,
|
||||||
|
while pybind11 was very far on the strict side. Originally PyCLIF accepted any
|
||||||
|
Python iterable as input for a C++ `vector`/`set`/`map` argument, as long as
|
||||||
|
the elements were convertible. The obvious (in hindsight) problem was that
|
||||||
|
any empty Python iterable could be passed to any of these C++ types, e.g. `{}`
|
||||||
|
was accepted for C++ `vector`/`set` arguments, or `[]` for C++ `map` arguments.
|
||||||
|
|
||||||
|
The functions below strike a practical permissive-vs-strict compromise,
|
||||||
|
informed by tens of thousands of use cases in the wild. A main objective is
|
||||||
|
to prevent accidents and improve readability:
|
||||||
|
|
||||||
|
- Python literals must match the C++ types.
|
||||||
|
|
||||||
|
- For C++ `set`: The potentially reducing conversion from a Python sequence
|
||||||
|
(e.g. Python `list` or `tuple`) to a C++ `set` must be explicit, by going
|
||||||
|
through a Python `set`.
|
||||||
|
|
||||||
|
- However, a Python `set` can still be passed to a C++ `vector`. The rationale
|
||||||
|
is that this conversion is not reducing. Implicit conversions of this kind
|
||||||
|
are also fairly commonly used, therefore enforcing explicit conversions
|
||||||
|
would have an unfavorable cost : benefit ratio; more sloppily speaking,
|
||||||
|
such an enforcement would be more annoying than helpful.
|
||||||
|
*/
|
||||||
|
|
||||||
|
inline bool PyObjectIsInstanceWithOneOfTpNames(PyObject *obj,
|
||||||
|
std::initializer_list<const char *> tp_names) {
|
||||||
|
if (PyType_Check(obj)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const char *obj_tp_name = Py_TYPE(obj)->tp_name;
|
||||||
|
for (const auto *tp_name : tp_names) {
|
||||||
|
if (std::strcmp(obj_tp_name, tp_name) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool PyObjectTypeIsConvertibleToStdVector(PyObject *obj) {
|
||||||
|
if (PySequence_Check(obj) != 0) {
|
||||||
|
return !PyUnicode_Check(obj) && !PyBytes_Check(obj);
|
||||||
|
}
|
||||||
|
return (PyGen_Check(obj) != 0) || (PyAnySet_Check(obj) != 0)
|
||||||
|
|| PyObjectIsInstanceWithOneOfTpNames(
|
||||||
|
obj, {"dict_keys", "dict_values", "dict_items", "map", "zip"});
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool PyObjectTypeIsConvertibleToStdSet(PyObject *obj) {
|
||||||
|
return (PyAnySet_Check(obj) != 0) || PyObjectIsInstanceWithOneOfTpNames(obj, {"dict_keys"});
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool PyObjectTypeIsConvertibleToStdMap(PyObject *obj) {
|
||||||
|
if (PyDict_Check(obj)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Implicit requirement in the conditions below:
|
||||||
|
// A type with `.__getitem__()` & `.items()` methods must implement these
|
||||||
|
// to be compatible with https://docs.python.org/3/c-api/mapping.html
|
||||||
|
if (PyMapping_Check(obj) == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PyObject *items = PyObject_GetAttrString(obj, "items");
|
||||||
|
if (items == nullptr) {
|
||||||
|
PyErr_Clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool is_convertible = (PyCallable_Check(items) != 0);
|
||||||
|
Py_DECREF(items);
|
||||||
|
return is_convertible;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// End: Equivalent of clif/python/runtime.cc
|
||||||
|
//
|
||||||
|
|
||||||
/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for
|
/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for
|
||||||
/// forwarding a container element). Typically used indirect via forwarded_type(), below.
|
/// forwarding a container element). Typically used indirect via forwarded_type(), below.
|
||||||
template <typename T, typename U>
|
template <typename T, typename U>
|
||||||
@ -66,17 +153,10 @@ private:
|
|||||||
}
|
}
|
||||||
void reserve_maybe(const anyset &, void *) {}
|
void reserve_maybe(const anyset &, void *) {}
|
||||||
|
|
||||||
public:
|
bool convert_iterable(const iterable &itbl, bool convert) {
|
||||||
bool load(handle src, bool convert) {
|
for (const auto &it : itbl) {
|
||||||
if (!isinstance<anyset>(src)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto s = reinterpret_borrow<anyset>(src);
|
|
||||||
value.clear();
|
|
||||||
reserve_maybe(s, &value);
|
|
||||||
for (auto entry : s) {
|
|
||||||
key_conv conv;
|
key_conv conv;
|
||||||
if (!conv.load(entry, convert)) {
|
if (!conv.load(it, convert)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
value.insert(cast_op<Key &&>(std::move(conv)));
|
value.insert(cast_op<Key &&>(std::move(conv)));
|
||||||
@ -84,6 +164,29 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool convert_anyset(anyset s, bool convert) {
|
||||||
|
value.clear();
|
||||||
|
reserve_maybe(s, &value);
|
||||||
|
return convert_iterable(s, convert);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool load(handle src, bool convert) {
|
||||||
|
if (!PyObjectTypeIsConvertibleToStdSet(src.ptr())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isinstance<anyset>(src)) {
|
||||||
|
value.clear();
|
||||||
|
return convert_anyset(reinterpret_borrow<anyset>(src), convert);
|
||||||
|
}
|
||||||
|
if (!convert) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
assert(isinstance<iterable>(src));
|
||||||
|
value.clear();
|
||||||
|
return convert_iterable(reinterpret_borrow<iterable>(src), convert);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||||
if (!std::is_lvalue_reference<T>::value) {
|
if (!std::is_lvalue_reference<T>::value) {
|
||||||
@ -115,15 +218,10 @@ private:
|
|||||||
}
|
}
|
||||||
void reserve_maybe(const dict &, void *) {}
|
void reserve_maybe(const dict &, void *) {}
|
||||||
|
|
||||||
public:
|
bool convert_elements(const dict &d, bool convert) {
|
||||||
bool load(handle src, bool convert) {
|
|
||||||
if (!isinstance<dict>(src)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto d = reinterpret_borrow<dict>(src);
|
|
||||||
value.clear();
|
value.clear();
|
||||||
reserve_maybe(d, &value);
|
reserve_maybe(d, &value);
|
||||||
for (auto it : d) {
|
for (const auto &it : d) {
|
||||||
key_conv kconv;
|
key_conv kconv;
|
||||||
value_conv vconv;
|
value_conv vconv;
|
||||||
if (!kconv.load(it.first.ptr(), convert) || !vconv.load(it.second.ptr(), convert)) {
|
if (!kconv.load(it.first.ptr(), convert) || !vconv.load(it.second.ptr(), convert)) {
|
||||||
@ -134,6 +232,25 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool load(handle src, bool convert) {
|
||||||
|
if (!PyObjectTypeIsConvertibleToStdMap(src.ptr())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isinstance<dict>(src)) {
|
||||||
|
return convert_elements(reinterpret_borrow<dict>(src), convert);
|
||||||
|
}
|
||||||
|
if (!convert) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto items = reinterpret_steal<object>(PyMapping_Items(src.ptr()));
|
||||||
|
if (!items) {
|
||||||
|
throw error_already_set();
|
||||||
|
}
|
||||||
|
assert(isinstance<iterable>(items));
|
||||||
|
return convert_elements(dict(reinterpret_borrow<iterable>(items)), convert);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||||
dict d;
|
dict d;
|
||||||
@ -166,20 +283,21 @@ struct list_caster {
|
|||||||
using value_conv = make_caster<Value>;
|
using value_conv = make_caster<Value>;
|
||||||
|
|
||||||
bool load(handle src, bool convert) {
|
bool load(handle src, bool convert) {
|
||||||
if (!isinstance<sequence>(src) || isinstance<bytes>(src) || isinstance<str>(src)) {
|
if (!PyObjectTypeIsConvertibleToStdVector(src.ptr())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto s = reinterpret_borrow<sequence>(src);
|
if (isinstance<sequence>(src)) {
|
||||||
value.clear();
|
return convert_elements(src, convert);
|
||||||
reserve_maybe(s, &value);
|
|
||||||
for (const auto &it : s) {
|
|
||||||
value_conv conv;
|
|
||||||
if (!conv.load(it, convert)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
value.push_back(cast_op<Value &&>(std::move(conv)));
|
|
||||||
}
|
}
|
||||||
return true;
|
if (!convert) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Designed to be behavior-equivalent to passing tuple(src) from Python:
|
||||||
|
// The conversion to a tuple will first exhaust the generator object, to ensure that
|
||||||
|
// the generator is not left in an unpredictable (to the caller) partially-consumed
|
||||||
|
// state.
|
||||||
|
assert(isinstance<iterable>(src));
|
||||||
|
return convert_elements(tuple(reinterpret_borrow<iterable>(src)), convert);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -189,6 +307,20 @@ private:
|
|||||||
}
|
}
|
||||||
void reserve_maybe(const sequence &, void *) {}
|
void reserve_maybe(const sequence &, void *) {}
|
||||||
|
|
||||||
|
bool convert_elements(handle seq, bool convert) {
|
||||||
|
auto s = reinterpret_borrow<sequence>(seq);
|
||||||
|
value.clear();
|
||||||
|
reserve_maybe(s, &value);
|
||||||
|
for (const auto &it : seq) {
|
||||||
|
value_conv conv;
|
||||||
|
if (!conv.load(it, convert)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value.push_back(cast_op<Value &&>(std::move(conv)));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||||
@ -220,43 +352,87 @@ struct type_caster<std::deque<Type, Alloc>> : list_caster<std::deque<Type, Alloc
|
|||||||
template <typename Type, typename Alloc>
|
template <typename Type, typename Alloc>
|
||||||
struct type_caster<std::list<Type, Alloc>> : list_caster<std::list<Type, Alloc>, Type> {};
|
struct type_caster<std::list<Type, Alloc>> : list_caster<std::list<Type, Alloc>, Type> {};
|
||||||
|
|
||||||
|
template <typename ArrayType, typename V, size_t... I>
|
||||||
|
ArrayType vector_to_array_impl(V &&v, index_sequence<I...>) {
|
||||||
|
return {{std::move(v[I])...}};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on https://en.cppreference.com/w/cpp/container/array/to_array
|
||||||
|
template <typename ArrayType, size_t N, typename V>
|
||||||
|
ArrayType vector_to_array(V &&v) {
|
||||||
|
return vector_to_array_impl<ArrayType, V>(std::forward<V>(v), make_index_sequence<N>{});
|
||||||
|
}
|
||||||
|
|
||||||
template <typename ArrayType, typename Value, bool Resizable, size_t Size = 0>
|
template <typename ArrayType, typename Value, bool Resizable, size_t Size = 0>
|
||||||
struct array_caster {
|
struct array_caster {
|
||||||
using value_conv = make_caster<Value>;
|
using value_conv = make_caster<Value>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <bool R = Resizable>
|
std::unique_ptr<ArrayType> value;
|
||||||
bool require_size(enable_if_t<R, size_t> size) {
|
|
||||||
if (value.size() != size) {
|
|
||||||
value.resize(size);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
template <bool R = Resizable>
|
|
||||||
bool require_size(enable_if_t<!R, size_t> size) {
|
|
||||||
return size == Size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
template <bool R = Resizable, enable_if_t<R, int> = 0>
|
||||||
bool load(handle src, bool convert) {
|
bool convert_elements(handle seq, bool convert) {
|
||||||
if (!isinstance<sequence>(src)) {
|
auto l = reinterpret_borrow<sequence>(seq);
|
||||||
return false;
|
value.reset(new ArrayType{});
|
||||||
}
|
// Using `resize` to preserve the behavior exactly as it was before PR #5305
|
||||||
auto l = reinterpret_borrow<sequence>(src);
|
// For the `resize` to work, `Value` must be default constructible.
|
||||||
if (!require_size(l.size())) {
|
// For `std::valarray`, this is a requirement:
|
||||||
return false;
|
// https://en.cppreference.com/w/cpp/named_req/NumericType
|
||||||
}
|
value->resize(l.size());
|
||||||
size_t ctr = 0;
|
size_t ctr = 0;
|
||||||
for (const auto &it : l) {
|
for (const auto &it : l) {
|
||||||
value_conv conv;
|
value_conv conv;
|
||||||
if (!conv.load(it, convert)) {
|
if (!conv.load(it, convert)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
value[ctr++] = cast_op<Value &&>(std::move(conv));
|
(*value)[ctr++] = cast_op<Value &&>(std::move(conv));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <bool R = Resizable, enable_if_t<!R, int> = 0>
|
||||||
|
bool convert_elements(handle seq, bool convert) {
|
||||||
|
auto l = reinterpret_borrow<sequence>(seq);
|
||||||
|
if (l.size() != Size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// The `temp` storage is needed to support `Value` types that are not
|
||||||
|
// default-constructible.
|
||||||
|
// Deliberate choice: no template specializations, for simplicity, and
|
||||||
|
// because the compile time overhead for the specializations is deemed
|
||||||
|
// more significant than the runtime overhead for the `temp` storage.
|
||||||
|
std::vector<Value> temp;
|
||||||
|
temp.reserve(l.size());
|
||||||
|
for (auto it : l) {
|
||||||
|
value_conv conv;
|
||||||
|
if (!conv.load(it, convert)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
temp.emplace_back(cast_op<Value &&>(std::move(conv)));
|
||||||
|
}
|
||||||
|
value.reset(new ArrayType(vector_to_array<ArrayType, Size>(std::move(temp))));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool load(handle src, bool convert) {
|
||||||
|
if (!PyObjectTypeIsConvertibleToStdVector(src.ptr())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isinstance<sequence>(src)) {
|
||||||
|
return convert_elements(src, convert);
|
||||||
|
}
|
||||||
|
if (!convert) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Designed to be behavior-equivalent to passing tuple(src) from Python:
|
||||||
|
// The conversion to a tuple will first exhaust the generator object, to ensure that
|
||||||
|
// the generator is not left in an unpredictable (to the caller) partially-consumed
|
||||||
|
// state.
|
||||||
|
assert(isinstance<iterable>(src));
|
||||||
|
return convert_elements(tuple(reinterpret_borrow<iterable>(src)), convert);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||||
list l(src.size());
|
list l(src.size());
|
||||||
@ -272,12 +448,36 @@ public:
|
|||||||
return l.release();
|
return l.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
PYBIND11_TYPE_CASTER(ArrayType,
|
// Code copied from PYBIND11_TYPE_CASTER macro.
|
||||||
const_name<Resizable>(const_name(""), const_name("Annotated["))
|
// Intentionally preserving the behavior exactly as it was before PR #5305
|
||||||
+ const_name("list[") + value_conv::name + const_name("]")
|
template <typename T_, enable_if_t<std::is_same<ArrayType, remove_cv_t<T_>>::value, int> = 0>
|
||||||
+ const_name<Resizable>(const_name(""),
|
static handle cast(T_ *src, return_value_policy policy, handle parent) {
|
||||||
const_name(", FixedSize(")
|
if (!src) {
|
||||||
+ const_name<Size>() + const_name(")]")));
|
return none().release();
|
||||||
|
}
|
||||||
|
if (policy == return_value_policy::take_ownership) {
|
||||||
|
auto h = cast(std::move(*src), policy, parent);
|
||||||
|
delete src; // WARNING: Assumes `src` was allocated with `new`.
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
return cast(*src, policy, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||||
|
operator ArrayType *() { return &(*value); }
|
||||||
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||||
|
operator ArrayType &() { return *value; }
|
||||||
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||||
|
operator ArrayType &&() && { return std::move(*value); }
|
||||||
|
|
||||||
|
template <typename T_>
|
||||||
|
using cast_op_type = movable_cast_op_type<T_>;
|
||||||
|
|
||||||
|
static constexpr auto name
|
||||||
|
= const_name<Resizable>(const_name(""), const_name("Annotated[")) + const_name("list[")
|
||||||
|
+ value_conv::name + const_name("]")
|
||||||
|
+ const_name<Resizable>(
|
||||||
|
const_name(""), const_name(", FixedSize(") + const_name<Size>() + const_name(")]"));
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Type, size_t Size>
|
template <typename Type, size_t Size>
|
||||||
|
@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../pybind11.h"
|
#include <pybind11/cast.h>
|
||||||
#include "../detail/common.h"
|
#include <pybind11/detail/common.h>
|
||||||
#include "../detail/descr.h"
|
#include <pybind11/detail/descr.h>
|
||||||
#include "../cast.h"
|
#include <pybind11/pybind11.h>
|
||||||
#include "../pytypes.h"
|
#include <pybind11/pytypes.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -100,9 +100,7 @@ class Never : public none {
|
|||||||
using none::none;
|
using none::none;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(__cpp_nontype_template_parameter_class) \
|
#if defined(__cpp_nontype_template_args) && __cpp_nontype_template_args >= 201911L
|
||||||
&& (/* See #5201 */ !defined(__GNUC__) \
|
|
||||||
|| (__GNUC__ > 10 || (__GNUC__ == 10 && __GNUC_MINOR__ >= 3)))
|
|
||||||
# define PYBIND11_TYPING_H_HAS_STRING_LITERAL
|
# define PYBIND11_TYPING_H_HAS_STRING_LITERAL
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
struct StringLiteral {
|
struct StringLiteral {
|
||||||
|
75
include/pybind11/warnings.h
Normal file
75
include/pybind11/warnings.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
pybind11/warnings.h: Python warnings wrappers.
|
||||||
|
|
||||||
|
Copyright (c) 2024 Jan Iwaszkiewicz <jiwaszkiewicz6@gmail.com>
|
||||||
|
|
||||||
|
All rights reserved. Use of this source code is governed by a
|
||||||
|
BSD-style license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pybind11.h"
|
||||||
|
#include "detail/common.h"
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
inline bool PyWarning_Check(PyObject *obj) {
|
||||||
|
int result = PyObject_IsSubclass(obj, PyExc_Warning);
|
||||||
|
if (result == 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (result == -1) {
|
||||||
|
raise_from(PyExc_SystemError,
|
||||||
|
"pybind11::detail::PyWarning_Check(): PyObject_IsSubclass() call failed.");
|
||||||
|
throw error_already_set();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(warnings)
|
||||||
|
|
||||||
|
inline object
|
||||||
|
new_warning_type(handle scope, const char *name, handle base = PyExc_RuntimeWarning) {
|
||||||
|
if (!detail::PyWarning_Check(base.ptr())) {
|
||||||
|
pybind11_fail("pybind11::warnings::new_warning_type(): cannot create custom warning, base "
|
||||||
|
"must be a subclass of "
|
||||||
|
"PyExc_Warning!");
|
||||||
|
}
|
||||||
|
if (hasattr(scope, name)) {
|
||||||
|
pybind11_fail("pybind11::warnings::new_warning_type(): an attribute with name \""
|
||||||
|
+ std::string(name) + "\" exists already.");
|
||||||
|
}
|
||||||
|
std::string full_name = scope.attr("__name__").cast<std::string>() + std::string(".") + name;
|
||||||
|
handle h(PyErr_NewException(full_name.c_str(), base.ptr(), nullptr));
|
||||||
|
if (!h) {
|
||||||
|
raise_from(PyExc_SystemError,
|
||||||
|
"pybind11::warnings::new_warning_type(): PyErr_NewException() call failed.");
|
||||||
|
throw error_already_set();
|
||||||
|
}
|
||||||
|
auto obj = reinterpret_steal<object>(h);
|
||||||
|
scope.attr(name) = obj;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Similar to Python `warnings.warn()`
|
||||||
|
inline void
|
||||||
|
warn(const char *message, handle category = PyExc_RuntimeWarning, int stack_level = 2) {
|
||||||
|
if (!detail::PyWarning_Check(category.ptr())) {
|
||||||
|
pybind11_fail(
|
||||||
|
"pybind11::warnings::warn(): cannot raise warning, category must be a subclass of "
|
||||||
|
"PyExc_Warning!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyErr_WarnEx(category.ptr(), message, stack_level) == -1) {
|
||||||
|
throw error_already_set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(warnings)
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
@ -2,12 +2,35 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import sysconfig
|
import sysconfig
|
||||||
|
|
||||||
from ._version import __version__
|
from ._version import __version__
|
||||||
from .commands import get_cmake_dir, get_include, get_pkgconfig_dir
|
from .commands import get_cmake_dir, get_include, get_pkgconfig_dir
|
||||||
|
|
||||||
|
# This is the conditional used for os.path being posixpath
|
||||||
|
if "posix" in sys.builtin_module_names:
|
||||||
|
from shlex import quote
|
||||||
|
elif "nt" in sys.builtin_module_names:
|
||||||
|
# See https://github.com/mesonbuild/meson/blob/db22551ed9d2dd7889abea01cc1c7bba02bf1c75/mesonbuild/utils/universal.py#L1092-L1121
|
||||||
|
# and the original documents:
|
||||||
|
# https://docs.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments and
|
||||||
|
# https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
|
||||||
|
UNSAFE = re.compile("[ \t\n\r]")
|
||||||
|
|
||||||
|
def quote(s: str) -> str:
|
||||||
|
if s and not UNSAFE.search(s):
|
||||||
|
return s
|
||||||
|
|
||||||
|
# Paths cannot contain a '"' on Windows, so we don't need to worry
|
||||||
|
# about nuanced counting here.
|
||||||
|
return f'"{s}\\"' if s.endswith("\\") else f'"{s}"'
|
||||||
|
else:
|
||||||
|
|
||||||
|
def quote(s: str) -> str:
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
def print_includes() -> None:
|
def print_includes() -> None:
|
||||||
dirs = [
|
dirs = [
|
||||||
@ -22,7 +45,7 @@ def print_includes() -> None:
|
|||||||
if d and d not in unique_dirs:
|
if d and d not in unique_dirs:
|
||||||
unique_dirs.append(d)
|
unique_dirs.append(d)
|
||||||
|
|
||||||
print(" ".join("-I" + d for d in unique_dirs))
|
print(" ".join(quote(f"-I{d}") for d in unique_dirs))
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
@ -54,9 +77,9 @@ def main() -> None:
|
|||||||
if args.includes:
|
if args.includes:
|
||||||
print_includes()
|
print_includes()
|
||||||
if args.cmakedir:
|
if args.cmakedir:
|
||||||
print(get_cmake_dir())
|
print(quote(get_cmake_dir()))
|
||||||
if args.pkgconfigdir:
|
if args.pkgconfigdir:
|
||||||
print(get_pkgconfig_dir())
|
print(quote(get_pkgconfig_dir()))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -5,16 +5,7 @@
|
|||||||
# All rights reserved. Use of this source code is governed by a
|
# All rights reserved. Use of this source code is governed by a
|
||||||
# BSD-style license that can be found in the LICENSE file.
|
# BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.15...3.30)
|
||||||
|
|
||||||
# The `cmake_minimum_required(VERSION 3.5...3.29)` 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.29)
|
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
|
||||||
else()
|
|
||||||
cmake_policy(VERSION 3.29)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Filter out items; print an optional message if any items filtered. This ignores extensions.
|
# Filter out items; print an optional message if any items filtered. This ignores extensions.
|
||||||
#
|
#
|
||||||
@ -76,8 +67,8 @@ project(pybind11_tests CXX)
|
|||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../tools")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../tools")
|
||||||
|
|
||||||
option(PYBIND11_WERROR "Report all warnings as errors" OFF)
|
option(PYBIND11_WERROR "Report all warnings as errors" OFF)
|
||||||
option(DOWNLOAD_EIGEN "Download EIGEN (requires CMake 3.11+)" OFF)
|
option(DOWNLOAD_EIGEN "Download EIGEN" OFF)
|
||||||
option(PYBIND11_CUDA_TESTS "Enable building CUDA tests (requires CMake 3.12+)" OFF)
|
option(PYBIND11_CUDA_TESTS "Enable building CUDA tests" OFF)
|
||||||
set(PYBIND11_TEST_OVERRIDE
|
set(PYBIND11_TEST_OVERRIDE
|
||||||
""
|
""
|
||||||
CACHE STRING "Tests from ;-separated list of *.cpp files will be built instead of all tests")
|
CACHE STRING "Tests from ;-separated list of *.cpp files will be built instead of all tests")
|
||||||
@ -127,6 +118,7 @@ set(PYBIND11_TEST_FILES
|
|||||||
test_const_name
|
test_const_name
|
||||||
test_constants_and_functions
|
test_constants_and_functions
|
||||||
test_copy_move
|
test_copy_move
|
||||||
|
test_cpp_conduit
|
||||||
test_custom_type_casters
|
test_custom_type_casters
|
||||||
test_custom_type_setup
|
test_custom_type_setup
|
||||||
test_docstring_options
|
test_docstring_options
|
||||||
@ -158,11 +150,13 @@ set(PYBIND11_TEST_FILES
|
|||||||
test_tagbased_polymorphic
|
test_tagbased_polymorphic
|
||||||
test_thread
|
test_thread
|
||||||
test_type_caster_pyobject_ptr
|
test_type_caster_pyobject_ptr
|
||||||
|
test_type_caster_std_function_specializations
|
||||||
test_union
|
test_union
|
||||||
test_unnamed_namespace_a
|
test_unnamed_namespace_a
|
||||||
test_unnamed_namespace_b
|
test_unnamed_namespace_b
|
||||||
test_vector_unique_ptr_member
|
test_vector_unique_ptr_member
|
||||||
test_virtual_functions)
|
test_virtual_functions
|
||||||
|
test_warnings)
|
||||||
|
|
||||||
# Invoking cmake with something like:
|
# Invoking cmake with something like:
|
||||||
# cmake -DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_pickling.cpp" ..
|
# cmake -DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_pickling.cpp" ..
|
||||||
@ -225,6 +219,8 @@ tests_extra_targets("test_exceptions.py;test_local_bindings.py;test_stl.py;test_
|
|||||||
# And add additional targets for other tests.
|
# And add additional targets for other tests.
|
||||||
tests_extra_targets("test_exceptions.py" "cross_module_interleaved_error_already_set")
|
tests_extra_targets("test_exceptions.py" "cross_module_interleaved_error_already_set")
|
||||||
tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils")
|
tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils")
|
||||||
|
tests_extra_targets("test_cpp_conduit.py"
|
||||||
|
"exo_planet_pybind11;exo_planet_c_api;home_planet_very_lonely_traveler")
|
||||||
|
|
||||||
set(PYBIND11_EIGEN_REPO
|
set(PYBIND11_EIGEN_REPO
|
||||||
"https://gitlab.com/libeigen/eigen.git"
|
"https://gitlab.com/libeigen/eigen.git"
|
||||||
@ -248,25 +244,21 @@ endif()
|
|||||||
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
||||||
# Try loading via newer Eigen's Eigen3Config first (bypassing tools/FindEigen3.cmake).
|
# Try loading via newer Eigen's Eigen3Config first (bypassing tools/FindEigen3.cmake).
|
||||||
# Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also
|
# Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also
|
||||||
# produces a fatal error if loaded from a pre-3.0 cmake.
|
|
||||||
if(DOWNLOAD_EIGEN)
|
if(DOWNLOAD_EIGEN)
|
||||||
if(CMAKE_VERSION VERSION_LESS 3.11)
|
if(CMAKE_VERSION VERSION_LESS 3.18)
|
||||||
message(FATAL_ERROR "CMake 3.11+ required when using DOWNLOAD_EIGEN")
|
set(_opts)
|
||||||
|
else()
|
||||||
|
set(_opts SOURCE_SUBDIR no-cmake-build)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
eigen
|
eigen
|
||||||
GIT_REPOSITORY "${PYBIND11_EIGEN_REPO}"
|
GIT_REPOSITORY "${PYBIND11_EIGEN_REPO}"
|
||||||
GIT_TAG "${PYBIND11_EIGEN_VERSION_HASH}")
|
GIT_TAG "${PYBIND11_EIGEN_VERSION_HASH}"
|
||||||
|
${_opts})
|
||||||
FetchContent_GetProperties(eigen)
|
FetchContent_MakeAvailable(eigen)
|
||||||
if(NOT eigen_POPULATED)
|
if(NOT CMAKE_VERSION VERSION_LESS 3.18)
|
||||||
message(
|
set(EIGEN3_INCLUDE_DIR "${eigen_SOURCE_DIR}")
|
||||||
STATUS
|
|
||||||
"Downloading Eigen ${PYBIND11_EIGEN_VERSION_STRING} (${PYBIND11_EIGEN_VERSION_HASH}) from ${PYBIND11_EIGEN_REPO}"
|
|
||||||
)
|
|
||||||
FetchContent_Populate(eigen)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR})
|
set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR})
|
||||||
@ -314,8 +306,7 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
|||||||
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
||||||
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
|
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
|
||||||
endif()
|
endif()
|
||||||
message(
|
message(STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN=ON to download")
|
||||||
STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN=ON on CMake 3.11+ to download")
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -395,6 +386,9 @@ function(pybind11_enable_warnings target_name)
|
|||||||
-Wdeprecated
|
-Wdeprecated
|
||||||
-Wundef
|
-Wundef
|
||||||
-Wnon-virtual-dtor)
|
-Wnon-virtual-dtor)
|
||||||
|
if(DEFINED CMAKE_CXX_STANDARD AND NOT CMAKE_CXX_STANDARD VERSION_LESS 20)
|
||||||
|
target_compile_options(${target_name} PRIVATE -Wpedantic)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(PYBIND11_WERROR)
|
if(PYBIND11_WERROR)
|
||||||
@ -500,12 +494,10 @@ foreach(target ${test_targets})
|
|||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
# Provide nice organisation in IDEs
|
# Provide nice organisation in IDEs
|
||||||
if(NOT CMAKE_VERSION VERSION_LESS 3.8)
|
source_group(
|
||||||
source_group(
|
TREE "${CMAKE_CURRENT_SOURCE_DIR}/../include"
|
||||||
TREE "${CMAKE_CURRENT_SOURCE_DIR}/../include"
|
PREFIX "Header Files"
|
||||||
PREFIX "Header Files"
|
FILES ${PYBIND11_HEADERS})
|
||||||
FILES ${PYBIND11_HEADERS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Make sure pytest is found or produce a warning
|
# Make sure pytest is found or produce a warning
|
||||||
pybind11_find_import(pytest VERSION 3.1)
|
pybind11_find_import(pytest VERSION 3.1)
|
||||||
|
@ -136,7 +136,7 @@ class Capture:
|
|||||||
return Output(self.err)
|
return Output(self.err)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture
|
||||||
def capture(capsys):
|
def capture(capsys):
|
||||||
"""Extended `capsys` with context manager and custom equality operators"""
|
"""Extended `capsys` with context manager and custom equality operators"""
|
||||||
return Capture(capsys)
|
return Capture(capsys)
|
||||||
@ -172,7 +172,7 @@ def _sanitize_docstring(thing):
|
|||||||
return _sanitize_general(s)
|
return _sanitize_general(s)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture
|
||||||
def doc():
|
def doc():
|
||||||
"""Sanitize docstrings and add custom failure explanation"""
|
"""Sanitize docstrings and add custom failure explanation"""
|
||||||
return SanitizedString(_sanitize_docstring)
|
return SanitizedString(_sanitize_docstring)
|
||||||
@ -184,7 +184,7 @@ def _sanitize_message(thing):
|
|||||||
return _hexadecimal.sub("0", s)
|
return _hexadecimal.sub("0", s)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture
|
||||||
def msg():
|
def msg():
|
||||||
"""Sanitize messages and add custom failure explanation"""
|
"""Sanitize messages and add custom failure explanation"""
|
||||||
return SanitizedString(_sanitize_message)
|
return SanitizedString(_sanitize_message)
|
||||||
|
10
tests/custom_exceptions.py
Normal file
10
tests/custom_exceptions.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
||||||
|
class PythonMyException7(Exception):
|
||||||
|
def __init__(self, message):
|
||||||
|
self.message = message
|
||||||
|
super().__init__(message)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "[PythonMyException7]: " + self.message.a
|
103
tests/exo_planet_c_api.cpp
Normal file
103
tests/exo_planet_c_api.cpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// Copyright (c) 2024 The pybind Community.
|
||||||
|
|
||||||
|
// THIS MUST STAY AT THE TOP!
|
||||||
|
#include <pybind11/pybind11.h> // EXCLUSIVELY for PYBIND11_PLATFORM_ABI_ID
|
||||||
|
// Potential future direction to maximize reusability:
|
||||||
|
// (e.g. for use from SWIG, Cython, PyCLIF, nanobind):
|
||||||
|
// #include <pybind11/compat/platform_abi_id.h>
|
||||||
|
// This would only depend on:
|
||||||
|
// 1. A C++ compiler, WITHOUT requiring -fexceptions.
|
||||||
|
// 2. Python.h
|
||||||
|
|
||||||
|
#include "test_cpp_conduit_traveler_types.h"
|
||||||
|
|
||||||
|
#include <Python.h>
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void *get_cpp_conduit_void_ptr(PyObject *py_obj, const std::type_info *cpp_type_info) {
|
||||||
|
PyObject *cpp_type_info_capsule
|
||||||
|
= PyCapsule_New(const_cast<void *>(static_cast<const void *>(cpp_type_info)),
|
||||||
|
typeid(std::type_info).name(),
|
||||||
|
nullptr);
|
||||||
|
if (cpp_type_info_capsule == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
PyObject *cpp_conduit = PyObject_CallMethod(py_obj,
|
||||||
|
"_pybind11_conduit_v1_",
|
||||||
|
"yOy",
|
||||||
|
PYBIND11_PLATFORM_ABI_ID,
|
||||||
|
cpp_type_info_capsule,
|
||||||
|
"raw_pointer_ephemeral");
|
||||||
|
Py_DECREF(cpp_type_info_capsule);
|
||||||
|
if (cpp_conduit == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
void *void_ptr = PyCapsule_GetPointer(cpp_conduit, cpp_type_info->name());
|
||||||
|
Py_DECREF(cpp_conduit);
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return void_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T *get_cpp_conduit_type_ptr(PyObject *py_obj) {
|
||||||
|
void *void_ptr = get_cpp_conduit_void_ptr(py_obj, &typeid(T));
|
||||||
|
if (void_ptr == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return static_cast<T *>(void_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" PyObject *wrapGetLuggage(PyObject * /*self*/, PyObject *traveler) {
|
||||||
|
const auto *cpp_traveler
|
||||||
|
= get_cpp_conduit_type_ptr<pybind11_tests::test_cpp_conduit::Traveler>(traveler);
|
||||||
|
if (cpp_traveler == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return PyUnicode_FromString(cpp_traveler->luggage.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" PyObject *wrapGetPoints(PyObject * /*self*/, PyObject *premium_traveler) {
|
||||||
|
const auto *cpp_premium_traveler
|
||||||
|
= get_cpp_conduit_type_ptr<pybind11_tests::test_cpp_conduit::PremiumTraveler>(
|
||||||
|
premium_traveler);
|
||||||
|
if (cpp_premium_traveler == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return PyLong_FromLong(static_cast<long>(cpp_premium_traveler->points));
|
||||||
|
}
|
||||||
|
|
||||||
|
PyMethodDef ThisMethodDef[] = {{"GetLuggage", wrapGetLuggage, METH_O, nullptr},
|
||||||
|
{"GetPoints", wrapGetPoints, METH_O, nullptr},
|
||||||
|
{nullptr, nullptr, 0, nullptr}};
|
||||||
|
|
||||||
|
struct PyModuleDef ThisModuleDef = {
|
||||||
|
PyModuleDef_HEAD_INIT, // m_base
|
||||||
|
"exo_planet_c_api", // m_name
|
||||||
|
nullptr, // m_doc
|
||||||
|
-1, // m_size
|
||||||
|
ThisMethodDef, // m_methods
|
||||||
|
nullptr, // m_slots
|
||||||
|
nullptr, // m_traverse
|
||||||
|
nullptr, // m_clear
|
||||||
|
nullptr // m_free
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#if defined(WIN32) || defined(_WIN32)
|
||||||
|
# define EXO_PLANET_C_API_EXPORT __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
# define EXO_PLANET_C_API_EXPORT __attribute__((visibility("default")))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern "C" EXO_PLANET_C_API_EXPORT PyObject *PyInit_exo_planet_c_api() {
|
||||||
|
PyObject *m = PyModule_Create(&ThisModuleDef);
|
||||||
|
if (m == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
19
tests/exo_planet_pybind11.cpp
Normal file
19
tests/exo_planet_pybind11.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) 2024 The pybind Community.
|
||||||
|
|
||||||
|
#if defined(PYBIND11_INTERNALS_VERSION)
|
||||||
|
# undef PYBIND11_INTERNALS_VERSION
|
||||||
|
#endif
|
||||||
|
#define PYBIND11_INTERNALS_VERSION 900000001
|
||||||
|
|
||||||
|
#include "test_cpp_conduit_traveler_bindings.h"
|
||||||
|
|
||||||
|
namespace pybind11_tests {
|
||||||
|
namespace test_cpp_conduit {
|
||||||
|
|
||||||
|
PYBIND11_MODULE(exo_planet_pybind11, m) {
|
||||||
|
wrap_traveler(m);
|
||||||
|
m.def("wrap_very_lonely_traveler", [m]() { wrap_very_lonely_traveler(m); });
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test_cpp_conduit
|
||||||
|
} // namespace pybind11_tests
|
@ -9,7 +9,6 @@ import tarfile
|
|||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
# These tests must be run explicitly
|
# These tests must be run explicitly
|
||||||
# They require CMake 3.15+ (--install)
|
|
||||||
|
|
||||||
DIR = os.path.abspath(os.path.dirname(__file__))
|
DIR = os.path.abspath(os.path.dirname(__file__))
|
||||||
MAIN_DIR = os.path.dirname(os.path.dirname(DIR))
|
MAIN_DIR = os.path.dirname(os.path.dirname(DIR))
|
||||||
@ -48,17 +47,20 @@ main_headers = {
|
|||||||
"include/pybind11/stl_bind.h",
|
"include/pybind11/stl_bind.h",
|
||||||
"include/pybind11/type_caster_pyobject_ptr.h",
|
"include/pybind11/type_caster_pyobject_ptr.h",
|
||||||
"include/pybind11/typing.h",
|
"include/pybind11/typing.h",
|
||||||
|
"include/pybind11/warnings.h",
|
||||||
}
|
}
|
||||||
|
|
||||||
detail_headers = {
|
detail_headers = {
|
||||||
"include/pybind11/detail/class.h",
|
"include/pybind11/detail/class.h",
|
||||||
"include/pybind11/detail/common.h",
|
"include/pybind11/detail/common.h",
|
||||||
|
"include/pybind11/detail/cpp_conduit.h",
|
||||||
"include/pybind11/detail/descr.h",
|
"include/pybind11/detail/descr.h",
|
||||||
"include/pybind11/detail/init.h",
|
"include/pybind11/detail/init.h",
|
||||||
"include/pybind11/detail/internals.h",
|
"include/pybind11/detail/internals.h",
|
||||||
"include/pybind11/detail/type_caster_base.h",
|
"include/pybind11/detail/type_caster_base.h",
|
||||||
"include/pybind11/detail/typeid.h",
|
"include/pybind11/detail/typeid.h",
|
||||||
"include/pybind11/detail/value_and_holder.h",
|
"include/pybind11/detail/value_and_holder.h",
|
||||||
|
"include/pybind11/detail/exception_translation.h",
|
||||||
}
|
}
|
||||||
|
|
||||||
eigen_headers = {
|
eigen_headers = {
|
||||||
|
13
tests/home_planet_very_lonely_traveler.cpp
Normal file
13
tests/home_planet_very_lonely_traveler.cpp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) 2024 The pybind Community.
|
||||||
|
|
||||||
|
#include "test_cpp_conduit_traveler_bindings.h"
|
||||||
|
|
||||||
|
namespace pybind11_tests {
|
||||||
|
namespace test_cpp_conduit {
|
||||||
|
|
||||||
|
PYBIND11_MODULE(home_planet_very_lonely_traveler, m) {
|
||||||
|
m.def("wrap_very_lonely_traveler", [m]() { wrap_very_lonely_traveler(m); });
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test_cpp_conduit
|
||||||
|
} // namespace pybind11_tests
|
@ -56,13 +56,13 @@ private:
|
|||||||
std::string message = "";
|
std::string message = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
PYBIND11_MAKE_OPAQUE(LocalVec);
|
PYBIND11_MAKE_OPAQUE(LocalVec)
|
||||||
PYBIND11_MAKE_OPAQUE(LocalVec2);
|
PYBIND11_MAKE_OPAQUE(LocalVec2)
|
||||||
PYBIND11_MAKE_OPAQUE(LocalMap);
|
PYBIND11_MAKE_OPAQUE(LocalMap)
|
||||||
PYBIND11_MAKE_OPAQUE(NonLocalVec);
|
PYBIND11_MAKE_OPAQUE(NonLocalVec)
|
||||||
// PYBIND11_MAKE_OPAQUE(NonLocalVec2); // same type as LocalVec2
|
// PYBIND11_MAKE_OPAQUE(NonLocalVec2) // same type as LocalVec2
|
||||||
PYBIND11_MAKE_OPAQUE(NonLocalMap);
|
PYBIND11_MAKE_OPAQUE(NonLocalMap)
|
||||||
PYBIND11_MAKE_OPAQUE(NonLocalMap2);
|
PYBIND11_MAKE_OPAQUE(NonLocalMap2)
|
||||||
|
|
||||||
// Simple bindings (used with the above):
|
// Simple bindings (used with the above):
|
||||||
template <typename T, int Adjust = 0, typename... Args>
|
template <typename T, int Adjust = 0, typename... Args>
|
||||||
@ -70,7 +70,7 @@ py::class_<T> bind_local(Args &&...args) {
|
|||||||
return py::class_<T>(std::forward<Args>(args)...).def(py::init<int>()).def("get", [](T &i) {
|
return py::class_<T>(std::forward<Args>(args)...).def(py::init<int>()).def("get", [](T &i) {
|
||||||
return i.i + Adjust;
|
return i.i + Adjust;
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// Simulate a foreign library base class (to match the example in the docs):
|
// Simulate a foreign library base class (to match the example in the docs):
|
||||||
namespace pets {
|
namespace pets {
|
||||||
|
@ -11,7 +11,7 @@ if sys.platform.startswith("emscripten"):
|
|||||||
pytest.skip("Can't run a new event_loop in pyodide", allow_module_level=True)
|
pytest.skip("Can't run a new event_loop in pyodide", allow_module_level=True)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture
|
||||||
def event_loop():
|
def event_loop():
|
||||||
loop = asyncio.new_event_loop()
|
loop = asyncio.new_event_loop()
|
||||||
yield loop
|
yield loop
|
||||||
|
@ -167,6 +167,18 @@ TEST_SUBMODULE(buffers, m) {
|
|||||||
sizeof(float)});
|
sizeof(float)});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
class BrokenMatrix : public Matrix {
|
||||||
|
public:
|
||||||
|
BrokenMatrix(py::ssize_t rows, py::ssize_t cols) : Matrix(rows, cols) {}
|
||||||
|
void throw_runtime_error() { throw std::runtime_error("See PR #5324 for context."); }
|
||||||
|
};
|
||||||
|
py::class_<BrokenMatrix>(m, "BrokenMatrix", py::buffer_protocol())
|
||||||
|
.def(py::init<py::ssize_t, py::ssize_t>())
|
||||||
|
.def_buffer([](BrokenMatrix &m) {
|
||||||
|
m.throw_runtime_error();
|
||||||
|
return py::buffer_info();
|
||||||
|
});
|
||||||
|
|
||||||
// test_inherited_protocol
|
// test_inherited_protocol
|
||||||
class SquareMatrix : public Matrix {
|
class SquareMatrix : public Matrix {
|
||||||
public:
|
public:
|
||||||
|
@ -228,3 +228,10 @@ def test_buffer_docstring():
|
|||||||
m.get_buffer_info.__doc__.strip()
|
m.get_buffer_info.__doc__.strip()
|
||||||
== "get_buffer_info(arg0: Buffer) -> pybind11_tests.buffers.buffer_info"
|
== "get_buffer_info(arg0: Buffer) -> pybind11_tests.buffers.buffer_info"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_buffer_exception():
|
||||||
|
with pytest.raises(BufferError, match="Error getting buffer") as excinfo:
|
||||||
|
memoryview(m.BrokenMatrix(1, 1))
|
||||||
|
assert isinstance(excinfo.value.__cause__, RuntimeError)
|
||||||
|
assert "for context" in str(excinfo.value.__cause__)
|
||||||
|
@ -148,7 +148,7 @@ TEST_SUBMODULE(callbacks, m) {
|
|||||||
m.def("dummy_function2", [](int i, int j) { return i + j; });
|
m.def("dummy_function2", [](int i, int j) { return i + j; });
|
||||||
m.def(
|
m.def(
|
||||||
"roundtrip",
|
"roundtrip",
|
||||||
[](std::function<int(int)> f, bool expect_none = false) {
|
[](std::function<int(int)> f, bool expect_none) {
|
||||||
if (expect_none && f) {
|
if (expect_none && f) {
|
||||||
throw std::runtime_error("Expected None to be converted to empty std::function");
|
throw std::runtime_error("Expected None to be converted to empty std::function");
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.15...3.30)
|
||||||
|
|
||||||
# The `cmake_minimum_required(VERSION 3.5...3.29)` 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.29)
|
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
|
||||||
else()
|
|
||||||
cmake_policy(VERSION 3.29)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
project(test_installed_embed CXX)
|
project(test_installed_embed CXX)
|
||||||
|
|
||||||
|
@ -1,14 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.15...3.30)
|
||||||
project(test_installed_module CXX)
|
|
||||||
|
|
||||||
# The `cmake_minimum_required(VERSION 3.5...3.29)` 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.29)
|
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
|
||||||
else()
|
|
||||||
cmake_policy(VERSION 3.29)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
project(test_installed_function CXX)
|
project(test_installed_function CXX)
|
||||||
|
|
||||||
|
@ -1,13 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.15...3.30)
|
||||||
|
|
||||||
# The `cmake_minimum_required(VERSION 3.5...3.29)` 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.29)
|
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
|
||||||
else()
|
|
||||||
cmake_policy(VERSION 3.29)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
project(test_installed_target CXX)
|
project(test_installed_target CXX)
|
||||||
|
|
||||||
|
@ -1,13 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.15...3.30)
|
||||||
|
|
||||||
# The `cmake_minimum_required(VERSION 3.5...3.29)` 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.29)
|
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
|
||||||
else()
|
|
||||||
cmake_policy(VERSION 3.29)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
project(test_subdirectory_embed CXX)
|
project(test_subdirectory_embed CXX)
|
||||||
|
|
||||||
|
@ -1,13 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.15...3.30)
|
||||||
|
|
||||||
# The `cmake_minimum_required(VERSION 3.5...3.29)` 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.29)
|
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
|
||||||
else()
|
|
||||||
cmake_policy(VERSION 3.29)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
project(test_subdirectory_function CXX)
|
project(test_subdirectory_function CXX)
|
||||||
|
|
||||||
|
@ -1,13 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.15...3.30)
|
||||||
|
|
||||||
# The `cmake_minimum_required(VERSION 3.5...3.29)` 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.29)
|
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
|
||||||
else()
|
|
||||||
cmake_policy(VERSION 3.29)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
project(test_subdirectory_target CXX)
|
project(test_subdirectory_target CXX)
|
||||||
|
|
||||||
|
22
tests/test_cpp_conduit.cpp
Normal file
22
tests/test_cpp_conduit.cpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) 2024 The pybind Community.
|
||||||
|
|
||||||
|
#include "pybind11_tests.h"
|
||||||
|
#include "test_cpp_conduit_traveler_bindings.h"
|
||||||
|
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
|
namespace pybind11_tests {
|
||||||
|
namespace test_cpp_conduit {
|
||||||
|
|
||||||
|
TEST_SUBMODULE(cpp_conduit, m) {
|
||||||
|
m.attr("PYBIND11_PLATFORM_ABI_ID") = py::bytes(PYBIND11_PLATFORM_ABI_ID);
|
||||||
|
m.attr("cpp_type_info_capsule_Traveler")
|
||||||
|
= py::capsule(&typeid(Traveler), typeid(std::type_info).name());
|
||||||
|
m.attr("cpp_type_info_capsule_int") = py::capsule(&typeid(int), typeid(std::type_info).name());
|
||||||
|
|
||||||
|
wrap_traveler(m);
|
||||||
|
wrap_lonely_traveler(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test_cpp_conduit
|
||||||
|
} // namespace pybind11_tests
|
162
tests/test_cpp_conduit.py
Normal file
162
tests/test_cpp_conduit.py
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
# Copyright (c) 2024 The pybind Community.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import exo_planet_c_api
|
||||||
|
import exo_planet_pybind11
|
||||||
|
import home_planet_very_lonely_traveler
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from pybind11_tests import cpp_conduit as home_planet
|
||||||
|
|
||||||
|
|
||||||
|
def test_traveler_getattr_actually_exists():
|
||||||
|
t_h = home_planet.Traveler("home")
|
||||||
|
assert t_h.any_name == "Traveler GetAttr: any_name luggage: home"
|
||||||
|
|
||||||
|
|
||||||
|
def test_premium_traveler_getattr_actually_exists():
|
||||||
|
t_h = home_planet.PremiumTraveler("home", 7)
|
||||||
|
assert t_h.secret_name == "PremiumTraveler GetAttr: secret_name points: 7"
|
||||||
|
|
||||||
|
|
||||||
|
def test_call_cpp_conduit_success():
|
||||||
|
t_h = home_planet.Traveler("home")
|
||||||
|
cap = t_h._pybind11_conduit_v1_(
|
||||||
|
home_planet.PYBIND11_PLATFORM_ABI_ID,
|
||||||
|
home_planet.cpp_type_info_capsule_Traveler,
|
||||||
|
b"raw_pointer_ephemeral",
|
||||||
|
)
|
||||||
|
assert cap.__class__.__name__ == "PyCapsule"
|
||||||
|
|
||||||
|
|
||||||
|
def test_call_cpp_conduit_platform_abi_id_mismatch():
|
||||||
|
t_h = home_planet.Traveler("home")
|
||||||
|
cap = t_h._pybind11_conduit_v1_(
|
||||||
|
home_planet.PYBIND11_PLATFORM_ABI_ID + b"MISMATCH",
|
||||||
|
home_planet.cpp_type_info_capsule_Traveler,
|
||||||
|
b"raw_pointer_ephemeral",
|
||||||
|
)
|
||||||
|
assert cap is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_call_cpp_conduit_cpp_type_info_capsule_mismatch():
|
||||||
|
t_h = home_planet.Traveler("home")
|
||||||
|
cap = t_h._pybind11_conduit_v1_(
|
||||||
|
home_planet.PYBIND11_PLATFORM_ABI_ID,
|
||||||
|
home_planet.cpp_type_info_capsule_int,
|
||||||
|
b"raw_pointer_ephemeral",
|
||||||
|
)
|
||||||
|
assert cap is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_call_cpp_conduit_pointer_kind_invalid():
|
||||||
|
t_h = home_planet.Traveler("home")
|
||||||
|
with pytest.raises(
|
||||||
|
RuntimeError, match='^Invalid pointer_kind: "raw_pointer_ephemreal"$'
|
||||||
|
):
|
||||||
|
t_h._pybind11_conduit_v1_(
|
||||||
|
home_planet.PYBIND11_PLATFORM_ABI_ID,
|
||||||
|
home_planet.cpp_type_info_capsule_Traveler,
|
||||||
|
b"raw_pointer_ephemreal",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_home_only_basic():
|
||||||
|
t_h = home_planet.Traveler("home")
|
||||||
|
assert t_h.luggage == "home"
|
||||||
|
assert home_planet.get_luggage(t_h) == "home"
|
||||||
|
|
||||||
|
|
||||||
|
def test_home_only_premium():
|
||||||
|
p_h = home_planet.PremiumTraveler("home", 2)
|
||||||
|
assert p_h.luggage == "home"
|
||||||
|
assert home_planet.get_luggage(p_h) == "home"
|
||||||
|
assert home_planet.get_points(p_h) == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_exo_only_basic():
|
||||||
|
t_e = exo_planet_pybind11.Traveler("exo")
|
||||||
|
assert t_e.luggage == "exo"
|
||||||
|
assert exo_planet_pybind11.get_luggage(t_e) == "exo"
|
||||||
|
|
||||||
|
|
||||||
|
def test_exo_only_premium():
|
||||||
|
p_e = exo_planet_pybind11.PremiumTraveler("exo", 3)
|
||||||
|
assert p_e.luggage == "exo"
|
||||||
|
assert exo_planet_pybind11.get_luggage(p_e) == "exo"
|
||||||
|
assert exo_planet_pybind11.get_points(p_e) == 3
|
||||||
|
|
||||||
|
|
||||||
|
def test_home_passed_to_exo_basic():
|
||||||
|
t_h = home_planet.Traveler("home")
|
||||||
|
assert exo_planet_pybind11.get_luggage(t_h) == "home"
|
||||||
|
|
||||||
|
|
||||||
|
def test_exo_passed_to_home_basic():
|
||||||
|
t_e = exo_planet_pybind11.Traveler("exo")
|
||||||
|
assert home_planet.get_luggage(t_e) == "exo"
|
||||||
|
|
||||||
|
|
||||||
|
def test_home_passed_to_exo_premium():
|
||||||
|
p_h = home_planet.PremiumTraveler("home", 2)
|
||||||
|
assert exo_planet_pybind11.get_luggage(p_h) == "home"
|
||||||
|
assert exo_planet_pybind11.get_points(p_h) == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_exo_passed_to_home_premium():
|
||||||
|
p_e = exo_planet_pybind11.PremiumTraveler("exo", 3)
|
||||||
|
assert home_planet.get_luggage(p_e) == "exo"
|
||||||
|
assert home_planet.get_points(p_e) == 3
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"traveler_type", [home_planet.Traveler, exo_planet_pybind11.Traveler]
|
||||||
|
)
|
||||||
|
def test_exo_planet_c_api_traveler(traveler_type):
|
||||||
|
t = traveler_type("socks")
|
||||||
|
assert exo_planet_c_api.GetLuggage(t) == "socks"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"premium_traveler_type",
|
||||||
|
[home_planet.PremiumTraveler, exo_planet_pybind11.PremiumTraveler],
|
||||||
|
)
|
||||||
|
def test_exo_planet_c_api_premium_traveler(premium_traveler_type):
|
||||||
|
pt = premium_traveler_type("gucci", 5)
|
||||||
|
assert exo_planet_c_api.GetLuggage(pt) == "gucci"
|
||||||
|
assert exo_planet_c_api.GetPoints(pt) == 5
|
||||||
|
|
||||||
|
|
||||||
|
def test_home_planet_wrap_very_lonely_traveler():
|
||||||
|
# This does not exercise the cpp_conduit feature, but is here to
|
||||||
|
# demonstrate that the cpp_conduit feature does not solve all
|
||||||
|
# cross-extension interoperability issues.
|
||||||
|
# Here is the proof that the following works for extensions with
|
||||||
|
# matching `PYBIND11_INTERNALS_ID`s:
|
||||||
|
# test_cpp_conduit.cpp:
|
||||||
|
# py::class_<LonelyTraveler>
|
||||||
|
# home_planet_very_lonely_traveler.cpp:
|
||||||
|
# py::class_<VeryLonelyTraveler, LonelyTraveler>
|
||||||
|
# See test_exo_planet_pybind11_wrap_very_lonely_traveler() for the negative
|
||||||
|
# test.
|
||||||
|
assert home_planet.LonelyTraveler is not None # Verify that the base class exists.
|
||||||
|
home_planet_very_lonely_traveler.wrap_very_lonely_traveler()
|
||||||
|
# Ensure that the derived class exists.
|
||||||
|
assert home_planet_very_lonely_traveler.VeryLonelyTraveler is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_exo_planet_pybind11_wrap_very_lonely_traveler():
|
||||||
|
# See comment under test_home_planet_wrap_very_lonely_traveler() first.
|
||||||
|
# Here the `PYBIND11_INTERNALS_ID`s don't match between:
|
||||||
|
# test_cpp_conduit.cpp:
|
||||||
|
# py::class_<LonelyTraveler>
|
||||||
|
# exo_planet_pybind11.cpp:
|
||||||
|
# py::class_<VeryLonelyTraveler, LonelyTraveler>
|
||||||
|
assert home_planet.LonelyTraveler is not None # Verify that the base class exists.
|
||||||
|
with pytest.raises(
|
||||||
|
RuntimeError,
|
||||||
|
match='^generic_type: type "VeryLonelyTraveler" referenced unknown base type '
|
||||||
|
'"pybind11_tests::test_cpp_conduit::LonelyTraveler"$',
|
||||||
|
):
|
||||||
|
exo_planet_pybind11.wrap_very_lonely_traveler()
|
47
tests/test_cpp_conduit_traveler_bindings.h
Normal file
47
tests/test_cpp_conduit_traveler_bindings.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (c) 2024 The pybind Community.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
|
||||||
|
#include "test_cpp_conduit_traveler_types.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace pybind11_tests {
|
||||||
|
namespace test_cpp_conduit {
|
||||||
|
|
||||||
|
namespace py = pybind11;
|
||||||
|
|
||||||
|
inline void wrap_traveler(py::module_ m) {
|
||||||
|
py::class_<Traveler>(m, "Traveler")
|
||||||
|
.def(py::init<std::string>())
|
||||||
|
.def_readwrite("luggage", &Traveler::luggage)
|
||||||
|
// See issue #3788:
|
||||||
|
.def("__getattr__", [](const Traveler &self, const std::string &key) {
|
||||||
|
return "Traveler GetAttr: " + key + " luggage: " + self.luggage;
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("get_luggage", [](const Traveler &person) { return person.luggage; });
|
||||||
|
|
||||||
|
py::class_<PremiumTraveler, Traveler>(m, "PremiumTraveler")
|
||||||
|
.def(py::init<std::string, int>())
|
||||||
|
.def_readwrite("points", &PremiumTraveler::points)
|
||||||
|
// See issue #3788:
|
||||||
|
.def("__getattr__", [](const PremiumTraveler &self, const std::string &key) {
|
||||||
|
return "PremiumTraveler GetAttr: " + key + " points: " + std::to_string(self.points);
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("get_points", [](const PremiumTraveler &person) { return person.points; });
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void wrap_lonely_traveler(py::module_ m) {
|
||||||
|
py::class_<LonelyTraveler>(std::move(m), "LonelyTraveler");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void wrap_very_lonely_traveler(py::module_ m) {
|
||||||
|
py::class_<VeryLonelyTraveler, LonelyTraveler>(std::move(m), "VeryLonelyTraveler");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test_cpp_conduit
|
||||||
|
} // namespace pybind11_tests
|
25
tests/test_cpp_conduit_traveler_types.h
Normal file
25
tests/test_cpp_conduit_traveler_types.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (c) 2024 The pybind Community.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace pybind11_tests {
|
||||||
|
namespace test_cpp_conduit {
|
||||||
|
|
||||||
|
struct Traveler {
|
||||||
|
explicit Traveler(const std::string &luggage) : luggage(luggage) {}
|
||||||
|
std::string luggage;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PremiumTraveler : Traveler {
|
||||||
|
explicit PremiumTraveler(const std::string &luggage, int points)
|
||||||
|
: Traveler(luggage), points(points) {}
|
||||||
|
int points;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LonelyTraveler {};
|
||||||
|
struct VeryLonelyTraveler : LonelyTraveler {};
|
||||||
|
|
||||||
|
} // namespace test_cpp_conduit
|
||||||
|
} // namespace pybind11_tests
|
@ -9,7 +9,7 @@ import env # noqa: F401
|
|||||||
from pybind11_tests import custom_type_setup as m
|
from pybind11_tests import custom_type_setup as m
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture
|
||||||
def gc_tester():
|
def gc_tester():
|
||||||
"""Tests that an object is garbage collected.
|
"""Tests that an object is garbage collected.
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ void reset_refs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns element 2,1 from a matrix (used to test copy/nocopy)
|
// Returns element 2,1 from a matrix (used to test copy/nocopy)
|
||||||
double get_elem(const Eigen::Ref<const Eigen::MatrixXd> &m) { return m(2, 1); };
|
double get_elem(const Eigen::Ref<const Eigen::MatrixXd> &m) { return m(2, 1); }
|
||||||
|
|
||||||
// Returns a matrix with 10*r + 100*c added to each matrix element (to help test that the matrix
|
// Returns a matrix with 10*r + 100*c added to each matrix element (to help test that the matrix
|
||||||
// reference is referencing rows/columns correctly).
|
// reference is referencing rows/columns correctly).
|
||||||
@ -76,7 +76,7 @@ struct CustomOperatorNew {
|
|||||||
Eigen::Matrix4d a = Eigen::Matrix4d::Zero();
|
Eigen::Matrix4d a = Eigen::Matrix4d::Zero();
|
||||||
Eigen::Matrix4d b = Eigen::Matrix4d::Identity();
|
Eigen::Matrix4d b = Eigen::Matrix4d::Identity();
|
||||||
|
|
||||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
|
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_SUBMODULE(eigen_matrix, m) {
|
TEST_SUBMODULE(eigen_matrix, m) {
|
||||||
|
@ -111,6 +111,16 @@ struct PythonAlreadySetInDestructor {
|
|||||||
py::str s;
|
py::str s;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CustomData {
|
||||||
|
explicit CustomData(const std::string &a) : a(a) {}
|
||||||
|
std::string a;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MyException7 {
|
||||||
|
explicit MyException7(const CustomData &message) : message(message) {}
|
||||||
|
CustomData message;
|
||||||
|
};
|
||||||
|
|
||||||
TEST_SUBMODULE(exceptions, m) {
|
TEST_SUBMODULE(exceptions, m) {
|
||||||
m.def("throw_std_exception",
|
m.def("throw_std_exception",
|
||||||
[]() { throw std::runtime_error("This exception was intentionally thrown."); });
|
[]() { throw std::runtime_error("This exception was intentionally thrown."); });
|
||||||
@ -385,4 +395,33 @@ TEST_SUBMODULE(exceptions, m) {
|
|||||||
|
|
||||||
// m.def("pass_exception_void", [](const py::exception<void>&) {}); // Does not compile.
|
// m.def("pass_exception_void", [](const py::exception<void>&) {}); // Does not compile.
|
||||||
m.def("return_exception_void", []() { return py::exception<void>(); });
|
m.def("return_exception_void", []() { return py::exception<void>(); });
|
||||||
|
|
||||||
|
m.def("throws7", []() {
|
||||||
|
auto data = CustomData("abc");
|
||||||
|
throw MyException7(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
py::class_<CustomData>(m, "CustomData", py::module_local())
|
||||||
|
.def(py::init<const std::string &>())
|
||||||
|
.def_readwrite("a", &CustomData::a);
|
||||||
|
|
||||||
|
PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object>
|
||||||
|
PythonMyException7_storage;
|
||||||
|
PythonMyException7_storage.call_once_and_store_result([&]() {
|
||||||
|
auto mod = py::module_::import("custom_exceptions");
|
||||||
|
py::object obj = mod.attr("PythonMyException7");
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
|
||||||
|
py::register_local_exception_translator([](std::exception_ptr p) {
|
||||||
|
try {
|
||||||
|
if (p) {
|
||||||
|
std::rethrow_exception(p);
|
||||||
|
}
|
||||||
|
} catch (const MyException7 &e) {
|
||||||
|
auto exc_type = PythonMyException7_storage.get_stored();
|
||||||
|
py::object exc_inst = exc_type(e.message);
|
||||||
|
PyErr_SetObject(PyExc_Exception, exc_inst.ptr());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from custom_exceptions import PythonMyException7
|
||||||
|
|
||||||
import env
|
import env
|
||||||
import pybind11_cross_module_tests as cm
|
import pybind11_cross_module_tests as cm
|
||||||
@ -195,6 +196,10 @@ def test_custom(msg):
|
|||||||
raise RuntimeError("Exception error: caught child from parent") from err
|
raise RuntimeError("Exception error: caught child from parent") from err
|
||||||
assert msg(excinfo.value) == "this is a helper-defined translated exception"
|
assert msg(excinfo.value) == "this is a helper-defined translated exception"
|
||||||
|
|
||||||
|
with pytest.raises(PythonMyException7) as excinfo:
|
||||||
|
m.throws7()
|
||||||
|
assert msg(excinfo.value) == "[PythonMyException7]: abc"
|
||||||
|
|
||||||
|
|
||||||
def test_nested_throws(capture):
|
def test_nested_throws(capture):
|
||||||
"""Tests nested (e.g. C++ -> Python -> C++) exception handling"""
|
"""Tests nested (e.g. C++ -> Python -> C++) exception handling"""
|
||||||
|
@ -80,6 +80,61 @@ struct I801D : I801C {}; // Indirect MI
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace TrampolineNesting {
|
||||||
|
class ChainBaseA {
|
||||||
|
public:
|
||||||
|
ChainBaseA() = default;
|
||||||
|
ChainBaseA(const ChainBaseA &) = default;
|
||||||
|
ChainBaseA(ChainBaseA &&) = default;
|
||||||
|
virtual ~ChainBaseA() = default;
|
||||||
|
virtual int resultA() = 0;
|
||||||
|
};
|
||||||
|
class ChainChildA : public ChainBaseA {
|
||||||
|
public:
|
||||||
|
using ChainBaseA::ChainBaseA;
|
||||||
|
int resultA() override { return 1; }
|
||||||
|
};
|
||||||
|
class ChainBaseB {
|
||||||
|
public:
|
||||||
|
ChainBaseB() = default;
|
||||||
|
ChainBaseB(const ChainBaseB &) = default;
|
||||||
|
ChainBaseB(ChainBaseB &&) = default;
|
||||||
|
virtual ~ChainBaseB() = default;
|
||||||
|
virtual std::string resultB() = 0;
|
||||||
|
};
|
||||||
|
class ChainChildB : public ChainBaseB {
|
||||||
|
public:
|
||||||
|
using ChainBaseB::ChainBaseB;
|
||||||
|
std::string resultB() override { return "A"; }
|
||||||
|
};
|
||||||
|
class Joined : public ChainChildA, public ChainChildB {
|
||||||
|
public:
|
||||||
|
Joined() = default;
|
||||||
|
Joined(const Joined &) = default;
|
||||||
|
Joined(Joined &&) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Base = ChainBaseA, typename PureVirtualBase = ChainBaseA>
|
||||||
|
class TrampolineA : public Base {
|
||||||
|
public:
|
||||||
|
using Base::Base;
|
||||||
|
int resultA() override { PYBIND11_OVERRIDE_TEMPLATE(PureVirtualBase, int, Base, resultA, ) }
|
||||||
|
};
|
||||||
|
template <class Base = ChainBaseB, class PyBase = Base, typename PureVirtualBase = ChainBaseB>
|
||||||
|
class TrampolineB : public PyBase {
|
||||||
|
public:
|
||||||
|
using PyBase::PyBase;
|
||||||
|
std::string resultB() override {
|
||||||
|
PYBIND11_OVERRIDE_TEMPLATE(PureVirtualBase, std::string, Base, resultB, )
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <class Base = Joined, class PyBase = Base>
|
||||||
|
class TrampolineJoined : public TrampolineB<Base, TrampolineA<Base, PyBase>> {
|
||||||
|
public:
|
||||||
|
using TrampolineB<Base, TrampolineA<Base, PyBase>>::TrampolineB;
|
||||||
|
};
|
||||||
|
} // namespace TrampolineNesting
|
||||||
|
|
||||||
TEST_SUBMODULE(multiple_inheritance, m) {
|
TEST_SUBMODULE(multiple_inheritance, m) {
|
||||||
// Please do not interleave `struct` and `class` definitions with bindings code,
|
// Please do not interleave `struct` and `class` definitions with bindings code,
|
||||||
// but implement `struct`s and `class`es in the anonymous namespace above.
|
// but implement `struct`s and `class`es in the anonymous namespace above.
|
||||||
@ -338,4 +393,24 @@ TEST_SUBMODULE(multiple_inheritance, m) {
|
|||||||
.def("get_f_e", &MVF::get_f_e)
|
.def("get_f_e", &MVF::get_f_e)
|
||||||
.def("get_f_f", &MVF::get_f_f)
|
.def("get_f_f", &MVF::get_f_f)
|
||||||
.def_readwrite("f", &MVF::f);
|
.def_readwrite("f", &MVF::f);
|
||||||
|
|
||||||
|
namespace TN = TrampolineNesting;
|
||||||
|
py::class_<TN::ChainBaseA, TN::TrampolineA<>>(m, "ChainBaseA")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def("resultA", &TN::ChainBaseA::resultA);
|
||||||
|
py::class_<TN::ChainChildA, TN::ChainBaseA, TN::TrampolineA<TN::ChainChildA>>(m, "ChainChildA")
|
||||||
|
.def(py::init<>());
|
||||||
|
py::class_<TN::ChainBaseB, TN::TrampolineB<>>(m, "ChainBaseB")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def("resultB", &TN::ChainBaseB::resultB);
|
||||||
|
py::class_<TN::ChainChildB, TN::ChainBaseB, TN::TrampolineB<TN::ChainChildB>>(m, "ChainChildB")
|
||||||
|
.def(py::init<>());
|
||||||
|
py::class_<TN::Joined, TN::ChainChildA, TN::ChainChildB, TN::TrampolineJoined<>>(m, "Joined")
|
||||||
|
.def(py::init<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Needed for MSVC linker
|
||||||
|
namespace TrampolineNesting {
|
||||||
|
int ChainBaseA::resultA() { return 0; }
|
||||||
|
std::string ChainBaseB::resultB() { return ""; }
|
||||||
|
} // namespace TrampolineNesting
|
||||||
|
@ -493,3 +493,14 @@ def test_python_inherit_from_mi():
|
|||||||
assert o.g == 7
|
assert o.g == 7
|
||||||
|
|
||||||
assert o.get_g_g() == 7
|
assert o.get_g_g() == 7
|
||||||
|
|
||||||
|
|
||||||
|
def test_trampoline_nesting():
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
m.ChainBaseA().resultA()
|
||||||
|
assert m.ChainChildA().resultA() == 1
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
m.ChainBaseB().resultB()
|
||||||
|
assert m.ChainChildB().resultB() == "A"
|
||||||
|
assert m.Joined().resultA() == 1
|
||||||
|
assert m.Joined().resultB() == "A"
|
||||||
|
@ -24,7 +24,7 @@ def test_dtypes():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture
|
||||||
def arr():
|
def arr():
|
||||||
return np.array([[1, 2, 3], [4, 5, 6]], "=u2")
|
return np.array([[1, 2, 3], [4, 5, 6]], "=u2")
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
// This also deliberately doesn't use the below StringList type alias to test
|
// This also deliberately doesn't use the below StringList type alias to test
|
||||||
// that MAKE_OPAQUE can handle a type containing a `,`. (The `std::allocator`
|
// that MAKE_OPAQUE can handle a type containing a `,`. (The `std::allocator`
|
||||||
// bit is just the default `std::vector` allocator).
|
// bit is just the default `std::vector` allocator).
|
||||||
PYBIND11_MAKE_OPAQUE(std::vector<std::string, std::allocator<std::string>>);
|
PYBIND11_MAKE_OPAQUE(std::vector<std::string, std::allocator<std::string>>)
|
||||||
|
|
||||||
using StringList = std::vector<std::string, std::allocator<std::string>>;
|
using StringList = std::vector<std::string, std::allocator<std::string>>;
|
||||||
|
|
||||||
|
@ -13,6 +13,14 @@
|
|||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
//__has_include has been part of C++17, no need to check it
|
||||||
|
#if defined(PYBIND11_CPP20) && __has_include(<ranges>)
|
||||||
|
# if !defined(PYBIND11_COMPILER_CLANG) || __clang_major__ >= 16 // llvm/llvm-project#52696
|
||||||
|
# define PYBIND11_TEST_PYTYPES_HAS_RANGES
|
||||||
|
# include <ranges>
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace external {
|
namespace external {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
bool check(PyObject *o) { return PyFloat_Check(o) != 0; }
|
bool check(PyObject *o) { return PyFloat_Check(o) != 0; }
|
||||||
@ -923,4 +931,59 @@ TEST_SUBMODULE(pytypes, m) {
|
|||||||
#else
|
#else
|
||||||
m.attr("defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL") = false;
|
m.attr("defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL") = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(PYBIND11_TEST_PYTYPES_HAS_RANGES)
|
||||||
|
|
||||||
|
// test_tuple_ranges
|
||||||
|
m.def("tuple_iterator_default_initialization", []() {
|
||||||
|
using TupleIterator = decltype(std::declval<py::tuple>().begin());
|
||||||
|
static_assert(std::random_access_iterator<TupleIterator>);
|
||||||
|
return TupleIterator{} == TupleIterator{};
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("transform_tuple_plus_one", [](py::tuple &tpl) {
|
||||||
|
py::list ret{};
|
||||||
|
for (auto it : tpl | std::views::transform([](auto &o) { return py::cast<int>(o) + 1; })) {
|
||||||
|
ret.append(py::int_(it));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
|
||||||
|
// test_list_ranges
|
||||||
|
m.def("list_iterator_default_initialization", []() {
|
||||||
|
using ListIterator = decltype(std::declval<py::list>().begin());
|
||||||
|
static_assert(std::random_access_iterator<ListIterator>);
|
||||||
|
return ListIterator{} == ListIterator{};
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("transform_list_plus_one", [](py::list &lst) {
|
||||||
|
py::list ret{};
|
||||||
|
for (auto it : lst | std::views::transform([](auto &o) { return py::cast<int>(o) + 1; })) {
|
||||||
|
ret.append(py::int_(it));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
|
||||||
|
// test_dict_ranges
|
||||||
|
m.def("dict_iterator_default_initialization", []() {
|
||||||
|
using DictIterator = decltype(std::declval<py::dict>().begin());
|
||||||
|
static_assert(std::forward_iterator<DictIterator>);
|
||||||
|
return DictIterator{} == DictIterator{};
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("transform_dict_plus_one", [](py::dict &dct) {
|
||||||
|
py::list ret{};
|
||||||
|
for (auto it : dct | std::views::transform([](auto &o) {
|
||||||
|
return std::pair{py::cast<int>(o.first) + 1,
|
||||||
|
py::cast<int>(o.second) + 1};
|
||||||
|
})) {
|
||||||
|
ret.append(py::make_tuple(py::int_(it.first), py::int_(it.second)));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
|
||||||
|
m.attr("defined_PYBIND11_TEST_PYTYPES_HAS_RANGES") = true;
|
||||||
|
#else
|
||||||
|
m.attr("defined_PYBIND11_TEST_PYTYPES_HAS_RANGES") = false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -1026,7 +1026,7 @@ def test_optional_object_annotations(doc):
|
|||||||
|
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
not m.defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL,
|
not m.defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL,
|
||||||
reason="C++20 feature not available.",
|
reason="C++20 non-type template args feature not available.",
|
||||||
)
|
)
|
||||||
def test_literal(doc):
|
def test_literal(doc):
|
||||||
assert (
|
assert (
|
||||||
@ -1037,7 +1037,7 @@ def test_literal(doc):
|
|||||||
|
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
not m.defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL,
|
not m.defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL,
|
||||||
reason="C++20 feature not available.",
|
reason="C++20 non-type template args feature not available.",
|
||||||
)
|
)
|
||||||
def test_typevar(doc):
|
def test_typevar(doc):
|
||||||
assert (
|
assert (
|
||||||
@ -1048,3 +1048,45 @@ def test_typevar(doc):
|
|||||||
assert doc(m.annotate_listT_to_T) == "annotate_listT_to_T(arg0: list[T]) -> T"
|
assert doc(m.annotate_listT_to_T) == "annotate_listT_to_T(arg0: list[T]) -> T"
|
||||||
|
|
||||||
assert doc(m.annotate_object_to_T) == "annotate_object_to_T(arg0: object) -> T"
|
assert doc(m.annotate_object_to_T) == "annotate_object_to_T(arg0: object) -> T"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
not m.defined_PYBIND11_TEST_PYTYPES_HAS_RANGES,
|
||||||
|
reason="<ranges> not available.",
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("tested_tuple", "expected"),
|
||||||
|
[((1,), [2]), ((3, 4), [4, 5]), ((7, 8, 9), [8, 9, 10])],
|
||||||
|
)
|
||||||
|
def test_tuple_ranges(tested_tuple, expected):
|
||||||
|
assert m.tuple_iterator_default_initialization()
|
||||||
|
assert m.transform_tuple_plus_one(tested_tuple) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
not m.defined_PYBIND11_TEST_PYTYPES_HAS_RANGES,
|
||||||
|
reason="<ranges> not available.",
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("tested_list", "expected"), [([1], [2]), ([3, 4], [4, 5]), ([7, 8, 9], [8, 9, 10])]
|
||||||
|
)
|
||||||
|
def test_list_ranges(tested_list, expected):
|
||||||
|
assert m.list_iterator_default_initialization()
|
||||||
|
assert m.transform_list_plus_one(tested_list) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
not m.defined_PYBIND11_TEST_PYTYPES_HAS_RANGES,
|
||||||
|
reason="<ranges> not available.",
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("tested_dict", "expected"),
|
||||||
|
[
|
||||||
|
({1: 2}, [(2, 3)]),
|
||||||
|
({3: 4, 5: 6}, [(4, 5), (6, 7)]),
|
||||||
|
({7: 8, 9: 10, 11: 12}, [(8, 9), (10, 11), (12, 13)]),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_dict_ranges(tested_dict, expected):
|
||||||
|
assert m.dict_iterator_default_initialization()
|
||||||
|
assert m.transform_dict_plus_one(tested_dict) == expected
|
||||||
|
@ -86,8 +86,8 @@ private:
|
|||||||
};
|
};
|
||||||
using NonCopyableIntPair = std::pair<NonCopyableInt, NonCopyableInt>;
|
using NonCopyableIntPair = std::pair<NonCopyableInt, NonCopyableInt>;
|
||||||
|
|
||||||
PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableInt>);
|
PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableInt>)
|
||||||
PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableIntPair>);
|
PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableIntPair>)
|
||||||
|
|
||||||
template <typename PythonType>
|
template <typename PythonType>
|
||||||
py::list test_random_access_iterator(PythonType x) {
|
py::list test_random_access_iterator(PythonType x) {
|
||||||
|
@ -11,6 +11,9 @@
|
|||||||
#include "object.h"
|
#include "object.h"
|
||||||
#include "pybind11_tests.h"
|
#include "pybind11_tests.h"
|
||||||
|
|
||||||
|
// This breaks on PYBIND11_DECLARE_HOLDER_TYPE
|
||||||
|
PYBIND11_WARNING_DISABLE_GCC("-Wpedantic")
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// This is just a wrapper around unique_ptr, but with extra fields to deliberately bloat up the
|
// This is just a wrapper around unique_ptr, but with extra fields to deliberately bloat up the
|
||||||
@ -279,13 +282,13 @@ struct holder_helper<ref<T>> {
|
|||||||
} // namespace PYBIND11_NAMESPACE
|
} // namespace PYBIND11_NAMESPACE
|
||||||
|
|
||||||
// Make pybind aware of the ref-counted wrapper type (s):
|
// Make pybind aware of the ref-counted wrapper type (s):
|
||||||
PYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>, true);
|
PYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>, true)
|
||||||
// The following is not required anymore for std::shared_ptr, but it should compile without error:
|
// The following is not required anymore for std::shared_ptr, but it should compile without error:
|
||||||
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>);
|
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>)
|
||||||
PYBIND11_DECLARE_HOLDER_TYPE(T, huge_unique_ptr<T>);
|
PYBIND11_DECLARE_HOLDER_TYPE(T, huge_unique_ptr<T>)
|
||||||
PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr<T>);
|
PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr<T>)
|
||||||
PYBIND11_DECLARE_HOLDER_TYPE(T, shared_ptr_with_addressof_operator<T>);
|
PYBIND11_DECLARE_HOLDER_TYPE(T, shared_ptr_with_addressof_operator<T>)
|
||||||
PYBIND11_DECLARE_HOLDER_TYPE(T, unique_ptr_with_addressof_operator<T>);
|
PYBIND11_DECLARE_HOLDER_TYPE(T, unique_ptr_with_addressof_operator<T>)
|
||||||
|
|
||||||
TEST_SUBMODULE(smart_ptr, m) {
|
TEST_SUBMODULE(smart_ptr, m) {
|
||||||
// Please do not interleave `struct` and `class` definitions with bindings code,
|
// Please do not interleave `struct` and `class` definitions with bindings code,
|
||||||
|
@ -59,7 +59,7 @@ struct visit_helper<boost::variant> {
|
|||||||
} // namespace PYBIND11_NAMESPACE
|
} // namespace PYBIND11_NAMESPACE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
PYBIND11_MAKE_OPAQUE(std::vector<std::string, std::allocator<std::string>>);
|
PYBIND11_MAKE_OPAQUE(std::vector<std::string, std::allocator<std::string>>)
|
||||||
|
|
||||||
/// Issue #528: templated constructor
|
/// Issue #528: templated constructor
|
||||||
struct TplCtorClass {
|
struct TplCtorClass {
|
||||||
@ -167,6 +167,14 @@ struct type_caster<ReferenceSensitiveOptional<T>>
|
|||||||
} // namespace detail
|
} // namespace detail
|
||||||
} // namespace PYBIND11_NAMESPACE
|
} // namespace PYBIND11_NAMESPACE
|
||||||
|
|
||||||
|
int pass_std_vector_int(const std::vector<int> &v) {
|
||||||
|
int zum = 100;
|
||||||
|
for (const int i : v) {
|
||||||
|
zum += 2 * i;
|
||||||
|
}
|
||||||
|
return zum;
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUBMODULE(stl, m) {
|
TEST_SUBMODULE(stl, m) {
|
||||||
// test_vector
|
// test_vector
|
||||||
m.def("cast_vector", []() { return std::vector<int>{1}; });
|
m.def("cast_vector", []() { return std::vector<int>{1}; });
|
||||||
@ -193,6 +201,23 @@ TEST_SUBMODULE(stl, m) {
|
|||||||
m.def("cast_array", []() { return std::array<int, 2>{{1, 2}}; });
|
m.def("cast_array", []() { return std::array<int, 2>{{1, 2}}; });
|
||||||
m.def("load_array", [](const std::array<int, 2> &a) { return a[0] == 1 && a[1] == 2; });
|
m.def("load_array", [](const std::array<int, 2> &a) { return a[0] == 1 && a[1] == 2; });
|
||||||
|
|
||||||
|
struct NoDefaultCtor {
|
||||||
|
explicit constexpr NoDefaultCtor(int val) : val{val} {}
|
||||||
|
int val;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NoDefaultCtorArray {
|
||||||
|
explicit constexpr NoDefaultCtorArray(int i)
|
||||||
|
: arr{{NoDefaultCtor(10 + i), NoDefaultCtor(20 + i)}} {}
|
||||||
|
std::array<NoDefaultCtor, 2> arr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// test_array_no_default_ctor
|
||||||
|
py::class_<NoDefaultCtor>(m, "NoDefaultCtor").def_readonly("val", &NoDefaultCtor::val);
|
||||||
|
py::class_<NoDefaultCtorArray>(m, "NoDefaultCtorArray")
|
||||||
|
.def(py::init<int>())
|
||||||
|
.def_readwrite("arr", &NoDefaultCtorArray::arr);
|
||||||
|
|
||||||
// test_valarray
|
// test_valarray
|
||||||
m.def("cast_valarray", []() { return std::valarray<int>{1, 4, 9}; });
|
m.def("cast_valarray", []() { return std::valarray<int>{1, 4, 9}; });
|
||||||
m.def("load_valarray", [](const std::valarray<int> &v) {
|
m.def("load_valarray", [](const std::valarray<int> &v) {
|
||||||
@ -546,4 +571,30 @@ TEST_SUBMODULE(stl, m) {
|
|||||||
[]() { return new std::vector<bool>(4513); },
|
[]() { return new std::vector<bool>(4513); },
|
||||||
// Without explicitly specifying `take_ownership`, this function leaks.
|
// Without explicitly specifying `take_ownership`, this function leaks.
|
||||||
py::return_value_policy::take_ownership);
|
py::return_value_policy::take_ownership);
|
||||||
|
|
||||||
|
m.def("pass_std_vector_int", pass_std_vector_int);
|
||||||
|
m.def("pass_std_vector_pair_int", [](const std::vector<std::pair<int, int>> &v) {
|
||||||
|
int zum = 0;
|
||||||
|
for (const auto &ij : v) {
|
||||||
|
zum += ij.first * 100 + ij.second;
|
||||||
|
}
|
||||||
|
return zum;
|
||||||
|
});
|
||||||
|
m.def("pass_std_array_int_2", [](const std::array<int, 2> &a) {
|
||||||
|
return pass_std_vector_int(std::vector<int>(a.begin(), a.end())) + 1;
|
||||||
|
});
|
||||||
|
m.def("pass_std_set_int", [](const std::set<int> &s) {
|
||||||
|
int zum = 200;
|
||||||
|
for (const int i : s) {
|
||||||
|
zum += 3 * i;
|
||||||
|
}
|
||||||
|
return zum;
|
||||||
|
});
|
||||||
|
m.def("pass_std_map_int", [](const std::map<int, int> &m) {
|
||||||
|
int zum = 500;
|
||||||
|
for (const auto &p : m) {
|
||||||
|
zum += p.first * 1000 + p.second;
|
||||||
|
}
|
||||||
|
return zum;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,13 @@ def test_array(doc):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_array_no_default_ctor():
|
||||||
|
lst = m.NoDefaultCtorArray(3)
|
||||||
|
assert [e.val for e in lst.arr] == [13, 23]
|
||||||
|
lst.arr = m.NoDefaultCtorArray(4).arr
|
||||||
|
assert [e.val for e in lst.arr] == [14, 24]
|
||||||
|
|
||||||
|
|
||||||
def test_valarray(doc):
|
def test_valarray(doc):
|
||||||
"""std::valarray <-> list"""
|
"""std::valarray <-> list"""
|
||||||
lst = m.cast_valarray()
|
lst = m.cast_valarray()
|
||||||
@ -381,3 +388,129 @@ def test_return_vector_bool_raw_ptr():
|
|||||||
v = m.return_vector_bool_raw_ptr()
|
v = m.return_vector_bool_raw_ptr()
|
||||||
assert isinstance(v, list)
|
assert isinstance(v, list)
|
||||||
assert len(v) == 4513
|
assert len(v) == 4513
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("fn", "offset"), [(m.pass_std_vector_int, 0), (m.pass_std_array_int_2, 1)]
|
||||||
|
)
|
||||||
|
def test_pass_std_vector_int(fn, offset):
|
||||||
|
assert fn([7, 13]) == 140 + offset
|
||||||
|
assert fn({6, 2}) == 116 + offset
|
||||||
|
assert fn({"x": 8, "y": 11}.values()) == 138 + offset
|
||||||
|
assert fn({3: None, 9: None}.keys()) == 124 + offset
|
||||||
|
assert fn(i for i in [4, 17]) == 142 + offset
|
||||||
|
assert fn(map(lambda i: i * 3, [8, 7])) == 190 + offset # noqa: C417
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
fn({"x": 0, "y": 1})
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
fn({})
|
||||||
|
|
||||||
|
|
||||||
|
def test_pass_std_vector_pair_int():
|
||||||
|
fn = m.pass_std_vector_pair_int
|
||||||
|
assert fn({1: 2, 3: 4}.items()) == 406
|
||||||
|
assert fn(zip([5, 17], [13, 9])) == 2222
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_caster_fully_consumes_generator_object():
|
||||||
|
def gen_invalid():
|
||||||
|
yield from [1, 2.0, 3]
|
||||||
|
|
||||||
|
gen_obj = gen_invalid()
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
m.pass_std_vector_int(gen_obj)
|
||||||
|
assert not tuple(gen_obj)
|
||||||
|
|
||||||
|
|
||||||
|
def test_pass_std_set_int():
|
||||||
|
fn = m.pass_std_set_int
|
||||||
|
assert fn({3, 15}) == 254
|
||||||
|
assert fn({5: None, 12: None}.keys()) == 251
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
fn([])
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
fn({})
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
fn({}.values())
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
fn(i for i in [])
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_caster_dict_keys_failure():
|
||||||
|
dict_keys = {1: None, 2.0: None, 3: None}.keys()
|
||||||
|
# The asserts does not really exercise anything in pybind11, but if one of
|
||||||
|
# them fails in some future version of Python, the set_caster load
|
||||||
|
# implementation may need to be revisited.
|
||||||
|
assert tuple(dict_keys) == (1, 2.0, 3)
|
||||||
|
assert tuple(dict_keys) == (1, 2.0, 3)
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
m.pass_std_set_int(dict_keys)
|
||||||
|
assert tuple(dict_keys) == (1, 2.0, 3)
|
||||||
|
|
||||||
|
|
||||||
|
class FakePyMappingMissingItems:
|
||||||
|
def __getitem__(self, _):
|
||||||
|
raise RuntimeError("Not expected to be called.")
|
||||||
|
|
||||||
|
|
||||||
|
class FakePyMappingWithItems(FakePyMappingMissingItems):
|
||||||
|
def items(self):
|
||||||
|
return ((1, 3), (2, 4))
|
||||||
|
|
||||||
|
|
||||||
|
class FakePyMappingBadItems(FakePyMappingMissingItems):
|
||||||
|
def items(self):
|
||||||
|
return ((1, 2), (3, "x"))
|
||||||
|
|
||||||
|
|
||||||
|
class FakePyMappingItemsNotCallable(FakePyMappingMissingItems):
|
||||||
|
@property
|
||||||
|
def items(self):
|
||||||
|
return ((1, 2), (3, 4))
|
||||||
|
|
||||||
|
|
||||||
|
class FakePyMappingItemsWithArg(FakePyMappingMissingItems):
|
||||||
|
def items(self, _):
|
||||||
|
return ((1, 2), (3, 4))
|
||||||
|
|
||||||
|
|
||||||
|
class FakePyMappingGenObj(FakePyMappingMissingItems):
|
||||||
|
def __init__(self, gen_obj):
|
||||||
|
super().__init__()
|
||||||
|
self.gen_obj = gen_obj
|
||||||
|
|
||||||
|
def items(self):
|
||||||
|
yield from self.gen_obj
|
||||||
|
|
||||||
|
|
||||||
|
def test_pass_std_map_int():
|
||||||
|
fn = m.pass_std_map_int
|
||||||
|
assert fn({1: 2, 3: 4}) == 4506
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
fn([])
|
||||||
|
assert fn(FakePyMappingWithItems()) == 3507
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
fn(FakePyMappingMissingItems())
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
fn(FakePyMappingBadItems())
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
fn(FakePyMappingItemsNotCallable())
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
fn(FakePyMappingItemsWithArg())
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("items", "expected_exception"),
|
||||||
|
[
|
||||||
|
(((1, 2), (3, "x"), (4, 5)), TypeError),
|
||||||
|
(((1, 2), (3, 4, 5), (6, 7)), ValueError),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_map_caster_fully_consumes_generator_object(items, expected_exception):
|
||||||
|
def gen_invalid():
|
||||||
|
yield from items
|
||||||
|
|
||||||
|
gen_obj = gen_invalid()
|
||||||
|
with pytest.raises(expected_exception):
|
||||||
|
m.pass_std_map_int(FakePyMappingGenObj(gen_obj))
|
||||||
|
assert not tuple(gen_obj)
|
||||||
|
@ -145,4 +145,4 @@ TEST_SUBMODULE(tagbased_polymorphic, m) {
|
|||||||
.def(py::init<std::string>())
|
.def(py::init<std::string>())
|
||||||
.def("purr", &Panther::purr);
|
.def("purr", &Panther::purr);
|
||||||
m.def("create_zoo", &create_zoo);
|
m.def("create_zoo", &create_zoo);
|
||||||
};
|
}
|
||||||
|
46
tests/test_type_caster_std_function_specializations.cpp
Normal file
46
tests/test_type_caster_std_function_specializations.cpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#include <pybind11/functional.h>
|
||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
|
||||||
|
#include "pybind11_tests.h"
|
||||||
|
|
||||||
|
namespace py = pybind11;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct SpecialReturn {
|
||||||
|
int value = 99;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace pybind11 {
|
||||||
|
namespace detail {
|
||||||
|
namespace type_caster_std_function_specializations {
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
struct func_wrapper<SpecialReturn, Args...> : func_wrapper_base {
|
||||||
|
using func_wrapper_base::func_wrapper_base;
|
||||||
|
SpecialReturn operator()(Args... args) const {
|
||||||
|
gil_scoped_acquire acq;
|
||||||
|
SpecialReturn result;
|
||||||
|
try {
|
||||||
|
result = hfunc.f(std::forward<Args>(args)...).template cast<SpecialReturn>();
|
||||||
|
} catch (error_already_set &) {
|
||||||
|
result.value += 1;
|
||||||
|
}
|
||||||
|
result.value += 100;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace type_caster_std_function_specializations
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace pybind11
|
||||||
|
|
||||||
|
TEST_SUBMODULE(type_caster_std_function_specializations, m) {
|
||||||
|
py::class_<SpecialReturn>(m, "SpecialReturn")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def_readwrite("value", &SpecialReturn::value);
|
||||||
|
m.def("call_callback_with_special_return",
|
||||||
|
[](const std::function<SpecialReturn()> &func) { return func(); });
|
||||||
|
}
|
15
tests/test_type_caster_std_function_specializations.py
Normal file
15
tests/test_type_caster_std_function_specializations.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pybind11_tests import type_caster_std_function_specializations as m
|
||||||
|
|
||||||
|
|
||||||
|
def test_callback_with_special_return():
|
||||||
|
def return_special():
|
||||||
|
return m.SpecialReturn()
|
||||||
|
|
||||||
|
def raise_exception():
|
||||||
|
raise ValueError("called raise_exception.")
|
||||||
|
|
||||||
|
assert return_special().value == 99
|
||||||
|
assert m.call_callback_with_special_return(return_special).value == 199
|
||||||
|
assert m.call_callback_with_special_return(raise_exception).value == 200
|
@ -589,4 +589,4 @@ void initialize_inherited_virtuals(py::module_ &m) {
|
|||||||
// Fix issue #1454 (crash when acquiring/releasing GIL on another thread in Python 2.7)
|
// Fix issue #1454 (crash when acquiring/releasing GIL on another thread in Python 2.7)
|
||||||
m.def("test_gil", &test_gil);
|
m.def("test_gil", &test_gil);
|
||||||
m.def("test_gil_from_thread", &test_gil_from_thread);
|
m.def("test_gil_from_thread", &test_gil_from_thread);
|
||||||
};
|
}
|
||||||
|
46
tests/test_warnings.cpp
Normal file
46
tests/test_warnings.cpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
tests/test_warnings.cpp -- usage of warnings::warn() and warnings categories.
|
||||||
|
|
||||||
|
Copyright (c) 2024 Jan Iwaszkiewicz <jiwaszkiewicz6@gmail.com>
|
||||||
|
|
||||||
|
All rights reserved. Use of this source code is governed by a
|
||||||
|
BSD-style license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <pybind11/warnings.h>
|
||||||
|
|
||||||
|
#include "pybind11_tests.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
TEST_SUBMODULE(warnings_, m) {
|
||||||
|
|
||||||
|
// Test warning mechanism base
|
||||||
|
m.def("warn_and_return_value", []() {
|
||||||
|
std::string message = "This is simple warning";
|
||||||
|
py::warnings::warn(message.c_str(), PyExc_Warning);
|
||||||
|
return 21;
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("warn_with_default_category", []() { py::warnings::warn("This is RuntimeWarning"); });
|
||||||
|
|
||||||
|
m.def("warn_with_different_category",
|
||||||
|
[]() { py::warnings::warn("This is FutureWarning", PyExc_FutureWarning); });
|
||||||
|
|
||||||
|
m.def("warn_with_invalid_category",
|
||||||
|
[]() { py::warnings::warn("Invalid category", PyExc_Exception); });
|
||||||
|
|
||||||
|
// Test custom warnings
|
||||||
|
PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> ex_storage;
|
||||||
|
ex_storage.call_once_and_store_result([&]() {
|
||||||
|
return py::warnings::new_warning_type(m, "CustomWarning", PyExc_DeprecationWarning);
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("warn_with_custom_type", []() {
|
||||||
|
py::warnings::warn("This is CustomWarning", ex_storage.get_stored());
|
||||||
|
return 37;
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("register_duplicate_warning",
|
||||||
|
[m]() { py::warnings::new_warning_type(m, "CustomWarning", PyExc_RuntimeWarning); });
|
||||||
|
}
|
68
tests/test_warnings.py
Normal file
68
tests/test_warnings.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import pybind11_tests # noqa: F401
|
||||||
|
from pybind11_tests import warnings_ as m
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("expected_category", "expected_message", "expected_value", "module_function"),
|
||||||
|
[
|
||||||
|
(Warning, "This is simple warning", 21, m.warn_and_return_value),
|
||||||
|
(RuntimeWarning, "This is RuntimeWarning", None, m.warn_with_default_category),
|
||||||
|
(FutureWarning, "This is FutureWarning", None, m.warn_with_different_category),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_warning_simple(
|
||||||
|
expected_category, expected_message, expected_value, module_function
|
||||||
|
):
|
||||||
|
with pytest.warns(Warning) as excinfo:
|
||||||
|
value = module_function()
|
||||||
|
|
||||||
|
assert issubclass(excinfo[0].category, expected_category)
|
||||||
|
assert str(excinfo[0].message) == expected_message
|
||||||
|
assert value == expected_value
|
||||||
|
|
||||||
|
|
||||||
|
def test_warning_wrong_subclass_fail():
|
||||||
|
with pytest.raises(Exception) as excinfo:
|
||||||
|
m.warn_with_invalid_category()
|
||||||
|
|
||||||
|
assert issubclass(excinfo.type, RuntimeError)
|
||||||
|
assert (
|
||||||
|
str(excinfo.value)
|
||||||
|
== "pybind11::warnings::warn(): cannot raise warning, category must be a subclass of PyExc_Warning!"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_warning_double_register_fail():
|
||||||
|
with pytest.raises(Exception) as excinfo:
|
||||||
|
m.register_duplicate_warning()
|
||||||
|
|
||||||
|
assert issubclass(excinfo.type, RuntimeError)
|
||||||
|
assert (
|
||||||
|
str(excinfo.value)
|
||||||
|
== 'pybind11::warnings::new_warning_type(): an attribute with name "CustomWarning" exists already.'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_warning_register():
|
||||||
|
assert m.CustomWarning is not None
|
||||||
|
|
||||||
|
with pytest.warns(m.CustomWarning) as excinfo:
|
||||||
|
warnings.warn("This is warning from Python!", m.CustomWarning, stacklevel=1)
|
||||||
|
|
||||||
|
assert issubclass(excinfo[0].category, DeprecationWarning)
|
||||||
|
assert str(excinfo[0].message) == "This is warning from Python!"
|
||||||
|
|
||||||
|
|
||||||
|
def test_warning_custom():
|
||||||
|
with pytest.warns(m.CustomWarning) as excinfo:
|
||||||
|
value = m.warn_with_custom_type()
|
||||||
|
|
||||||
|
assert issubclass(excinfo[0].category, DeprecationWarning)
|
||||||
|
assert str(excinfo[0].message) == "This is CustomWarning"
|
||||||
|
assert value == 37
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Adds the following targets::
|
Adds the following targets::
|
||||||
|
|
||||||
pybind11::pybind11 - link to headers and pybind11
|
pybind11::pybind11 - link to Python headers and pybind11::headers
|
||||||
pybind11::module - Adds module links
|
pybind11::module - Adds module links
|
||||||
pybind11::embed - Adds embed links
|
pybind11::embed - Adds embed links
|
||||||
pybind11::lto - Link time optimizations (only if CMAKE_INTERPROCEDURAL_OPTIMIZATION is not set)
|
pybind11::lto - Link time optimizations (only if CMAKE_INTERPROCEDURAL_OPTIMIZATION is not set)
|
||||||
@ -18,11 +18,7 @@ Adds the following functions::
|
|||||||
|
|
||||||
#]======================================================]
|
#]======================================================]
|
||||||
|
|
||||||
# CMake 3.10 has an include_guard command, but we can't use that yet
|
include_guard(GLOBAL)
|
||||||
# include_guard(global) (pre-CMake 3.10)
|
|
||||||
if(TARGET pybind11::pybind11)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# If we are in subdirectory mode, all IMPORTED targets must be GLOBAL. If we
|
# If we are in subdirectory mode, all IMPORTED targets must be GLOBAL. If we
|
||||||
# are in CONFIG mode, they should be "normal" targets instead.
|
# are in CONFIG mode, they should be "normal" targets instead.
|
||||||
@ -75,26 +71,36 @@ set_property(
|
|||||||
APPEND
|
APPEND
|
||||||
PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11)
|
PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11)
|
||||||
|
|
||||||
|
# -------------- emscripten requires exceptions enabled -------------
|
||||||
|
# _pybind11_no_exceptions is a private mechanism to disable this addition.
|
||||||
|
# Please open an issue if you need to use it; it will be removed if no one
|
||||||
|
# needs it.
|
||||||
|
if(CMAKE_SYSTEM_NAME MATCHES Emscripten AND NOT _pybind11_no_exceptions)
|
||||||
|
if(is_config)
|
||||||
|
set(_tmp_config_target pybind11::pybind11_headers)
|
||||||
|
else()
|
||||||
|
set(_tmp_config_target pybind11_headers)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_property(
|
||||||
|
TARGET ${_tmp_config_target}
|
||||||
|
APPEND
|
||||||
|
PROPERTY INTERFACE_LINK_OPTIONS -fexceptions)
|
||||||
|
set_property(
|
||||||
|
TARGET ${_tmp_config_target}
|
||||||
|
APPEND
|
||||||
|
PROPERTY INTERFACE_COMPILE_OPTIONS -fexceptions)
|
||||||
|
unset(_tmp_config_target)
|
||||||
|
endif()
|
||||||
|
|
||||||
# --------------------------- link helper ---------------------------
|
# --------------------------- link helper ---------------------------
|
||||||
|
|
||||||
add_library(pybind11::python_link_helper IMPORTED INTERFACE ${optional_global})
|
add_library(pybind11::python_link_helper IMPORTED INTERFACE ${optional_global})
|
||||||
|
|
||||||
if(CMAKE_VERSION VERSION_LESS 3.13)
|
set_property(
|
||||||
# In CMake 3.11+, you can set INTERFACE properties via the normal methods, and
|
TARGET pybind11::python_link_helper
|
||||||
# this would be simpler.
|
APPEND
|
||||||
set_property(
|
PROPERTY INTERFACE_LINK_OPTIONS "$<$<PLATFORM_ID:Darwin>:LINKER:-undefined,dynamic_lookup>")
|
||||||
TARGET pybind11::python_link_helper
|
|
||||||
APPEND
|
|
||||||
PROPERTY INTERFACE_LINK_LIBRARIES "$<$<PLATFORM_ID:Darwin>:-undefined dynamic_lookup>")
|
|
||||||
else()
|
|
||||||
# link_options was added in 3.13+
|
|
||||||
# This is safer, because you are ensured the deduplication pass in CMake will not consider
|
|
||||||
# these separate and remove one but not the other.
|
|
||||||
set_property(
|
|
||||||
TARGET pybind11::python_link_helper
|
|
||||||
APPEND
|
|
||||||
PROPERTY INTERFACE_LINK_OPTIONS "$<$<PLATFORM_ID:Darwin>:LINKER:-undefined,dynamic_lookup>")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ------------------------ Windows extras -------------------------
|
# ------------------------ Windows extras -------------------------
|
||||||
|
|
||||||
@ -110,22 +116,14 @@ if(MSVC) # That's also clang-cl
|
|||||||
|
|
||||||
# /MP enables multithreaded builds (relevant when there are many files) for MSVC
|
# /MP enables multithreaded builds (relevant when there are many files) for MSVC
|
||||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # no Clang no Intel
|
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # no Clang no Intel
|
||||||
if(CMAKE_VERSION VERSION_LESS 3.11)
|
# Only set these options for C++ files. This is important so that, for
|
||||||
set_property(
|
# instance, projects that include other types of source files like CUDA
|
||||||
TARGET pybind11::windows_extras
|
# .cu files don't get these options propagated to nvcc since that would
|
||||||
APPEND
|
# cause the build to fail.
|
||||||
PROPERTY INTERFACE_COMPILE_OPTIONS $<$<NOT:$<CONFIG:Debug>>:/MP>)
|
set_property(
|
||||||
else()
|
TARGET pybind11::windows_extras
|
||||||
# Only set these options for C++ files. This is important so that, for
|
APPEND
|
||||||
# instance, projects that include other types of source files like CUDA
|
PROPERTY INTERFACE_COMPILE_OPTIONS $<$<NOT:$<CONFIG:Debug>>:$<$<COMPILE_LANGUAGE:CXX>:/MP>>)
|
||||||
# .cu files don't get these options propagated to nvcc since that would
|
|
||||||
# cause the build to fail.
|
|
||||||
set_property(
|
|
||||||
TARGET pybind11::windows_extras
|
|
||||||
APPEND
|
|
||||||
PROPERTY INTERFACE_COMPILE_OPTIONS
|
|
||||||
$<$<NOT:$<CONFIG:Debug>>:$<$<COMPILE_LANGUAGE:CXX>:/MP>>)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -329,7 +327,7 @@ function(_pybind11_generate_lto target prefer_thin_lto)
|
|||||||
|
|
||||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le" OR CMAKE_SYSTEM_PROCESSOR MATCHES "mips64")
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le" OR CMAKE_SYSTEM_PROCESSOR MATCHES "mips64")
|
||||||
# Do nothing
|
# Do nothing
|
||||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES emscripten)
|
elseif(CMAKE_SYSTEM_NAME MATCHES Emscripten)
|
||||||
# This compile is very costly when cross-compiling, so set this without checking
|
# This compile is very costly when cross-compiling, so set this without checking
|
||||||
set(PYBIND11_LTO_CXX_FLAGS "-flto${thin}${cxx_append}")
|
set(PYBIND11_LTO_CXX_FLAGS "-flto${thin}${cxx_append}")
|
||||||
set(PYBIND11_LTO_LINKER_FLAGS "-flto${thin}${linker_append}")
|
set(PYBIND11_LTO_LINKER_FLAGS "-flto${thin}${linker_append}")
|
||||||
@ -371,11 +369,7 @@ function(_pybind11_generate_lto target prefer_thin_lto)
|
|||||||
set(is_debug "$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>")
|
set(is_debug "$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>")
|
||||||
set(not_debug "$<NOT:${is_debug}>")
|
set(not_debug "$<NOT:${is_debug}>")
|
||||||
set(cxx_lang "$<COMPILE_LANGUAGE:CXX>")
|
set(cxx_lang "$<COMPILE_LANGUAGE:CXX>")
|
||||||
if(MSVC AND CMAKE_VERSION VERSION_LESS 3.11)
|
set(genex "$<AND:${not_debug},${cxx_lang}>")
|
||||||
set(genex "${not_debug}")
|
|
||||||
else()
|
|
||||||
set(genex "$<AND:${not_debug},${cxx_lang}>")
|
|
||||||
endif()
|
|
||||||
set_property(
|
set_property(
|
||||||
TARGET ${target}
|
TARGET ${target}
|
||||||
APPEND
|
APPEND
|
||||||
@ -390,17 +384,10 @@ function(_pybind11_generate_lto target prefer_thin_lto)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(PYBIND11_LTO_LINKER_FLAGS)
|
if(PYBIND11_LTO_LINKER_FLAGS)
|
||||||
if(CMAKE_VERSION VERSION_LESS 3.11)
|
set_property(
|
||||||
set_property(
|
TARGET ${target}
|
||||||
TARGET ${target}
|
APPEND
|
||||||
APPEND
|
PROPERTY INTERFACE_LINK_OPTIONS "$<${not_debug}:${PYBIND11_LTO_LINKER_FLAGS}>")
|
||||||
PROPERTY INTERFACE_LINK_LIBRARIES "$<${not_debug}:${PYBIND11_LTO_LINKER_FLAGS}>")
|
|
||||||
else()
|
|
||||||
set_property(
|
|
||||||
TARGET ${target}
|
|
||||||
APPEND
|
|
||||||
PROPERTY INTERFACE_LINK_OPTIONS "$<${not_debug}:${PYBIND11_LTO_LINKER_FLAGS}>")
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ you can either use the basic targets, or use the FindPython tools:
|
|||||||
|
|
||||||
# Python method:
|
# Python method:
|
||||||
Python_add_library(MyModule2 src2.cpp)
|
Python_add_library(MyModule2 src2.cpp)
|
||||||
target_link_libraries(MyModule2 pybind11::headers)
|
target_link_libraries(MyModule2 PUBLIC pybind11::headers)
|
||||||
set_target_properties(MyModule2 PROPERTIES
|
set_target_properties(MyModule2 PROPERTIES
|
||||||
INTERPROCEDURAL_OPTIMIZATION ON
|
INTERPROCEDURAL_OPTIMIZATION ON
|
||||||
CXX_VISIBILITY_PRESET ON
|
CXX_VISIBILITY_PRESET ON
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.15...3.30)
|
||||||
|
|
||||||
function(pybind11_guess_python_module_extension python)
|
function(pybind11_guess_python_module_extension python)
|
||||||
|
|
||||||
|
@ -5,10 +5,6 @@
|
|||||||
# All rights reserved. Use of this source code is governed by a
|
# All rights reserved. Use of this source code is governed by a
|
||||||
# BSD-style license that can be found in the LICENSE file.
|
# BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
if(CMAKE_VERSION VERSION_LESS 3.12)
|
|
||||||
message(FATAL_ERROR "You cannot use the new FindPython module with CMake < 3.12")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include_guard(DIRECTORY)
|
include_guard(DIRECTORY)
|
||||||
|
|
||||||
get_property(
|
get_property(
|
||||||
@ -236,7 +232,6 @@ if(TARGET ${_Python}::Python)
|
|||||||
PROPERTY INTERFACE_LINK_LIBRARIES ${_Python}::Python)
|
PROPERTY INTERFACE_LINK_LIBRARIES ${_Python}::Python)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# CMake 3.15+ has this
|
|
||||||
if(TARGET ${_Python}::Module)
|
if(TARGET ${_Python}::Module)
|
||||||
set_property(
|
set_property(
|
||||||
TARGET pybind11::module
|
TARGET pybind11::module
|
||||||
@ -279,10 +274,6 @@ function(pybind11_add_module target_name)
|
|||||||
target_link_libraries(${target_name} PRIVATE pybind11::embed)
|
target_link_libraries(${target_name} PRIVATE pybind11::embed)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MSVC)
|
|
||||||
target_link_libraries(${target_name} PRIVATE pybind11::windows_extras)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# -fvisibility=hidden is required to allow multiple modules compiled against
|
# -fvisibility=hidden is required to allow multiple modules compiled against
|
||||||
# different pybind versions to work properly, and for some features (e.g.
|
# different pybind versions to work properly, and for some features (e.g.
|
||||||
# py::module_local). We force it on everything inside the `pybind11`
|
# py::module_local). We force it on everything inside the `pybind11`
|
||||||
|
@ -5,13 +5,7 @@
|
|||||||
# All rights reserved. Use of this source code is governed by a
|
# All rights reserved. Use of this source code is governed by a
|
||||||
# BSD-style license that can be found in the LICENSE file.
|
# BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
# include_guard(global) (pre-CMake 3.10)
|
include_guard(GLOBAL)
|
||||||
if(TARGET pybind11::python_headers)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Built-in in CMake 3.5+
|
|
||||||
include(CMakeParseArguments)
|
|
||||||
|
|
||||||
if(pybind11_FIND_QUIETLY)
|
if(pybind11_FIND_QUIETLY)
|
||||||
set(_pybind11_quiet QUIET)
|
set(_pybind11_quiet QUIET)
|
||||||
@ -116,36 +110,19 @@ if(PYTHON_IS_DEBUG)
|
|||||||
PROPERTY INTERFACE_COMPILE_DEFINITIONS Py_DEBUG)
|
PROPERTY INTERFACE_COMPILE_DEFINITIONS Py_DEBUG)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# The <3.11 code here does not support release/debug builds at the same time, like on vcpkg
|
# The IMPORTED INTERFACE library here is to ensure that "debug" and "release" get processed outside
|
||||||
if(CMAKE_VERSION VERSION_LESS 3.11)
|
# of a generator expression - https://gitlab.kitware.com/cmake/cmake/-/issues/18424, as they are
|
||||||
set_property(
|
# target_link_library keywords rather than real libraries.
|
||||||
TARGET pybind11::module
|
add_library(pybind11::_ClassicPythonLibraries IMPORTED INTERFACE)
|
||||||
APPEND
|
target_link_libraries(pybind11::_ClassicPythonLibraries INTERFACE ${PYTHON_LIBRARIES})
|
||||||
PROPERTY
|
target_link_libraries(
|
||||||
INTERFACE_LINK_LIBRARIES
|
pybind11::module
|
||||||
pybind11::python_link_helper
|
INTERFACE
|
||||||
"$<$<OR:$<PLATFORM_ID:Windows>,$<PLATFORM_ID:Cygwin>>:$<BUILD_INTERFACE:${PYTHON_LIBRARIES}>>"
|
pybind11::python_link_helper
|
||||||
)
|
"$<$<OR:$<PLATFORM_ID:Windows>,$<PLATFORM_ID:Cygwin>>:pybind11::_ClassicPythonLibraries>")
|
||||||
|
|
||||||
set_property(
|
target_link_libraries(pybind11::embed INTERFACE pybind11::pybind11
|
||||||
TARGET pybind11::embed
|
pybind11::_ClassicPythonLibraries)
|
||||||
APPEND
|
|
||||||
PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11 $<BUILD_INTERFACE:${PYTHON_LIBRARIES}>)
|
|
||||||
else()
|
|
||||||
# The IMPORTED INTERFACE library here is to ensure that "debug" and "release" get processed outside
|
|
||||||
# of a generator expression - https://gitlab.kitware.com/cmake/cmake/-/issues/18424, as they are
|
|
||||||
# target_link_library keywords rather than real libraries.
|
|
||||||
add_library(pybind11::_ClassicPythonLibraries IMPORTED INTERFACE)
|
|
||||||
target_link_libraries(pybind11::_ClassicPythonLibraries INTERFACE ${PYTHON_LIBRARIES})
|
|
||||||
target_link_libraries(
|
|
||||||
pybind11::module
|
|
||||||
INTERFACE
|
|
||||||
pybind11::python_link_helper
|
|
||||||
"$<$<OR:$<PLATFORM_ID:Windows>,$<PLATFORM_ID:Cygwin>>:pybind11::_ClassicPythonLibraries>")
|
|
||||||
|
|
||||||
target_link_libraries(pybind11::embed INTERFACE pybind11::pybind11
|
|
||||||
pybind11::_ClassicPythonLibraries)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
function(pybind11_extension name)
|
function(pybind11_extension name)
|
||||||
# The prefix and extension are provided by FindPythonLibsNew.cmake
|
# The prefix and extension are provided by FindPythonLibsNew.cmake
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.15...3.30)
|
||||||
|
|
||||||
# Tests for pybind11_guess_python_module_extension
|
# Tests for pybind11_guess_python_module_extension
|
||||||
# Run using `cmake -P tools/test-pybind11GuessPythonExtSuffix.cmake`
|
# Run using `cmake -P tools/test-pybind11GuessPythonExtSuffix.cmake`
|
||||||
|
Loading…
Reference in New Issue
Block a user