From 3f522e5a92c2f082f3a95258054604dbfc0772e3 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 11 Aug 2024 09:19:51 -0700 Subject: [PATCH 01/39] `self.__cpp_transporter__()` proof of concept: Enable passing C++ pointers across extensions even if the `PYBIND11_INTERNALS_VERSION`s do not match. --- CMakeLists.txt | 1 + include/pybind11/detail/cpp_transporter.h | 71 +++++++++++++++++++ include/pybind11/detail/type_caster_base.h | 12 ++++ tests/CMakeLists.txt | 2 + tests/exo_planet.cpp | 8 +++ tests/extra_python_package/test_files.py | 1 + tests/test_cpp_transporter.cpp | 4 ++ tests/test_cpp_transporter.py | 37 ++++++++++ .../test_cpp_transporter_traveler_bindings.h | 36 ++++++++++ tests/test_cpp_transporter_traveler_type.h | 14 ++++ 10 files changed, 186 insertions(+) create mode 100644 include/pybind11/detail/cpp_transporter.h create mode 100644 tests/exo_planet.cpp create mode 100644 tests/test_cpp_transporter.cpp create mode 100644 tests/test_cpp_transporter.py create mode 100644 tests/test_cpp_transporter_traveler_bindings.h create mode 100644 tests/test_cpp_transporter_traveler_type.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e5b8c8f..b1396d7d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,6 +149,7 @@ endif() set(PYBIND11_HEADERS include/pybind11/detail/class.h include/pybind11/detail/common.h + include/pybind11/detail/cpp_transporter.h include/pybind11/detail/descr.h include/pybind11/detail/init.h include/pybind11/detail/internals.h diff --git a/include/pybind11/detail/cpp_transporter.h b/include/pybind11/detail/cpp_transporter.h new file mode 100644 index 00000000..d068a497 --- /dev/null +++ b/include/pybind11/detail/cpp_transporter.h @@ -0,0 +1,71 @@ +// Copyright (c) 2024 The pybind Community. + +#pragma once + +#include "../pytypes.h" +#include "common.h" +#include "typeid.h" + +#include + +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_transporter_method(PyObject *obj) { + if (PyType_Check(obj)) { + return object(); + } + PyTypeObject *type_obj = Py_TYPE(obj); + str attr_name("__cpp_transporter__"); + 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(method); +} + +inline void *try_raw_pointer_ephemeral_from_cpp_transporter(handle src, const char *typeid_name) { + object method = try_get_cpp_transporter_method(src.ptr()); + if (method) { + object cpp_transporter = method("cpp_abi_code", typeid_name, "raw_pointer_ephemeral"); + if (isinstance(cpp_transporter)) { + return reinterpret_borrow(cpp_transporter).get_pointer(); + } + } + return nullptr; +} + +#define PYBIND11_HAS_CPP_TRANSPORTER + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index 481b7c78..5ef9ca61 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -11,6 +11,7 @@ #include "../pytypes.h" #include "common.h" +#include "cpp_transporter.h" #include "descr.h" #include "internals.h" #include "typeid.h" @@ -610,6 +611,13 @@ class type_caster_generic { } return false; } + bool try_cpp_transporter(handle src) { + value = try_raw_pointer_ephemeral_from_cpp_transporter(src, cpptype->name()); + if (value != nullptr) { + return true; + } + return false; + } void check_holder_compat() {} PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) { @@ -741,6 +749,10 @@ class type_caster_generic { return true; } + if (convert && cpptype && this_.try_cpp_transporter(src)) { + return true; + } + return false; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5b7e3f80..fb67f29a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -157,6 +157,7 @@ set(PYBIND11_TEST_FILES test_stl_binders test_tagbased_polymorphic test_thread + test_cpp_transporter test_type_caster_pyobject_ptr test_type_caster_std_function_specializations test_union @@ -226,6 +227,7 @@ tests_extra_targets("test_exceptions.py;test_local_bindings.py;test_stl.py;test_ # And add additional targets for other tests. 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_cpp_transporter.py" "exo_planet") set(PYBIND11_EIGEN_REPO "https://gitlab.com/libeigen/eigen.git" diff --git a/tests/exo_planet.cpp b/tests/exo_planet.cpp new file mode 100644 index 00000000..e1fefda3 --- /dev/null +++ b/tests/exo_planet.cpp @@ -0,0 +1,8 @@ +#if defined(PYBIND11_INTERNALS_VERSION) +# undef PYBIND11_INTERNALS_VERSION +#endif +#define PYBIND11_INTERNALS_VERSION 900000001 + +#include "test_cpp_transporter_traveler_bindings.h" + +PYBIND11_MODULE(exo_planet, m) { pybind11_tests::test_cpp_transporter::wrap_traveler(m); } diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index aedbdf1c..4f737a26 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -53,6 +53,7 @@ detail_headers = { "include/pybind11/detail/class.h", "include/pybind11/detail/common.h", + "include/pybind11/detail/cpp_transporter.h", "include/pybind11/detail/descr.h", "include/pybind11/detail/init.h", "include/pybind11/detail/internals.h", diff --git a/tests/test_cpp_transporter.cpp b/tests/test_cpp_transporter.cpp new file mode 100644 index 00000000..d13e649e --- /dev/null +++ b/tests/test_cpp_transporter.cpp @@ -0,0 +1,4 @@ +#include "pybind11_tests.h" +#include "test_cpp_transporter_traveler_bindings.h" + +TEST_SUBMODULE(cpp_transporter, m) { pybind11_tests::test_cpp_transporter::wrap_traveler(m); } diff --git a/tests/test_cpp_transporter.py b/tests/test_cpp_transporter.py new file mode 100644 index 00000000..984b977d --- /dev/null +++ b/tests/test_cpp_transporter.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +import exo_planet + +from pybind11_tests import cpp_transporter as home_planet + + +def test_home_only(): + t_h = home_planet.Traveler("home") + assert t_h.luggage == "home" + assert home_planet.get_luggage(t_h) == "home" + + +def test_exo_only(): + t_e = exo_planet.Traveler("exo") + assert t_e.luggage == "exo" + assert exo_planet.get_luggage(t_e) == "exo" + + +def test_home_passed_to_exo(): + t_h = home_planet.Traveler("home") + assert exo_planet.get_luggage(t_h) == "home" + + +def test_exo_passed_to_home(): + t_e = exo_planet.Traveler("exo") + assert home_planet.get_luggage(t_e) == "exo" + + +def test_call_cpp_transporter(): + t_h = home_planet.Traveler("home") + assert ( + t_h.__cpp_transporter__( + "cpp_abi_code", "cpp_typeid_name", "raw_pointer_ephemeral" + ) + is not None + ) diff --git a/tests/test_cpp_transporter_traveler_bindings.h b/tests/test_cpp_transporter_traveler_bindings.h new file mode 100644 index 00000000..39cb3687 --- /dev/null +++ b/tests/test_cpp_transporter_traveler_bindings.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include "test_cpp_transporter_traveler_type.h" + +#include + +namespace pybind11_tests { +namespace test_cpp_transporter { + +namespace py = pybind11; + +inline void wrap_traveler(py::module_ m) { + py::class_(m, "Traveler") + .def(py::init()) + .def("__cpp_transporter__", + [](py::handle self, + py::str /*cpp_abi_code*/, + py::str /*cpp_typeid_name*/, + py::str pointer_kind) { + auto pointer_kind_cpp = pointer_kind.cast(); + if (pointer_kind_cpp != "raw_pointer_ephemeral") { + throw std::runtime_error("Unknown pointer_kind: \"" + pointer_kind_cpp + + "\""); + } + auto *self_cpp_ptr = py::cast(self); + return py::capsule(static_cast(self_cpp_ptr), typeid(Traveler).name()); + }) + .def_readwrite("luggage", &Traveler::luggage); + + m.def("get_luggage", [](const Traveler &person) { return person.luggage; }); +}; + +} // namespace test_cpp_transporter +} // namespace pybind11_tests diff --git a/tests/test_cpp_transporter_traveler_type.h b/tests/test_cpp_transporter_traveler_type.h new file mode 100644 index 00000000..8d40c917 --- /dev/null +++ b/tests/test_cpp_transporter_traveler_type.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace pybind11_tests { +namespace test_cpp_transporter { + +struct Traveler { + explicit Traveler(const std::string &luggage) : luggage(luggage) {} + std::string luggage; +}; + +} // namespace test_cpp_transporter +} // namespace pybind11_tests From 900bc26b475cb49c117a91ee07f2acabb88a4b9a Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 11 Aug 2024 13:07:10 -0700 Subject: [PATCH 02/39] Include cleanup (mainly to resolve PyPy build failures). --- include/pybind11/detail/cpp_transporter.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/pybind11/detail/cpp_transporter.h b/include/pybind11/detail/cpp_transporter.h index d068a497..8e845ca4 100644 --- a/include/pybind11/detail/cpp_transporter.h +++ b/include/pybind11/detail/cpp_transporter.h @@ -4,9 +4,7 @@ #include "../pytypes.h" #include "common.h" -#include "typeid.h" - -#include +#include "internals.h" PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) From 2fbe93026b5626940834df3eb66092f3dc7e4383 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 11 Aug 2024 13:30:08 -0700 Subject: [PATCH 03/39] Fix clang-tidy errors. --- tests/test_cpp_transporter_traveler_bindings.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_cpp_transporter_traveler_bindings.h b/tests/test_cpp_transporter_traveler_bindings.h index 39cb3687..462d48d4 100644 --- a/tests/test_cpp_transporter_traveler_bindings.h +++ b/tests/test_cpp_transporter_traveler_bindings.h @@ -16,9 +16,9 @@ inline void wrap_traveler(py::module_ m) { .def(py::init()) .def("__cpp_transporter__", [](py::handle self, - py::str /*cpp_abi_code*/, - py::str /*cpp_typeid_name*/, - py::str pointer_kind) { + const py::str & /*cpp_abi_code*/, + const py::str & /*cpp_typeid_name*/, + const py::str &pointer_kind) { auto pointer_kind_cpp = pointer_kind.cast(); if (pointer_kind_cpp != "raw_pointer_ephemeral") { throw std::runtime_error("Unknown pointer_kind: \"" + pointer_kind_cpp From 2dd6c7a227eb6b2bb8798fc31fc43c3822eba5e0 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 25 Aug 2024 14:28:34 -0700 Subject: [PATCH 04/39] Resolve `error: extra --- tests/test_cpp_transporter_traveler_bindings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cpp_transporter_traveler_bindings.h b/tests/test_cpp_transporter_traveler_bindings.h index 462d48d4..c125e58f 100644 --- a/tests/test_cpp_transporter_traveler_bindings.h +++ b/tests/test_cpp_transporter_traveler_bindings.h @@ -30,7 +30,7 @@ inline void wrap_traveler(py::module_ m) { .def_readwrite("luggage", &Traveler::luggage); m.def("get_luggage", [](const Traveler &person) { return person.luggage; }); -}; +} } // namespace test_cpp_transporter } // namespace pybind11_tests From 87ebc39e9831ca7849219afd7cc6bd52e0d5ef01 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 27 Aug 2024 17:02:17 -0700 Subject: [PATCH 05/39] factor out platform_abi_id.h from internals.h (no functional changes) --- include/pybind11/detail/internals.h | 64 ++-------------------- include/pybind11/detail/platform_abi_id.h | 66 +++++++++++++++++++++++ 2 files changed, 69 insertions(+), 61 deletions(-) create mode 100644 include/pybind11/detail/platform_abi_id.h diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 17e6df82..0051d34d 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -10,6 +10,7 @@ #pragma once #include "common.h" +#include "platform_abi_id.h" #if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT) # include @@ -264,72 +265,13 @@ struct type_info { bool module_local : 1; }; -/// On MSVC, debug and release builds are not ABI-compatible! -#if defined(_MSC_VER) && defined(_DEBUG) -# define PYBIND11_BUILD_TYPE "_debug" -#else -# define PYBIND11_BUILD_TYPE "" -#endif - -/// Let's assume that different compilers are ABI-incompatible. -/// A user can manually set this string if they know their -/// compiler is compatible. -#ifndef PYBIND11_COMPILER_TYPE -# if defined(_MSC_VER) -# define PYBIND11_COMPILER_TYPE "_msvc" -# elif defined(__INTEL_COMPILER) -# define PYBIND11_COMPILER_TYPE "_icc" -# elif defined(__clang__) -# define PYBIND11_COMPILER_TYPE "_clang" -# elif defined(__PGI) -# define PYBIND11_COMPILER_TYPE "_pgi" -# elif defined(__MINGW32__) -# define PYBIND11_COMPILER_TYPE "_mingw" -# elif defined(__CYGWIN__) -# define PYBIND11_COMPILER_TYPE "_gcc_cygwin" -# elif defined(__GNUC__) -# define PYBIND11_COMPILER_TYPE "_gcc" -# else -# define PYBIND11_COMPILER_TYPE "_unknown" -# endif -#endif - -/// Also standard libs -#ifndef PYBIND11_STDLIB -# if defined(_LIBCPP_VERSION) -# define PYBIND11_STDLIB "_libcpp" -# elif defined(__GLIBCXX__) || defined(__GLIBCPP__) -# define PYBIND11_STDLIB "_libstdcpp" -# else -# define PYBIND11_STDLIB "" -# endif -#endif - -/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility. -/// On MSVC, changes in _MSC_VER may indicate ABI incompatibility (#2898). -#ifndef PYBIND11_BUILD_ABI -# if defined(__GXX_ABI_VERSION) -# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION) -# elif defined(_MSC_VER) -# define PYBIND11_BUILD_ABI "_mscver" PYBIND11_TOSTRING(_MSC_VER) -# else -# define PYBIND11_BUILD_ABI "" -# endif -#endif - -#ifndef PYBIND11_INTERNALS_KIND -# define PYBIND11_INTERNALS_KIND "" -#endif - #define PYBIND11_INTERNALS_ID \ "__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \ - PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB \ - PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" + PYBIND11_PLATFORM_ABI_ID "__" #define PYBIND11_MODULE_LOCAL_ID \ "__pybind11_module_local_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \ - PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB \ - PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" + PYBIND11_PLATFORM_ABI_ID "__" /// Each module locally stores a pointer to the `internals` data. The data /// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`. diff --git a/include/pybind11/detail/platform_abi_id.h b/include/pybind11/detail/platform_abi_id.h new file mode 100644 index 00000000..88cb3495 --- /dev/null +++ b/include/pybind11/detail/platform_abi_id.h @@ -0,0 +1,66 @@ +// Copyright (c) 2024 The pybind Community. + +#pragma once + +#include "common.h" + +/// On MSVC, debug and release builds are not ABI-compatible! +#if defined(_MSC_VER) && defined(_DEBUG) +# define PYBIND11_BUILD_TYPE "_debug" +#else +# define PYBIND11_BUILD_TYPE "" +#endif + +/// Let's assume that different compilers are ABI-incompatible. +/// A user can manually set this string if they know their +/// compiler is compatible. +#ifndef PYBIND11_COMPILER_TYPE +# if defined(_MSC_VER) +# define PYBIND11_COMPILER_TYPE "_msvc" +# elif defined(__INTEL_COMPILER) +# define PYBIND11_COMPILER_TYPE "_icc" +# elif defined(__clang__) +# define PYBIND11_COMPILER_TYPE "_clang" +# elif defined(__PGI) +# define PYBIND11_COMPILER_TYPE "_pgi" +# elif defined(__MINGW32__) +# define PYBIND11_COMPILER_TYPE "_mingw" +# elif defined(__CYGWIN__) +# define PYBIND11_COMPILER_TYPE "_gcc_cygwin" +# elif defined(__GNUC__) +# define PYBIND11_COMPILER_TYPE "_gcc" +# else +# define PYBIND11_COMPILER_TYPE "_unknown" +# endif +#endif + +/// Also standard libs +#ifndef PYBIND11_STDLIB +# if defined(_LIBCPP_VERSION) +# define PYBIND11_STDLIB "_libcpp" +# elif defined(__GLIBCXX__) || defined(__GLIBCPP__) +# define PYBIND11_STDLIB "_libstdcpp" +# else +# define PYBIND11_STDLIB "" +# endif +#endif + +/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility. +/// On MSVC, changes in _MSC_VER may indicate ABI incompatibility (#2898). +#ifndef PYBIND11_BUILD_ABI +# if defined(__GXX_ABI_VERSION) +# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION) +# elif defined(_MSC_VER) +# define PYBIND11_BUILD_ABI "_mscver" PYBIND11_TOSTRING(_MSC_VER) +# else +# define PYBIND11_BUILD_ABI "" +# endif +#endif + +#ifndef PYBIND11_INTERNALS_KIND +# define PYBIND11_INTERNALS_KIND "" +#endif + +#define PYBIND11_PLATFORM_ABI_ID \ + PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \ + PYBIND11_BUILD_TYPE From 3ccea8cd4302cfaf2b6842f77ee3a2eefd61182a Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 27 Aug 2024 17:08:46 -0700 Subject: [PATCH 06/39] factor out internals_version.h from internals.h (no functional changes) --- include/pybind11/detail/internals.h | 29 +----------------- include/pybind11/detail/internals_version.h | 33 +++++++++++++++++++++ 2 files changed, 34 insertions(+), 28 deletions(-) create mode 100644 include/pybind11/detail/internals_version.h diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 0051d34d..089fd420 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -10,6 +10,7 @@ #pragma once #include "common.h" +#include "internals_version.h" #include "platform_abi_id.h" #if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT) @@ -22,34 +23,6 @@ #include #include -/// Tracks the `internals` and `type_info` ABI version independent of the main library version. -/// -/// Some portions of the code use an ABI that is conditional depending on this -/// version number. That allows ABI-breaking changes to be "pre-implemented". -/// Once the default version number is incremented, the conditional logic that -/// no longer applies can be removed. Additionally, users that need not -/// maintain ABI compatibility can increase the version number in order to take -/// advantage of any functionality/efficiency improvements that depend on the -/// newer ABI. -/// -/// WARNING: If you choose to manually increase the ABI version, note that -/// pybind11 may not be tested as thoroughly with a non-default ABI version, and -/// further ABI-incompatible changes may be made before the ABI is officially -/// changed to the new version. -#ifndef PYBIND11_INTERNALS_VERSION -# if PY_VERSION_HEX >= 0x030C0000 || defined(_MSC_VER) -// Version bump for Python 3.12+, before first 3.12 beta release. -// Version bump for MSVC piggy-backed on PR #4779. See comments there. -# define PYBIND11_INTERNALS_VERSION 5 -# else -# define PYBIND11_INTERNALS_VERSION 4 -# endif -#endif - -// This requirement is mainly to reduce the support burden (see PR #4570). -static_assert(PY_VERSION_HEX < 0x030C0000 || PYBIND11_INTERNALS_VERSION >= 5, - "pybind11 ABI version 5 is the minimum for Python 3.12+"); - PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) using ExceptionTranslator = void (*)(std::exception_ptr); diff --git a/include/pybind11/detail/internals_version.h b/include/pybind11/detail/internals_version.h new file mode 100644 index 00000000..f09b2fdb --- /dev/null +++ b/include/pybind11/detail/internals_version.h @@ -0,0 +1,33 @@ +// Copyright (c) 2024 The pybind Community. + +#pragma once + +#include "common.h" + +/// Tracks the `internals` and `type_info` ABI version independent of the main library version. +/// +/// Some portions of the code use an ABI that is conditional depending on this +/// version number. That allows ABI-breaking changes to be "pre-implemented". +/// Once the default version number is incremented, the conditional logic that +/// no longer applies can be removed. Additionally, users that need not +/// maintain ABI compatibility can increase the version number in order to take +/// advantage of any functionality/efficiency improvements that depend on the +/// newer ABI. +/// +/// WARNING: If you choose to manually increase the ABI version, note that +/// pybind11 may not be tested as thoroughly with a non-default ABI version, and +/// further ABI-incompatible changes may be made before the ABI is officially +/// changed to the new version. +#ifndef PYBIND11_INTERNALS_VERSION +# if PY_VERSION_HEX >= 0x030C0000 || defined(_MSC_VER) +// Version bump for Python 3.12+, before first 3.12 beta release. +// Version bump for MSVC piggy-backed on PR #4779. See comments there. +# define PYBIND11_INTERNALS_VERSION 5 +# else +# define PYBIND11_INTERNALS_VERSION 4 +# endif +#endif + +// This requirement is mainly to reduce the support burden (see PR #4570). +static_assert(PY_VERSION_HEX < 0x030C0000 || PYBIND11_INTERNALS_VERSION >= 5, + "pybind11 ABI version 5 is the minimum for Python 3.12+"); From a09d6007a1d63cc412057cbde258a98d1815fe63 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 27 Aug 2024 17:23:47 -0700 Subject: [PATCH 07/39] Update CMakeLists.txt, tests/extra_python_package/test_files.py --- CMakeLists.txt | 2 ++ tests/extra_python_package/test_files.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index b6a76967..127a604c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,6 +133,8 @@ set(PYBIND11_HEADERS include/pybind11/detail/descr.h include/pybind11/detail/init.h include/pybind11/detail/internals.h + include/pybind11/detail/internals_version.h + include/pybind11/detail/platform_abi_id.h include/pybind11/detail/type_caster_base.h include/pybind11/detail/typeid.h include/pybind11/detail/value_and_holder.h diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index feec9f5d..8a95c126 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -56,6 +56,8 @@ "include/pybind11/detail/descr.h", "include/pybind11/detail/init.h", "include/pybind11/detail/internals.h", + "include/pybind11/detail/internals_version.h", + "include/pybind11/detail/platform_abi_id.h", "include/pybind11/detail/type_caster_base.h", "include/pybind11/detail/typeid.h", "include/pybind11/detail/value_and_holder.h", From 2fde1bd94b7cf83ed22541555bd8732503ab4f0b Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 27 Aug 2024 21:11:41 -0700 Subject: [PATCH 08/39] Revert "factor out internals_version.h from internals.h (no functional changes)" This reverts commit 3ccea8cd4302cfaf2b6842f77ee3a2eefd61182a. --- include/pybind11/detail/internals.h | 29 +++++++++++++++++- include/pybind11/detail/internals_version.h | 33 --------------------- 2 files changed, 28 insertions(+), 34 deletions(-) delete mode 100644 include/pybind11/detail/internals_version.h diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 089fd420..0051d34d 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -10,7 +10,6 @@ #pragma once #include "common.h" -#include "internals_version.h" #include "platform_abi_id.h" #if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT) @@ -23,6 +22,34 @@ #include #include +/// Tracks the `internals` and `type_info` ABI version independent of the main library version. +/// +/// Some portions of the code use an ABI that is conditional depending on this +/// version number. That allows ABI-breaking changes to be "pre-implemented". +/// Once the default version number is incremented, the conditional logic that +/// no longer applies can be removed. Additionally, users that need not +/// maintain ABI compatibility can increase the version number in order to take +/// advantage of any functionality/efficiency improvements that depend on the +/// newer ABI. +/// +/// WARNING: If you choose to manually increase the ABI version, note that +/// pybind11 may not be tested as thoroughly with a non-default ABI version, and +/// further ABI-incompatible changes may be made before the ABI is officially +/// changed to the new version. +#ifndef PYBIND11_INTERNALS_VERSION +# if PY_VERSION_HEX >= 0x030C0000 || defined(_MSC_VER) +// Version bump for Python 3.12+, before first 3.12 beta release. +// Version bump for MSVC piggy-backed on PR #4779. See comments there. +# define PYBIND11_INTERNALS_VERSION 5 +# else +# define PYBIND11_INTERNALS_VERSION 4 +# endif +#endif + +// This requirement is mainly to reduce the support burden (see PR #4570). +static_assert(PY_VERSION_HEX < 0x030C0000 || PYBIND11_INTERNALS_VERSION >= 5, + "pybind11 ABI version 5 is the minimum for Python 3.12+"); + PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) using ExceptionTranslator = void (*)(std::exception_ptr); diff --git a/include/pybind11/detail/internals_version.h b/include/pybind11/detail/internals_version.h deleted file mode 100644 index f09b2fdb..00000000 --- a/include/pybind11/detail/internals_version.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2024 The pybind Community. - -#pragma once - -#include "common.h" - -/// Tracks the `internals` and `type_info` ABI version independent of the main library version. -/// -/// Some portions of the code use an ABI that is conditional depending on this -/// version number. That allows ABI-breaking changes to be "pre-implemented". -/// Once the default version number is incremented, the conditional logic that -/// no longer applies can be removed. Additionally, users that need not -/// maintain ABI compatibility can increase the version number in order to take -/// advantage of any functionality/efficiency improvements that depend on the -/// newer ABI. -/// -/// WARNING: If you choose to manually increase the ABI version, note that -/// pybind11 may not be tested as thoroughly with a non-default ABI version, and -/// further ABI-incompatible changes may be made before the ABI is officially -/// changed to the new version. -#ifndef PYBIND11_INTERNALS_VERSION -# if PY_VERSION_HEX >= 0x030C0000 || defined(_MSC_VER) -// Version bump for Python 3.12+, before first 3.12 beta release. -// Version bump for MSVC piggy-backed on PR #4779. See comments there. -# define PYBIND11_INTERNALS_VERSION 5 -# else -# define PYBIND11_INTERNALS_VERSION 4 -# endif -#endif - -// This requirement is mainly to reduce the support burden (see PR #4570). -static_assert(PY_VERSION_HEX < 0x030C0000 || PYBIND11_INTERNALS_VERSION >= 5, - "pybind11 ABI version 5 is the minimum for Python 3.12+"); From 0a29d05784724544affc0e3424a6fd7e1dbd98ff Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 27 Aug 2024 21:13:02 -0700 Subject: [PATCH 09/39] Remove internals_version.h from CMakeLists.txt, tests/extra_python_package/test_files.py --- CMakeLists.txt | 1 - tests/extra_python_package/test_files.py | 1 - 2 files changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 127a604c..9dcd3ec2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,7 +133,6 @@ set(PYBIND11_HEADERS include/pybind11/detail/descr.h include/pybind11/detail/init.h include/pybind11/detail/internals.h - include/pybind11/detail/internals_version.h include/pybind11/detail/platform_abi_id.h include/pybind11/detail/type_caster_base.h include/pybind11/detail/typeid.h diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index 8a95c126..eadb7057 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -56,7 +56,6 @@ "include/pybind11/detail/descr.h", "include/pybind11/detail/init.h", "include/pybind11/detail/internals.h", - "include/pybind11/detail/internals_version.h", "include/pybind11/detail/platform_abi_id.h", "include/pybind11/detail/type_caster_base.h", "include/pybind11/detail/typeid.h", From d2479aa6e74ab7d81e4f69a9602f16000de3b8da Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 27 Aug 2024 22:26:58 -0700 Subject: [PATCH 10/39] `.__cpp_transporter__()` implementation: compare `pybind11_platform_abi_id`, `cpp_typeid_name` --- include/pybind11/detail/cpp_transporter.h | 4 +- tests/test_cpp_transporter.py | 44 ++++++++++++++++--- .../test_cpp_transporter_traveler_bindings.h | 22 ++++++++-- 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/include/pybind11/detail/cpp_transporter.h b/include/pybind11/detail/cpp_transporter.h index 8e845ca4..0d7cfc64 100644 --- a/include/pybind11/detail/cpp_transporter.h +++ b/include/pybind11/detail/cpp_transporter.h @@ -5,6 +5,7 @@ #include "../pytypes.h" #include "common.h" #include "internals.h" +#include "platform_abi_id.h" PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) @@ -55,7 +56,8 @@ inline object try_get_cpp_transporter_method(PyObject *obj) { inline void *try_raw_pointer_ephemeral_from_cpp_transporter(handle src, const char *typeid_name) { object method = try_get_cpp_transporter_method(src.ptr()); if (method) { - object cpp_transporter = method("cpp_abi_code", typeid_name, "raw_pointer_ephemeral"); + object cpp_transporter + = method(PYBIND11_PLATFORM_ABI_ID, typeid_name, "raw_pointer_ephemeral"); if (isinstance(cpp_transporter)) { return reinterpret_borrow(cpp_transporter).get_pointer(); } diff --git a/tests/test_cpp_transporter.py b/tests/test_cpp_transporter.py index 984b977d..141d15e5 100644 --- a/tests/test_cpp_transporter.py +++ b/tests/test_cpp_transporter.py @@ -27,11 +27,43 @@ def test_exo_passed_to_home(): assert home_planet.get_luggage(t_e) == "exo" -def test_call_cpp_transporter(): +def test_call_cpp_transporter_success(): t_h = home_planet.Traveler("home") - assert ( - t_h.__cpp_transporter__( - "cpp_abi_code", "cpp_typeid_name", "raw_pointer_ephemeral" - ) - is not None + cap = t_h.__cpp_transporter__( + home_planet.PYBIND11_PLATFORM_ABI_ID, + home_planet.typeid_Traveler_name, + "raw_pointer_ephemeral", ) + assert cap.__class__.__name__ == "PyCapsule" + + +def test_call_cpp_transporter_platform_abi_id_mismatch(): + t_h = home_planet.Traveler("home") + cap = t_h.__cpp_transporter__( + home_planet.PYBIND11_PLATFORM_ABI_ID + "MISMATCH", + home_planet.typeid_Traveler_name, + "raw_pointer_ephemeral", + ) + assert cap is None + diag = t_h.__cpp_transporter__( + home_planet.PYBIND11_PLATFORM_ABI_ID + "MISMATCH", + home_planet.typeid_Traveler_name, + "query_mismatch", + ) + assert diag == "pybind11_platform_abi_id_mismatch" + + +def test_call_cpp_transporter_type_id_name_mismatch(): + t_h = home_planet.Traveler("home") + cap = t_h.__cpp_transporter__( + home_planet.PYBIND11_PLATFORM_ABI_ID, + home_planet.typeid_Traveler_name + "MISMATCH", + "raw_pointer_ephemeral", + ) + assert cap is None + diag = t_h.__cpp_transporter__( + home_planet.PYBIND11_PLATFORM_ABI_ID, + home_planet.typeid_Traveler_name + "MISMATCH", + "query_mismatch", + ) + assert diag == "cpp_typeid_name_mismatch" diff --git a/tests/test_cpp_transporter_traveler_bindings.h b/tests/test_cpp_transporter_traveler_bindings.h index c125e58f..5666a052 100644 --- a/tests/test_cpp_transporter_traveler_bindings.h +++ b/tests/test_cpp_transporter_traveler_bindings.h @@ -4,6 +4,7 @@ #include "test_cpp_transporter_traveler_type.h" +#include #include namespace pybind11_tests { @@ -12,14 +13,29 @@ namespace test_cpp_transporter { namespace py = pybind11; inline void wrap_traveler(py::module_ m) { + m.attr("PYBIND11_PLATFORM_ABI_ID") = PYBIND11_PLATFORM_ABI_ID; + m.attr("typeid_Traveler_name") = typeid(Traveler).name(); + py::class_(m, "Traveler") .def(py::init()) .def("__cpp_transporter__", [](py::handle self, - const py::str & /*cpp_abi_code*/, - const py::str & /*cpp_typeid_name*/, - const py::str &pointer_kind) { + const py::str &pybind11_platform_abi_id, + const py::str &cpp_typeid_name, + const py::str &pointer_kind) -> py::object { auto pointer_kind_cpp = pointer_kind.cast(); + if (pybind11_platform_abi_id.cast() != PYBIND11_PLATFORM_ABI_ID) { + if (pointer_kind_cpp == "query_mismatch") { + return py::cast("pybind11_platform_abi_id_mismatch"); + } + return py::none(); + } + if (cpp_typeid_name.cast() != typeid(Traveler).name()) { + if (pointer_kind_cpp == "query_mismatch") { + return py::cast("cpp_typeid_name_mismatch"); + } + return py::none(); + } if (pointer_kind_cpp != "raw_pointer_ephemeral") { throw std::runtime_error("Unknown pointer_kind: \"" + pointer_kind_cpp + "\""); From 39ca2aa2e28c87c6e0463a902637fd44264ed224 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 28 Aug 2024 15:23:14 -0700 Subject: [PATCH 11/39] Add PremiumTraveler --- tests/test_cpp_transporter_traveler_bindings.h | 4 ++++ tests/test_cpp_transporter_traveler_type.h | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/tests/test_cpp_transporter_traveler_bindings.h b/tests/test_cpp_transporter_traveler_bindings.h index 5666a052..4939209b 100644 --- a/tests/test_cpp_transporter_traveler_bindings.h +++ b/tests/test_cpp_transporter_traveler_bindings.h @@ -46,6 +46,10 @@ inline void wrap_traveler(py::module_ m) { .def_readwrite("luggage", &Traveler::luggage); m.def("get_luggage", [](const Traveler &person) { return person.luggage; }); + + py::class_(m, "PremiumTraveler") + .def(py::init()) + .def_readwrite("points", &PremiumTraveler::points); } } // namespace test_cpp_transporter diff --git a/tests/test_cpp_transporter_traveler_type.h b/tests/test_cpp_transporter_traveler_type.h index 8d40c917..1f737102 100644 --- a/tests/test_cpp_transporter_traveler_type.h +++ b/tests/test_cpp_transporter_traveler_type.h @@ -10,5 +10,11 @@ struct Traveler { std::string luggage; }; +struct PremiumTraveler : Traveler { + explicit PremiumTraveler(const std::string &luggage, int points) + : Traveler(luggage), points(points) {} + int points; +}; + } // namespace test_cpp_transporter } // namespace pybind11_tests From fc57b446cdc3c39eaf00090bd2c67b83273c246e Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 28 Aug 2024 15:41:54 -0700 Subject: [PATCH 12/39] Rename test_cpp_transporter_traveler_type.h -> test_cpp_transporter_traveler_types.h --- tests/test_cpp_transporter_traveler_bindings.h | 2 +- ...er_traveler_type.h => test_cpp_transporter_traveler_types.h} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/{test_cpp_transporter_traveler_type.h => test_cpp_transporter_traveler_types.h} (100%) diff --git a/tests/test_cpp_transporter_traveler_bindings.h b/tests/test_cpp_transporter_traveler_bindings.h index 4939209b..8ecb8bab 100644 --- a/tests/test_cpp_transporter_traveler_bindings.h +++ b/tests/test_cpp_transporter_traveler_bindings.h @@ -2,7 +2,7 @@ #include -#include "test_cpp_transporter_traveler_type.h" +#include "test_cpp_transporter_traveler_types.h" #include #include diff --git a/tests/test_cpp_transporter_traveler_type.h b/tests/test_cpp_transporter_traveler_types.h similarity index 100% rename from tests/test_cpp_transporter_traveler_type.h rename to tests/test_cpp_transporter_traveler_types.h From 3063c033f9b51da751027d3d7b67c1f79ed8608e Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 28 Aug 2024 15:54:38 -0700 Subject: [PATCH 13/39] Expand tests: `PremiumTraveler`, `get_points()` --- tests/test_cpp_transporter.py | 34 ++++++++++++++++--- .../test_cpp_transporter_traveler_bindings.h | 2 ++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/tests/test_cpp_transporter.py b/tests/test_cpp_transporter.py index 141d15e5..cabde85e 100644 --- a/tests/test_cpp_transporter.py +++ b/tests/test_cpp_transporter.py @@ -5,28 +5,54 @@ from pybind11_tests import cpp_transporter as home_planet -def test_home_only(): +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_exo_only(): +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.Traveler("exo") assert t_e.luggage == "exo" assert exo_planet.get_luggage(t_e) == "exo" -def test_home_passed_to_exo(): +def test_exo_only_premium(): + p_e = exo_planet.PremiumTraveler("exo", 3) + assert p_e.luggage == "exo" + assert exo_planet.get_luggage(p_e) == "exo" + assert exo_planet.get_points(p_e) == 3 + + +def test_home_passed_to_exo_basic(): t_h = home_planet.Traveler("home") assert exo_planet.get_luggage(t_h) == "home" -def test_exo_passed_to_home(): +def test_exo_passed_to_home_basic(): t_e = exo_planet.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.get_luggage(p_h) == "home" + # FAILS assert exo_planet.get_points(p_h) == 2 + + +def test_exo_passed_to_home_premium(): + p_e = exo_planet.PremiumTraveler("exo", 3) + assert home_planet.get_luggage(p_e) == "exo" + # FAILS assert home_planet.get_points(p_e) == 3 + + def test_call_cpp_transporter_success(): t_h = home_planet.Traveler("home") cap = t_h.__cpp_transporter__( diff --git a/tests/test_cpp_transporter_traveler_bindings.h b/tests/test_cpp_transporter_traveler_bindings.h index 8ecb8bab..758db462 100644 --- a/tests/test_cpp_transporter_traveler_bindings.h +++ b/tests/test_cpp_transporter_traveler_bindings.h @@ -50,6 +50,8 @@ inline void wrap_traveler(py::module_ m) { py::class_(m, "PremiumTraveler") .def(py::init()) .def_readwrite("points", &PremiumTraveler::points); + + m.def("get_points", [](const PremiumTraveler &person) { return person.points; }); } } // namespace test_cpp_transporter From 251bf91047ea73ee04efbc806e2c666d36a8f1ea Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 28 Aug 2024 15:58:36 -0700 Subject: [PATCH 14/39] Shuffle order of tests (no real changes). --- tests/test_cpp_transporter.py | 84 +++++++++++++++++------------------ 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/tests/test_cpp_transporter.py b/tests/test_cpp_transporter.py index cabde85e..3341a7bc 100644 --- a/tests/test_cpp_transporter.py +++ b/tests/test_cpp_transporter.py @@ -5,6 +5,48 @@ from pybind11_tests import cpp_transporter as home_planet +def test_call_cpp_transporter_success(): + t_h = home_planet.Traveler("home") + cap = t_h.__cpp_transporter__( + home_planet.PYBIND11_PLATFORM_ABI_ID, + home_planet.typeid_Traveler_name, + "raw_pointer_ephemeral", + ) + assert cap.__class__.__name__ == "PyCapsule" + + +def test_call_cpp_transporter_platform_abi_id_mismatch(): + t_h = home_planet.Traveler("home") + cap = t_h.__cpp_transporter__( + home_planet.PYBIND11_PLATFORM_ABI_ID + "MISMATCH", + home_planet.typeid_Traveler_name, + "raw_pointer_ephemeral", + ) + assert cap is None + diag = t_h.__cpp_transporter__( + home_planet.PYBIND11_PLATFORM_ABI_ID + "MISMATCH", + home_planet.typeid_Traveler_name, + "query_mismatch", + ) + assert diag == "pybind11_platform_abi_id_mismatch" + + +def test_call_cpp_transporter_type_id_name_mismatch(): + t_h = home_planet.Traveler("home") + cap = t_h.__cpp_transporter__( + home_planet.PYBIND11_PLATFORM_ABI_ID, + home_planet.typeid_Traveler_name + "MISMATCH", + "raw_pointer_ephemeral", + ) + assert cap is None + diag = t_h.__cpp_transporter__( + home_planet.PYBIND11_PLATFORM_ABI_ID, + home_planet.typeid_Traveler_name + "MISMATCH", + "query_mismatch", + ) + assert diag == "cpp_typeid_name_mismatch" + + def test_home_only_basic(): t_h = home_planet.Traveler("home") assert t_h.luggage == "home" @@ -51,45 +93,3 @@ def test_exo_passed_to_home_premium(): p_e = exo_planet.PremiumTraveler("exo", 3) assert home_planet.get_luggage(p_e) == "exo" # FAILS assert home_planet.get_points(p_e) == 3 - - -def test_call_cpp_transporter_success(): - t_h = home_planet.Traveler("home") - cap = t_h.__cpp_transporter__( - home_planet.PYBIND11_PLATFORM_ABI_ID, - home_planet.typeid_Traveler_name, - "raw_pointer_ephemeral", - ) - assert cap.__class__.__name__ == "PyCapsule" - - -def test_call_cpp_transporter_platform_abi_id_mismatch(): - t_h = home_planet.Traveler("home") - cap = t_h.__cpp_transporter__( - home_planet.PYBIND11_PLATFORM_ABI_ID + "MISMATCH", - home_planet.typeid_Traveler_name, - "raw_pointer_ephemeral", - ) - assert cap is None - diag = t_h.__cpp_transporter__( - home_planet.PYBIND11_PLATFORM_ABI_ID + "MISMATCH", - home_planet.typeid_Traveler_name, - "query_mismatch", - ) - assert diag == "pybind11_platform_abi_id_mismatch" - - -def test_call_cpp_transporter_type_id_name_mismatch(): - t_h = home_planet.Traveler("home") - cap = t_h.__cpp_transporter__( - home_planet.PYBIND11_PLATFORM_ABI_ID, - home_planet.typeid_Traveler_name + "MISMATCH", - "raw_pointer_ephemeral", - ) - assert cap is None - diag = t_h.__cpp_transporter__( - home_planet.PYBIND11_PLATFORM_ABI_ID, - home_planet.typeid_Traveler_name + "MISMATCH", - "query_mismatch", - ) - assert diag == "cpp_typeid_name_mismatch" From 2df540ea4d1708880fd1d20f57355f3caa94b397 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 28 Aug 2024 16:07:33 -0700 Subject: [PATCH 15/39] Move `__cpp_transporter__` lambda to `py::cpp_transporter()` regular function. --- .../test_cpp_transporter_traveler_bindings.h | 55 ++++++++++--------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/tests/test_cpp_transporter_traveler_bindings.h b/tests/test_cpp_transporter_traveler_bindings.h index 758db462..666825d4 100644 --- a/tests/test_cpp_transporter_traveler_bindings.h +++ b/tests/test_cpp_transporter_traveler_bindings.h @@ -7,6 +7,35 @@ #include #include +PYBIND11_NAMESPACE_BEGIN(pybind11) + +template +object cpp_transporter(handle self, + const str &pybind11_platform_abi_id, + const str &cpp_typeid_name, + const str &pointer_kind) { + auto pointer_kind_cpp = pointer_kind.cast(); + if (pybind11_platform_abi_id.cast() != PYBIND11_PLATFORM_ABI_ID) { + if (pointer_kind_cpp == "query_mismatch") { + return cast("pybind11_platform_abi_id_mismatch"); + } + return none(); + } + if (cpp_typeid_name.cast() != typeid(T).name()) { + if (pointer_kind_cpp == "query_mismatch") { + return cast("cpp_typeid_name_mismatch"); + } + return none(); + } + if (pointer_kind_cpp != "raw_pointer_ephemeral") { + throw std::runtime_error("Unknown pointer_kind: \"" + pointer_kind_cpp + "\""); + } + auto *self_cpp_ptr = cast(self); + return capsule(static_cast(self_cpp_ptr), typeid(T).name()); +} + +PYBIND11_NAMESPACE_END(pybind11) + namespace pybind11_tests { namespace test_cpp_transporter { @@ -18,31 +47,7 @@ inline void wrap_traveler(py::module_ m) { py::class_(m, "Traveler") .def(py::init()) - .def("__cpp_transporter__", - [](py::handle self, - const py::str &pybind11_platform_abi_id, - const py::str &cpp_typeid_name, - const py::str &pointer_kind) -> py::object { - auto pointer_kind_cpp = pointer_kind.cast(); - if (pybind11_platform_abi_id.cast() != PYBIND11_PLATFORM_ABI_ID) { - if (pointer_kind_cpp == "query_mismatch") { - return py::cast("pybind11_platform_abi_id_mismatch"); - } - return py::none(); - } - if (cpp_typeid_name.cast() != typeid(Traveler).name()) { - if (pointer_kind_cpp == "query_mismatch") { - return py::cast("cpp_typeid_name_mismatch"); - } - return py::none(); - } - if (pointer_kind_cpp != "raw_pointer_ephemeral") { - throw std::runtime_error("Unknown pointer_kind: \"" + pointer_kind_cpp - + "\""); - } - auto *self_cpp_ptr = py::cast(self); - return py::capsule(static_cast(self_cpp_ptr), typeid(Traveler).name()); - }) + .def("__cpp_transporter__", py::cpp_transporter) .def_readwrite("luggage", &Traveler::luggage); m.def("get_luggage", [](const Traveler &person) { return person.luggage; }); From 757f88596628d6f850078c24eea36fd5001782f4 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 28 Aug 2024 16:33:03 -0700 Subject: [PATCH 16/39] Use `type_caster_generic::load(self)` instead of `cast(self)` --- .../test_cpp_transporter_traveler_bindings.h | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/test_cpp_transporter_traveler_bindings.h b/tests/test_cpp_transporter_traveler_bindings.h index 666825d4..bbbc8bfa 100644 --- a/tests/test_cpp_transporter_traveler_bindings.h +++ b/tests/test_cpp_transporter_traveler_bindings.h @@ -9,8 +9,8 @@ PYBIND11_NAMESPACE_BEGIN(pybind11) -template object cpp_transporter(handle self, + const std::type_info &self_cpp_type_info, const str &pybind11_platform_abi_id, const str &cpp_typeid_name, const str &pointer_kind) { @@ -21,7 +21,7 @@ object cpp_transporter(handle self, } return none(); } - if (cpp_typeid_name.cast() != typeid(T).name()) { + if (cpp_typeid_name.cast() != self_cpp_type_info.name()) { if (pointer_kind_cpp == "query_mismatch") { return cast("cpp_typeid_name_mismatch"); } @@ -30,8 +30,20 @@ object cpp_transporter(handle self, if (pointer_kind_cpp != "raw_pointer_ephemeral") { throw std::runtime_error("Unknown pointer_kind: \"" + pointer_kind_cpp + "\""); } - auto *self_cpp_ptr = cast(self); - return capsule(static_cast(self_cpp_ptr), typeid(T).name()); + detail::type_caster_generic caster(self_cpp_type_info); + if (!caster.load(self, false)) { + return none(); + } + return capsule(caster.value, self_cpp_type_info.name()); +} + +template +object cpp_transporter(handle self, + const str &pybind11_platform_abi_id, + const str &cpp_typeid_name, + const str &pointer_kind) { + return cpp_transporter( + self, typeid(T), pybind11_platform_abi_id, cpp_typeid_name, pointer_kind); } PYBIND11_NAMESPACE_END(pybind11) From 3951b63030ee00cf55331a7034108c64de7f4155 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 28 Aug 2024 17:18:50 -0700 Subject: [PATCH 17/39] Pass `const std::type_info *` via `py::capsule` (instead of `cpp_typeid_name`). --- include/pybind11/detail/cpp_transporter.h | 7 +++-- include/pybind11/detail/type_caster_base.h | 2 +- tests/test_cpp_transporter.py | 10 +++---- .../test_cpp_transporter_traveler_bindings.h | 29 +++++++------------ 4 files changed, 21 insertions(+), 27 deletions(-) diff --git a/include/pybind11/detail/cpp_transporter.h b/include/pybind11/detail/cpp_transporter.h index 0d7cfc64..2c5df42f 100644 --- a/include/pybind11/detail/cpp_transporter.h +++ b/include/pybind11/detail/cpp_transporter.h @@ -53,11 +53,14 @@ inline object try_get_cpp_transporter_method(PyObject *obj) { return reinterpret_steal(method); } -inline void *try_raw_pointer_ephemeral_from_cpp_transporter(handle src, const char *typeid_name) { +inline void *try_raw_pointer_ephemeral_from_cpp_transporter(handle src, + const std::type_info *cpp_type_info) { object method = try_get_cpp_transporter_method(src.ptr()); if (method) { + capsule cap_cpp_type_info(const_cast(static_cast(cpp_type_info)), + "const std::type_info *"); object cpp_transporter - = method(PYBIND11_PLATFORM_ABI_ID, typeid_name, "raw_pointer_ephemeral"); + = method(PYBIND11_PLATFORM_ABI_ID, cap_cpp_type_info, "raw_pointer_ephemeral"); if (isinstance(cpp_transporter)) { return reinterpret_borrow(cpp_transporter).get_pointer(); } diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index 88e5c071..a52f5658 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -613,7 +613,7 @@ class type_caster_generic { return false; } bool try_cpp_transporter(handle src) { - value = try_raw_pointer_ephemeral_from_cpp_transporter(src, cpptype->name()); + value = try_raw_pointer_ephemeral_from_cpp_transporter(src, cpptype); if (value != nullptr) { return true; } diff --git a/tests/test_cpp_transporter.py b/tests/test_cpp_transporter.py index 3341a7bc..2a34bcc5 100644 --- a/tests/test_cpp_transporter.py +++ b/tests/test_cpp_transporter.py @@ -5,7 +5,7 @@ from pybind11_tests import cpp_transporter as home_planet -def test_call_cpp_transporter_success(): +def NOtest_call_cpp_transporter_success(): t_h = home_planet.Traveler("home") cap = t_h.__cpp_transporter__( home_planet.PYBIND11_PLATFORM_ABI_ID, @@ -15,7 +15,7 @@ def test_call_cpp_transporter_success(): assert cap.__class__.__name__ == "PyCapsule" -def test_call_cpp_transporter_platform_abi_id_mismatch(): +def NOtest_call_cpp_transporter_platform_abi_id_mismatch(): t_h = home_planet.Traveler("home") cap = t_h.__cpp_transporter__( home_planet.PYBIND11_PLATFORM_ABI_ID + "MISMATCH", @@ -31,7 +31,7 @@ def test_call_cpp_transporter_platform_abi_id_mismatch(): assert diag == "pybind11_platform_abi_id_mismatch" -def test_call_cpp_transporter_type_id_name_mismatch(): +def NOtest_call_cpp_transporter_type_id_name_mismatch(): t_h = home_planet.Traveler("home") cap = t_h.__cpp_transporter__( home_planet.PYBIND11_PLATFORM_ABI_ID, @@ -86,10 +86,10 @@ def test_exo_passed_to_home_basic(): def test_home_passed_to_exo_premium(): p_h = home_planet.PremiumTraveler("home", 2) assert exo_planet.get_luggage(p_h) == "home" - # FAILS assert exo_planet.get_points(p_h) == 2 + assert exo_planet.get_points(p_h) == 2 def test_exo_passed_to_home_premium(): p_e = exo_planet.PremiumTraveler("exo", 3) assert home_planet.get_luggage(p_e) == "exo" - # FAILS assert home_planet.get_points(p_e) == 3 + assert home_planet.get_points(p_e) == 3 diff --git a/tests/test_cpp_transporter_traveler_bindings.h b/tests/test_cpp_transporter_traveler_bindings.h index bbbc8bfa..da0a3316 100644 --- a/tests/test_cpp_transporter_traveler_bindings.h +++ b/tests/test_cpp_transporter_traveler_bindings.h @@ -9,11 +9,10 @@ PYBIND11_NAMESPACE_BEGIN(pybind11) -object cpp_transporter(handle self, - const std::type_info &self_cpp_type_info, - const str &pybind11_platform_abi_id, - const str &cpp_typeid_name, - const str &pointer_kind) { +inline object cpp_transporter(handle self, + const str &pybind11_platform_abi_id, + const capsule &cap_cpp_type_info, + const str &pointer_kind) { auto pointer_kind_cpp = pointer_kind.cast(); if (pybind11_platform_abi_id.cast() != PYBIND11_PLATFORM_ABI_ID) { if (pointer_kind_cpp == "query_mismatch") { @@ -21,29 +20,21 @@ object cpp_transporter(handle self, } return none(); } - if (cpp_typeid_name.cast() != self_cpp_type_info.name()) { + if (std::strcmp(cap_cpp_type_info.name(), "const std::type_info *") != 0) { if (pointer_kind_cpp == "query_mismatch") { - return cast("cpp_typeid_name_mismatch"); + return cast("cap_cpp_type_info_name_mismatch"); } return none(); } if (pointer_kind_cpp != "raw_pointer_ephemeral") { throw std::runtime_error("Unknown pointer_kind: \"" + pointer_kind_cpp + "\""); } - detail::type_caster_generic caster(self_cpp_type_info); + const auto *cpp_type_info = cap_cpp_type_info.get_pointer(); + detail::type_caster_generic caster(*cpp_type_info); if (!caster.load(self, false)) { return none(); } - return capsule(caster.value, self_cpp_type_info.name()); -} - -template -object cpp_transporter(handle self, - const str &pybind11_platform_abi_id, - const str &cpp_typeid_name, - const str &pointer_kind) { - return cpp_transporter( - self, typeid(T), pybind11_platform_abi_id, cpp_typeid_name, pointer_kind); + return capsule(caster.value, cpp_type_info->name()); } PYBIND11_NAMESPACE_END(pybind11) @@ -58,8 +49,8 @@ inline void wrap_traveler(py::module_ m) { m.attr("typeid_Traveler_name") = typeid(Traveler).name(); py::class_(m, "Traveler") + .def("__cpp_transporter__", py::cpp_transporter) .def(py::init()) - .def("__cpp_transporter__", py::cpp_transporter) .def_readwrite("luggage", &Traveler::luggage); m.def("get_luggage", [](const Traveler &person) { return person.luggage; }); From 981a2a7693718ae6eb90002a6efb35b2779e3c81 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 29 Aug 2024 16:01:56 -0700 Subject: [PATCH 18/39] Make platform_abi_id.h completely stand-alone. --- include/pybind11/detail/common.h | 4 ++-- include/pybind11/detail/platform_abi_id.h | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 2a39e88f..df493220 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -9,6 +9,8 @@ #pragma once +#include "platform_abi_id.h" + #define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MINOR 14 #define PYBIND11_VERSION_PATCH 0.dev1 @@ -382,8 +384,6 @@ PYBIND11_WARNING_POP extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() #define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code -#define PYBIND11_STRINGIFY(x) #x -#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) #define PYBIND11_CONCAT(first, second) first##second #define PYBIND11_ENSURE_INTERNALS_READY pybind11::detail::get_internals(); diff --git a/include/pybind11/detail/platform_abi_id.h b/include/pybind11/detail/platform_abi_id.h index 88cb3495..93466304 100644 --- a/include/pybind11/detail/platform_abi_id.h +++ b/include/pybind11/detail/platform_abi_id.h @@ -2,7 +2,14 @@ #pragma once -#include "common.h" +// ************************************** +// DO NOT #include ANY HEADER FILES HERE! +// ************************************** +// This is to make this file easily reusable in all environments, so that +// other bindings systems can easily and safely interoperate with pybind11. + +#define PYBIND11_STRINGIFY(x) #x +#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) /// On MSVC, debug and release builds are not ABI-compatible! #if defined(_MSC_VER) && defined(_DEBUG) From 4e9a0c7c8964fb1f1f236aaff54df8addb3eb9dc Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 29 Aug 2024 16:06:45 -0700 Subject: [PATCH 19/39] rename exo_planet.cpp -> exo_planet_pybind11.cpp --- tests/CMakeLists.txt | 2 +- ...exo_planet.cpp => exo_planet_pybind11.cpp} | 2 +- tests/test_cpp_transporter.py | 22 +++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) rename tests/{exo_planet.cpp => exo_planet_pybind11.cpp} (64%) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5b2c5ec3..5e04fd69 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -219,7 +219,7 @@ tests_extra_targets("test_exceptions.py;test_local_bindings.py;test_stl.py;test_ # And add additional targets for other tests. 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_cpp_transporter.py" "exo_planet") +tests_extra_targets("test_cpp_transporter.py" "exo_planet_pybind11") set(PYBIND11_EIGEN_REPO "https://gitlab.com/libeigen/eigen.git" diff --git a/tests/exo_planet.cpp b/tests/exo_planet_pybind11.cpp similarity index 64% rename from tests/exo_planet.cpp rename to tests/exo_planet_pybind11.cpp index e1fefda3..1556d58c 100644 --- a/tests/exo_planet.cpp +++ b/tests/exo_planet_pybind11.cpp @@ -5,4 +5,4 @@ #include "test_cpp_transporter_traveler_bindings.h" -PYBIND11_MODULE(exo_planet, m) { pybind11_tests::test_cpp_transporter::wrap_traveler(m); } +PYBIND11_MODULE(exo_planet_pybind11, m) { pybind11_tests::test_cpp_transporter::wrap_traveler(m); } diff --git a/tests/test_cpp_transporter.py b/tests/test_cpp_transporter.py index 2a34bcc5..7e5808a0 100644 --- a/tests/test_cpp_transporter.py +++ b/tests/test_cpp_transporter.py @@ -1,6 +1,6 @@ from __future__ import annotations -import exo_planet +import exo_planet_pybind11 as ex11_planet from pybind11_tests import cpp_transporter as home_planet @@ -61,35 +61,35 @@ def test_home_only_premium(): def test_exo_only_basic(): - t_e = exo_planet.Traveler("exo") + t_e = ex11_planet.Traveler("exo") assert t_e.luggage == "exo" - assert exo_planet.get_luggage(t_e) == "exo" + assert ex11_planet.get_luggage(t_e) == "exo" def test_exo_only_premium(): - p_e = exo_planet.PremiumTraveler("exo", 3) + p_e = ex11_planet.PremiumTraveler("exo", 3) assert p_e.luggage == "exo" - assert exo_planet.get_luggage(p_e) == "exo" - assert exo_planet.get_points(p_e) == 3 + assert ex11_planet.get_luggage(p_e) == "exo" + assert ex11_planet.get_points(p_e) == 3 def test_home_passed_to_exo_basic(): t_h = home_planet.Traveler("home") - assert exo_planet.get_luggage(t_h) == "home" + assert ex11_planet.get_luggage(t_h) == "home" def test_exo_passed_to_home_basic(): - t_e = exo_planet.Traveler("exo") + t_e = ex11_planet.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.get_luggage(p_h) == "home" - assert exo_planet.get_points(p_h) == 2 + assert ex11_planet.get_luggage(p_h) == "home" + assert ex11_planet.get_points(p_h) == 2 def test_exo_passed_to_home_premium(): - p_e = exo_planet.PremiumTraveler("exo", 3) + p_e = ex11_planet.PremiumTraveler("exo", 3) assert home_planet.get_luggage(p_e) == "exo" assert home_planet.get_points(p_e) == 3 From 777ab9b01c9f7fc2311b6cd340a2620a4f025e7d Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 29 Aug 2024 16:56:43 -0700 Subject: [PATCH 20/39] Add exo_planet_c_api.cpp (incomplete). --- tests/CMakeLists.txt | 2 +- tests/exo_planet_c_api.cpp | 43 +++++++++++++++++++++++++++++++++++ tests/test_cpp_transporter.py | 7 ++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 tests/exo_planet_c_api.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5e04fd69..40b1bd5d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -219,7 +219,7 @@ tests_extra_targets("test_exceptions.py;test_local_bindings.py;test_stl.py;test_ # And add additional targets for other tests. 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_cpp_transporter.py" "exo_planet_pybind11") +tests_extra_targets("test_cpp_transporter.py" "exo_planet_pybind11;exo_planet_c_api") set(PYBIND11_EIGEN_REPO "https://gitlab.com/libeigen/eigen.git" diff --git a/tests/exo_planet_c_api.cpp b/tests/exo_planet_c_api.cpp new file mode 100644 index 00000000..7a4110bc --- /dev/null +++ b/tests/exo_planet_c_api.cpp @@ -0,0 +1,43 @@ +#include // THIS MUST STAY AT THE TOP! + +// DO NOT USE ANY OTHER pybind11 HEADERS HERE! +#include + +#include "test_cpp_transporter_traveler_bindings.h" + +namespace { + +static PyObject *wrapGetLuggage(PyObject *, PyObject *) { return PyUnicode_FromString("TODO"); } + +PyDoc_STRVAR(ThisModuleDoc, "Uses only the plain CPython API."); + +static PyMethodDef ThisMethodDef[] + = {{"GetLuggage", wrapGetLuggage, METH_VARARGS, nullptr}, {nullptr, nullptr, 0, nullptr}}; + +static struct PyModuleDef ThisModuleDef = { + PyModuleDef_HEAD_INIT, // m_base + "exo_planet_c_api", // m_name + ThisModuleDoc, // 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; +} diff --git a/tests/test_cpp_transporter.py b/tests/test_cpp_transporter.py index 7e5808a0..9b28c5fd 100644 --- a/tests/test_cpp_transporter.py +++ b/tests/test_cpp_transporter.py @@ -1,5 +1,6 @@ from __future__ import annotations +import exo_planet_c_api as exca_planet import exo_planet_pybind11 as ex11_planet from pybind11_tests import cpp_transporter as home_planet @@ -93,3 +94,9 @@ def test_exo_passed_to_home_premium(): p_e = ex11_planet.PremiumTraveler("exo", 3) assert home_planet.get_luggage(p_e) == "exo" assert home_planet.get_points(p_e) == 3 + + +def test_exca_planet(): + assert exca_planet.__doc__ == "Uses only the plain CPython API." + t_h = home_planet.Traveler("home") + assert exca_planet.GetLuggage(t_h) == "TODO" From e220b42e9f6b5b29eda36e3ed611edcab91820c4 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 29 Aug 2024 21:58:54 -0700 Subject: [PATCH 21/39] Fix silly oversight (wrong filename in `#include`). --- tests/exo_planet_c_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/exo_planet_c_api.cpp b/tests/exo_planet_c_api.cpp index 7a4110bc..04fbcd12 100644 --- a/tests/exo_planet_c_api.cpp +++ b/tests/exo_planet_c_api.cpp @@ -3,7 +3,7 @@ // DO NOT USE ANY OTHER pybind11 HEADERS HERE! #include -#include "test_cpp_transporter_traveler_bindings.h" +#include "test_cpp_transporter_traveler_types.h" namespace { From aa81066584702968667b2110d33ce17dca5fdac9 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 29 Aug 2024 22:10:00 -0700 Subject: [PATCH 22/39] Resolve clang-tidy errors: ``` /__w/pybind11/pybind11/tests/exo_planet_c_api.cpp:10:18: error: 'wrapGetLuggage' is a static definition in anonymous namespace; static is redundant here [readability-static-definition-in-anonymous-namespace,-warnings-as-errors] 10 | static PyObject *wrapGetLuggage(PyObject *, PyObject *) { return PyUnicode_FromString("TODO"); } | ~~~~~~ ^ /__w/pybind11/pybind11/tests/exo_planet_c_api.cpp:14:20: error: 'ThisMethodDef' is a static definition in anonymous namespace; static is redundant here [readability-static-definition-in-anonymous-namespace,-warnings-as-errors] 14 | static PyMethodDef ThisMethodDef[] | ~~~~~~ ^ /__w/pybind11/pybind11/tests/exo_planet_c_api.cpp:17:27: error: 'ThisModuleDef' is a static definition in anonymous namespace; static is redundant here [readability-static-definition-in-anonymous-namespace,-warnings-as-errors] 17 | static struct PyModuleDef ThisModuleDef = { | ~~~~~~ ^ ``` --- tests/exo_planet_c_api.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/exo_planet_c_api.cpp b/tests/exo_planet_c_api.cpp index 04fbcd12..2cee9a13 100644 --- a/tests/exo_planet_c_api.cpp +++ b/tests/exo_planet_c_api.cpp @@ -7,14 +7,16 @@ namespace { -static PyObject *wrapGetLuggage(PyObject *, PyObject *) { return PyUnicode_FromString("TODO"); } +extern "C" PyObject *wrapGetLuggage(PyObject *, PyObject *) { + return PyUnicode_FromString("TODO"); +} PyDoc_STRVAR(ThisModuleDoc, "Uses only the plain CPython API."); -static PyMethodDef ThisMethodDef[] +PyMethodDef ThisMethodDef[] = {{"GetLuggage", wrapGetLuggage, METH_VARARGS, nullptr}, {nullptr, nullptr, 0, nullptr}}; -static struct PyModuleDef ThisModuleDef = { +struct PyModuleDef ThisModuleDef = { PyModuleDef_HEAD_INIT, // m_base "exo_planet_c_api", // m_name ThisModuleDoc, // m_doc From caccc6fa7a1b87d6ffb2b2b55af2d2d15cb83d8a Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 30 Aug 2024 01:01:59 -0700 Subject: [PATCH 23/39] Implement exo_planet_c_api GetLuggage(), GetPoints() --- include/pybind11/detail/common.h | 4 +- include/pybind11/detail/platform_abi_id.h | 11 ++-- tests/exo_planet_c_api.cpp | 61 ++++++++++++++++++++--- tests/test_cpp_transporter.py | 45 +++++++++++------ 4 files changed, 87 insertions(+), 34 deletions(-) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index df493220..2a39e88f 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -9,8 +9,6 @@ #pragma once -#include "platform_abi_id.h" - #define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MINOR 14 #define PYBIND11_VERSION_PATCH 0.dev1 @@ -384,6 +382,8 @@ PYBIND11_WARNING_POP extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() #define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code +#define PYBIND11_STRINGIFY(x) #x +#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) #define PYBIND11_CONCAT(first, second) first##second #define PYBIND11_ENSURE_INTERNALS_READY pybind11::detail::get_internals(); diff --git a/include/pybind11/detail/platform_abi_id.h b/include/pybind11/detail/platform_abi_id.h index 93466304..33f18b6c 100644 --- a/include/pybind11/detail/platform_abi_id.h +++ b/include/pybind11/detail/platform_abi_id.h @@ -2,14 +2,9 @@ #pragma once -// ************************************** -// DO NOT #include ANY HEADER FILES HERE! -// ************************************** -// This is to make this file easily reusable in all environments, so that -// other bindings systems can easily and safely interoperate with pybind11. - -#define PYBIND11_STRINGIFY(x) #x -#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) +// This is needed, unfortunately. +// E.g. on some platforms, `#include ` defines `__GLIBCXX__`. +#include "common.h" /// On MSVC, debug and release builds are not ABI-compatible! #if defined(_MSC_VER) && defined(_DEBUG) diff --git a/tests/exo_planet_c_api.cpp b/tests/exo_planet_c_api.cpp index 2cee9a13..01e5a19c 100644 --- a/tests/exo_planet_c_api.cpp +++ b/tests/exo_planet_c_api.cpp @@ -1,25 +1,70 @@ -#include // THIS MUST STAY AT THE TOP! - +// THIS MUST STAY AT THE TOP! // DO NOT USE ANY OTHER pybind11 HEADERS HERE! #include #include "test_cpp_transporter_traveler_types.h" +#include +#include + namespace { -extern "C" PyObject *wrapGetLuggage(PyObject *, PyObject *) { - return PyUnicode_FromString("TODO"); +void *get_cpp_transporter_void_ptr(PyObject *py_obj, const std::type_info *cpp_type_info) { + PyObject *cap_cpp_type_info + = PyCapsule_New(const_cast(static_cast(cpp_type_info)), + "const std::type_info *", + nullptr); + if (cap_cpp_type_info == nullptr) { + return nullptr; + } + PyObject *cpp_transporter = PyObject_CallMethod(py_obj, + "__cpp_transporter__", + "sOs", + PYBIND11_PLATFORM_ABI_ID, + cap_cpp_type_info, + "raw_pointer_ephemeral"); + Py_DECREF(cap_cpp_type_info); + if (cpp_transporter == nullptr) { + return nullptr; + } + void *void_ptr = PyCapsule_GetPointer(cpp_transporter, cpp_type_info->name()); + Py_DECREF(cpp_transporter); + if (PyErr_Occurred()) { + return nullptr; + } + return void_ptr; } -PyDoc_STRVAR(ThisModuleDoc, "Uses only the plain CPython API."); +template +T *get_cpp_transporter_type_ptr(PyObject *py_obj) { + void *void_ptr = get_cpp_transporter_void_ptr(py_obj, &typeid(T)); + if (void_ptr == nullptr) { + return nullptr; + } + return static_cast(void_ptr); +} + +extern "C" PyObject *wrapGetLuggage(PyObject * /*self*/, PyObject *traveler) { + const auto *cpp_traveler + = get_cpp_transporter_type_ptr(traveler); + return PyUnicode_FromString(cpp_traveler->luggage.c_str()); +} + +extern "C" PyObject *wrapGetPoints(PyObject * /*self*/, PyObject *premium_traveler) { + const auto *cpp_premium_traveler + = get_cpp_transporter_type_ptr( + premium_traveler); + return PyLong_FromLong(static_cast(cpp_premium_traveler->points)); +} -PyMethodDef ThisMethodDef[] - = {{"GetLuggage", wrapGetLuggage, METH_VARARGS, nullptr}, {nullptr, nullptr, 0, nullptr}}; +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 - ThisModuleDoc, // m_doc + nullptr, // m_doc -1, // m_size ThisMethodDef, // m_methods nullptr, // m_slots diff --git a/tests/test_cpp_transporter.py b/tests/test_cpp_transporter.py index 9b28c5fd..d4abde02 100644 --- a/tests/test_cpp_transporter.py +++ b/tests/test_cpp_transporter.py @@ -1,7 +1,8 @@ from __future__ import annotations -import exo_planet_c_api as exca_planet -import exo_planet_pybind11 as ex11_planet +import exo_planet_c_api +import exo_planet_pybind11 +import pytest from pybind11_tests import cpp_transporter as home_planet @@ -62,41 +63,53 @@ def test_home_only_premium(): def test_exo_only_basic(): - t_e = ex11_planet.Traveler("exo") + t_e = exo_planet_pybind11.Traveler("exo") assert t_e.luggage == "exo" - assert ex11_planet.get_luggage(t_e) == "exo" + assert exo_planet_pybind11.get_luggage(t_e) == "exo" def test_exo_only_premium(): - p_e = ex11_planet.PremiumTraveler("exo", 3) + p_e = exo_planet_pybind11.PremiumTraveler("exo", 3) assert p_e.luggage == "exo" - assert ex11_planet.get_luggage(p_e) == "exo" - assert ex11_planet.get_points(p_e) == 3 + 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 ex11_planet.get_luggage(t_h) == "home" + assert exo_planet_pybind11.get_luggage(t_h) == "home" def test_exo_passed_to_home_basic(): - t_e = ex11_planet.Traveler("exo") + 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 ex11_planet.get_luggage(p_h) == "home" - assert ex11_planet.get_points(p_h) == 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 = ex11_planet.PremiumTraveler("exo", 3) + p_e = exo_planet_pybind11.PremiumTraveler("exo", 3) assert home_planet.get_luggage(p_e) == "exo" assert home_planet.get_points(p_e) == 3 -def test_exca_planet(): - assert exca_planet.__doc__ == "Uses only the plain CPython API." - t_h = home_planet.Traveler("home") - assert exca_planet.GetLuggage(t_h) == "TODO" +@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 From 78c3c88a71bd918ffd1cb541d7f2dbd14da790c1 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 30 Aug 2024 12:31:35 -0700 Subject: [PATCH 24/39] Move new code from test_cpp_transporter_traveler_bindings.h to pybind11/detail/type_caster_base.h, under the name `class_dunder_cpp_transporter()` --- include/pybind11/detail/cpp_transporter.h | 3 +- include/pybind11/detail/type_caster_base.h | 22 ++++++++++++ .../test_cpp_transporter_traveler_bindings.h | 34 +------------------ 3 files changed, 25 insertions(+), 34 deletions(-) diff --git a/include/pybind11/detail/cpp_transporter.h b/include/pybind11/detail/cpp_transporter.h index 2c5df42f..d826b885 100644 --- a/include/pybind11/detail/cpp_transporter.h +++ b/include/pybind11/detail/cpp_transporter.h @@ -2,7 +2,8 @@ #pragma once -#include "../pytypes.h" +#include + #include "common.h" #include "internals.h" #include "platform_abi_id.h" diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index a52f5658..0ab82d07 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -781,6 +781,28 @@ class type_caster_generic { void *value = nullptr; }; +inline object class_dunder_cpp_transporter(handle self, + const str &pybind11_platform_abi_id, + const capsule &cap_cpp_type_info, + const str &pointer_kind) { + auto pointer_kind_cpp = pointer_kind.cast(); + if (pybind11_platform_abi_id.cast() != PYBIND11_PLATFORM_ABI_ID) { + return none(); + } + if (std::strcmp(cap_cpp_type_info.name(), "const std::type_info *") != 0) { + return none(); + } + if (pointer_kind_cpp != "raw_pointer_ephemeral") { + throw std::runtime_error("Unknown pointer_kind: \"" + pointer_kind_cpp + "\""); + } + const auto *cpp_type_info = cap_cpp_type_info.get_pointer(); + 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 * needs to provide `operator T*()` and `operator T&()` operators. diff --git a/tests/test_cpp_transporter_traveler_bindings.h b/tests/test_cpp_transporter_traveler_bindings.h index da0a3316..94fa2483 100644 --- a/tests/test_cpp_transporter_traveler_bindings.h +++ b/tests/test_cpp_transporter_traveler_bindings.h @@ -7,38 +7,6 @@ #include #include -PYBIND11_NAMESPACE_BEGIN(pybind11) - -inline object cpp_transporter(handle self, - const str &pybind11_platform_abi_id, - const capsule &cap_cpp_type_info, - const str &pointer_kind) { - auto pointer_kind_cpp = pointer_kind.cast(); - if (pybind11_platform_abi_id.cast() != PYBIND11_PLATFORM_ABI_ID) { - if (pointer_kind_cpp == "query_mismatch") { - return cast("pybind11_platform_abi_id_mismatch"); - } - return none(); - } - if (std::strcmp(cap_cpp_type_info.name(), "const std::type_info *") != 0) { - if (pointer_kind_cpp == "query_mismatch") { - return cast("cap_cpp_type_info_name_mismatch"); - } - return none(); - } - if (pointer_kind_cpp != "raw_pointer_ephemeral") { - throw std::runtime_error("Unknown pointer_kind: \"" + pointer_kind_cpp + "\""); - } - const auto *cpp_type_info = cap_cpp_type_info.get_pointer(); - detail::type_caster_generic caster(*cpp_type_info); - if (!caster.load(self, false)) { - return none(); - } - return capsule(caster.value, cpp_type_info->name()); -} - -PYBIND11_NAMESPACE_END(pybind11) - namespace pybind11_tests { namespace test_cpp_transporter { @@ -49,7 +17,7 @@ inline void wrap_traveler(py::module_ m) { m.attr("typeid_Traveler_name") = typeid(Traveler).name(); py::class_(m, "Traveler") - .def("__cpp_transporter__", py::cpp_transporter) + .def("__cpp_transporter__", py::detail::class_dunder_cpp_transporter) .def(py::init()) .def_readwrite("luggage", &Traveler::luggage); From 926c2ae1d5d15311aa3b3f931d39a2a532b0368b Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 30 Aug 2024 12:40:11 -0700 Subject: [PATCH 25/39] Fix oversight. --- tests/exo_planet_c_api.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/exo_planet_c_api.cpp b/tests/exo_planet_c_api.cpp index 01e5a19c..17dc5768 100644 --- a/tests/exo_planet_c_api.cpp +++ b/tests/exo_planet_c_api.cpp @@ -47,6 +47,9 @@ T *get_cpp_transporter_type_ptr(PyObject *py_obj) { extern "C" PyObject *wrapGetLuggage(PyObject * /*self*/, PyObject *traveler) { const auto *cpp_traveler = get_cpp_transporter_type_ptr(traveler); + if (cpp_traveler == nullptr) { + return nullptr; + } return PyUnicode_FromString(cpp_traveler->luggage.c_str()); } @@ -54,6 +57,9 @@ extern "C" PyObject *wrapGetPoints(PyObject * /*self*/, PyObject *premium_travel const auto *cpp_premium_traveler = get_cpp_transporter_type_ptr( premium_traveler); + if (cpp_premium_traveler == nullptr) { + return nullptr; + } return PyLong_FromLong(static_cast(cpp_premium_traveler->points)); } From b442699ac72f3a66ccf3d7a93af047cda07754ca Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 30 Aug 2024 13:44:43 -0700 Subject: [PATCH 26/39] Unconditionally add `__cpp_transporter__` method to all `py::class_` objects, but do not include that magic method in docstring signatures. --- include/pybind11/pybind11.h | 7 +++++-- tests/test_cpp_transporter_traveler_bindings.h | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 74919a7d..434eb672 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -610,7 +610,8 @@ class cpp_function : public function { int index = 0; /* Create a nice pydoc rec including all signatures and docstrings of the functions in the overload chain */ - if (chain && options::show_function_signatures()) { + if (chain && options::show_function_signatures() + && strcmp(rec->name, "__cpp_transporter__") != 0) { // First a generic signature signatures += rec->name; signatures += "(*args, **kwargs)\n"; @@ -619,7 +620,8 @@ class cpp_function : public function { // Then specific overload signatures bool first_user_def = true; for (auto *it = chain_start; it != nullptr; it = it->next) { - if (options::show_function_signatures()) { + if (options::show_function_signatures() + && strcmp(rec->name, "__cpp_transporter__") != 0) { if (index > 0) { signatures += '\n'; } @@ -1652,6 +1654,7 @@ class class_ : public detail::generic_type { = instances[std::type_index(typeid(type))]; }); } + def("__cpp_transporter__", class_dunder_cpp_transporter); } template ::value, int> = 0> diff --git a/tests/test_cpp_transporter_traveler_bindings.h b/tests/test_cpp_transporter_traveler_bindings.h index 94fa2483..2ce80ad7 100644 --- a/tests/test_cpp_transporter_traveler_bindings.h +++ b/tests/test_cpp_transporter_traveler_bindings.h @@ -17,7 +17,6 @@ inline void wrap_traveler(py::module_ m) { m.attr("typeid_Traveler_name") = typeid(Traveler).name(); py::class_(m, "Traveler") - .def("__cpp_transporter__", py::detail::class_dunder_cpp_transporter) .def(py::init()) .def_readwrite("luggage", &Traveler::luggage); From e61147100e87493819ad0f4d473aa7b66bbc2763 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 30 Aug 2024 14:21:38 -0700 Subject: [PATCH 27/39] Back out pybind11/detail/platform_abi_id.h for now. Maximizing reusability can be handled separately, later. --- CMakeLists.txt | 1 - include/pybind11/detail/cpp_transporter.h | 1 - include/pybind11/detail/internals.h | 62 ++++++++++++++++++++- include/pybind11/detail/platform_abi_id.h | 68 ----------------------- tests/exo_planet_c_api.cpp | 9 ++- tests/extra_python_package/test_files.py | 1 - 6 files changed, 68 insertions(+), 74 deletions(-) delete mode 100644 include/pybind11/detail/platform_abi_id.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a5283fb..a70eb1e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,7 +133,6 @@ set(PYBIND11_HEADERS include/pybind11/detail/descr.h include/pybind11/detail/init.h include/pybind11/detail/internals.h - include/pybind11/detail/platform_abi_id.h include/pybind11/detail/type_caster_base.h include/pybind11/detail/typeid.h include/pybind11/detail/value_and_holder.h diff --git a/include/pybind11/detail/cpp_transporter.h b/include/pybind11/detail/cpp_transporter.h index d826b885..4bc55b70 100644 --- a/include/pybind11/detail/cpp_transporter.h +++ b/include/pybind11/detail/cpp_transporter.h @@ -6,7 +6,6 @@ #include "common.h" #include "internals.h" -#include "platform_abi_id.h" PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 0051d34d..7cc6f53a 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -10,7 +10,6 @@ #pragma once #include "common.h" -#include "platform_abi_id.h" #if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT) # include @@ -265,6 +264,67 @@ struct type_info { bool module_local : 1; }; +/// On MSVC, debug and release builds are not ABI-compatible! +#if defined(_MSC_VER) && defined(_DEBUG) +# define PYBIND11_BUILD_TYPE "_debug" +#else +# define PYBIND11_BUILD_TYPE "" +#endif + +/// Let's assume that different compilers are ABI-incompatible. +/// A user can manually set this string if they know their +/// compiler is compatible. +#ifndef PYBIND11_COMPILER_TYPE +# if defined(_MSC_VER) +# define PYBIND11_COMPILER_TYPE "_msvc" +# elif defined(__INTEL_COMPILER) +# define PYBIND11_COMPILER_TYPE "_icc" +# elif defined(__clang__) +# define PYBIND11_COMPILER_TYPE "_clang" +# elif defined(__PGI) +# define PYBIND11_COMPILER_TYPE "_pgi" +# elif defined(__MINGW32__) +# define PYBIND11_COMPILER_TYPE "_mingw" +# elif defined(__CYGWIN__) +# define PYBIND11_COMPILER_TYPE "_gcc_cygwin" +# elif defined(__GNUC__) +# define PYBIND11_COMPILER_TYPE "_gcc" +# else +# define PYBIND11_COMPILER_TYPE "_unknown" +# endif +#endif + +/// Also standard libs +#ifndef PYBIND11_STDLIB +# if defined(_LIBCPP_VERSION) +# define PYBIND11_STDLIB "_libcpp" +# elif defined(__GLIBCXX__) || defined(__GLIBCPP__) +# define PYBIND11_STDLIB "_libstdcpp" +# else +# define PYBIND11_STDLIB "" +# endif +#endif + +/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility. +/// On MSVC, changes in _MSC_VER may indicate ABI incompatibility (#2898). +#ifndef PYBIND11_BUILD_ABI +# if defined(__GXX_ABI_VERSION) +# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION) +# elif defined(_MSC_VER) +# define PYBIND11_BUILD_ABI "_mscver" PYBIND11_TOSTRING(_MSC_VER) +# else +# define PYBIND11_BUILD_ABI "" +# endif +#endif + +#ifndef PYBIND11_INTERNALS_KIND +# define PYBIND11_INTERNALS_KIND "" +#endif + +#define PYBIND11_PLATFORM_ABI_ID \ + PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \ + PYBIND11_BUILD_TYPE + #define PYBIND11_INTERNALS_ID \ "__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \ PYBIND11_PLATFORM_ABI_ID "__" diff --git a/include/pybind11/detail/platform_abi_id.h b/include/pybind11/detail/platform_abi_id.h deleted file mode 100644 index 33f18b6c..00000000 --- a/include/pybind11/detail/platform_abi_id.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2024 The pybind Community. - -#pragma once - -// This is needed, unfortunately. -// E.g. on some platforms, `#include ` defines `__GLIBCXX__`. -#include "common.h" - -/// On MSVC, debug and release builds are not ABI-compatible! -#if defined(_MSC_VER) && defined(_DEBUG) -# define PYBIND11_BUILD_TYPE "_debug" -#else -# define PYBIND11_BUILD_TYPE "" -#endif - -/// Let's assume that different compilers are ABI-incompatible. -/// A user can manually set this string if they know their -/// compiler is compatible. -#ifndef PYBIND11_COMPILER_TYPE -# if defined(_MSC_VER) -# define PYBIND11_COMPILER_TYPE "_msvc" -# elif defined(__INTEL_COMPILER) -# define PYBIND11_COMPILER_TYPE "_icc" -# elif defined(__clang__) -# define PYBIND11_COMPILER_TYPE "_clang" -# elif defined(__PGI) -# define PYBIND11_COMPILER_TYPE "_pgi" -# elif defined(__MINGW32__) -# define PYBIND11_COMPILER_TYPE "_mingw" -# elif defined(__CYGWIN__) -# define PYBIND11_COMPILER_TYPE "_gcc_cygwin" -# elif defined(__GNUC__) -# define PYBIND11_COMPILER_TYPE "_gcc" -# else -# define PYBIND11_COMPILER_TYPE "_unknown" -# endif -#endif - -/// Also standard libs -#ifndef PYBIND11_STDLIB -# if defined(_LIBCPP_VERSION) -# define PYBIND11_STDLIB "_libcpp" -# elif defined(__GLIBCXX__) || defined(__GLIBCPP__) -# define PYBIND11_STDLIB "_libstdcpp" -# else -# define PYBIND11_STDLIB "" -# endif -#endif - -/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility. -/// On MSVC, changes in _MSC_VER may indicate ABI incompatibility (#2898). -#ifndef PYBIND11_BUILD_ABI -# if defined(__GXX_ABI_VERSION) -# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION) -# elif defined(_MSC_VER) -# define PYBIND11_BUILD_ABI "_mscver" PYBIND11_TOSTRING(_MSC_VER) -# else -# define PYBIND11_BUILD_ABI "" -# endif -#endif - -#ifndef PYBIND11_INTERNALS_KIND -# define PYBIND11_INTERNALS_KIND "" -#endif - -#define PYBIND11_PLATFORM_ABI_ID \ - PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \ - PYBIND11_BUILD_TYPE diff --git a/tests/exo_planet_c_api.cpp b/tests/exo_planet_c_api.cpp index 17dc5768..c6ce9113 100644 --- a/tests/exo_planet_c_api.cpp +++ b/tests/exo_planet_c_api.cpp @@ -1,6 +1,11 @@ // THIS MUST STAY AT THE TOP! -// DO NOT USE ANY OTHER pybind11 HEADERS HERE! -#include +#include // EXCLUSIVELY for PYBIND11_PLATFORM_ABI_ID +// Potential future direction to maximize reusability: +// (e.g. for use from SWIG, Cython, PyCLIF, nanobind): +// #include +// This would only depend on: +// 1. A C++ compiler, WITHOUT requiring -fexceptions. +// 2. Python.h #include "test_cpp_transporter_traveler_types.h" diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index 386c1db8..5f249db8 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -57,7 +57,6 @@ "include/pybind11/detail/descr.h", "include/pybind11/detail/init.h", "include/pybind11/detail/internals.h", - "include/pybind11/detail/platform_abi_id.h", "include/pybind11/detail/type_caster_base.h", "include/pybind11/detail/typeid.h", "include/pybind11/detail/value_and_holder.h", From 0a97c979083dbcd4cc98409ebd862a9cd179eef0 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 30 Aug 2024 14:36:33 -0700 Subject: [PATCH 28/39] Small cleanup. --- include/pybind11/detail/type_caster_base.h | 2 ++ tests/test_cpp_transporter.cpp | 13 ++++++++++++- tests/test_cpp_transporter_traveler_bindings.h | 4 ---- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index 0ab82d07..4e40b3b2 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -19,6 +19,8 @@ #include "value_and_holder.h" #include +#include +#include #include #include #include diff --git a/tests/test_cpp_transporter.cpp b/tests/test_cpp_transporter.cpp index d13e649e..8bffae6a 100644 --- a/tests/test_cpp_transporter.cpp +++ b/tests/test_cpp_transporter.cpp @@ -1,4 +1,15 @@ #include "pybind11_tests.h" #include "test_cpp_transporter_traveler_bindings.h" -TEST_SUBMODULE(cpp_transporter, m) { pybind11_tests::test_cpp_transporter::wrap_traveler(m); } +namespace pybind11_tests { +namespace test_cpp_transporter { + +TEST_SUBMODULE(cpp_transporter, m) { + m.attr("PYBIND11_PLATFORM_ABI_ID") = PYBIND11_PLATFORM_ABI_ID; + m.attr("typeid_Traveler_name") = typeid(Traveler).name(); + + wrap_traveler(m); +} + +} // namespace test_cpp_transporter +} // namespace pybind11_tests diff --git a/tests/test_cpp_transporter_traveler_bindings.h b/tests/test_cpp_transporter_traveler_bindings.h index 2ce80ad7..16244ef8 100644 --- a/tests/test_cpp_transporter_traveler_bindings.h +++ b/tests/test_cpp_transporter_traveler_bindings.h @@ -4,7 +4,6 @@ #include "test_cpp_transporter_traveler_types.h" -#include #include namespace pybind11_tests { @@ -13,9 +12,6 @@ namespace test_cpp_transporter { namespace py = pybind11; inline void wrap_traveler(py::module_ m) { - m.attr("PYBIND11_PLATFORM_ABI_ID") = PYBIND11_PLATFORM_ABI_ID; - m.attr("typeid_Traveler_name") = typeid(Traveler).name(); - py::class_(m, "Traveler") .def(py::init()) .def_readwrite("luggage", &Traveler::luggage); From 8a27b98a949ef81a50bc660a037b77dde5caf136 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 30 Aug 2024 15:01:35 -0700 Subject: [PATCH 29/39] Restore and add to `test_call_cpp_transporter_*()` --- include/pybind11/detail/type_caster_base.h | 2 +- tests/test_cpp_transporter.cpp | 6 +++- tests/test_cpp_transporter.py | 36 +++++++++++----------- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index 4e40b3b2..da66648b 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -795,7 +795,7 @@ inline object class_dunder_cpp_transporter(handle self, return none(); } if (pointer_kind_cpp != "raw_pointer_ephemeral") { - throw std::runtime_error("Unknown pointer_kind: \"" + pointer_kind_cpp + "\""); + throw std::runtime_error("Invalid pointer_kind: \"" + pointer_kind_cpp + "\""); } const auto *cpp_type_info = cap_cpp_type_info.get_pointer(); type_caster_generic caster(*cpp_type_info); diff --git a/tests/test_cpp_transporter.cpp b/tests/test_cpp_transporter.cpp index 8bffae6a..8b1fc678 100644 --- a/tests/test_cpp_transporter.cpp +++ b/tests/test_cpp_transporter.cpp @@ -1,12 +1,16 @@ #include "pybind11_tests.h" #include "test_cpp_transporter_traveler_bindings.h" +#include + namespace pybind11_tests { namespace test_cpp_transporter { TEST_SUBMODULE(cpp_transporter, m) { m.attr("PYBIND11_PLATFORM_ABI_ID") = PYBIND11_PLATFORM_ABI_ID; - m.attr("typeid_Traveler_name") = typeid(Traveler).name(); + m.attr("cap_cpp_type_info_Traveler") + = py::capsule(&typeid(Traveler), "const std::type_info *"); + m.attr("cap_cpp_type_info_int") = py::capsule(&typeid(int), "const std::type_info *"); wrap_traveler(m); } diff --git a/tests/test_cpp_transporter.py b/tests/test_cpp_transporter.py index d4abde02..0f3ea6a0 100644 --- a/tests/test_cpp_transporter.py +++ b/tests/test_cpp_transporter.py @@ -7,46 +7,46 @@ from pybind11_tests import cpp_transporter as home_planet -def NOtest_call_cpp_transporter_success(): +def test_call_cpp_transporter_success(): t_h = home_planet.Traveler("home") cap = t_h.__cpp_transporter__( home_planet.PYBIND11_PLATFORM_ABI_ID, - home_planet.typeid_Traveler_name, + home_planet.cap_cpp_type_info_Traveler, "raw_pointer_ephemeral", ) assert cap.__class__.__name__ == "PyCapsule" -def NOtest_call_cpp_transporter_platform_abi_id_mismatch(): +def test_call_cpp_transporter_platform_abi_id_mismatch(): t_h = home_planet.Traveler("home") cap = t_h.__cpp_transporter__( home_planet.PYBIND11_PLATFORM_ABI_ID + "MISMATCH", - home_planet.typeid_Traveler_name, + home_planet.cap_cpp_type_info_Traveler, "raw_pointer_ephemeral", ) assert cap is None - diag = t_h.__cpp_transporter__( - home_planet.PYBIND11_PLATFORM_ABI_ID + "MISMATCH", - home_planet.typeid_Traveler_name, - "query_mismatch", - ) - assert diag == "pybind11_platform_abi_id_mismatch" -def NOtest_call_cpp_transporter_type_id_name_mismatch(): +def test_call_cpp_transporter_cap_cpp_type_info_mismatch(): t_h = home_planet.Traveler("home") cap = t_h.__cpp_transporter__( home_planet.PYBIND11_PLATFORM_ABI_ID, - home_planet.typeid_Traveler_name + "MISMATCH", + home_planet.cap_cpp_type_info_int, "raw_pointer_ephemeral", ) assert cap is None - diag = t_h.__cpp_transporter__( - home_planet.PYBIND11_PLATFORM_ABI_ID, - home_planet.typeid_Traveler_name + "MISMATCH", - "query_mismatch", - ) - assert diag == "cpp_typeid_name_mismatch" + + +def test_call_cpp_transporter_pointer_kind_invalid(): + t_h = home_planet.Traveler("home") + with pytest.raises( + RuntimeError, match='^Invalid pointer_kind: "raw_pointer_ephemreal"$' + ): + t_h.__cpp_transporter__( + home_planet.PYBIND11_PLATFORM_ABI_ID, + home_planet.cap_cpp_type_info_Traveler, + "raw_pointer_ephemreal", + ) def test_home_only_basic(): From 80550a9e7c14f70b8371442ac5b85eefb9bd6e2c Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 30 Aug 2024 15:41:53 -0700 Subject: [PATCH 30/39] Ensure https://github.com/pybind/pybind11/issues/3788 does not bite again. --- tests/test_cpp_transporter.py | 10 ++++++++++ tests/test_cpp_transporter_traveler_bindings.h | 12 ++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/test_cpp_transporter.py b/tests/test_cpp_transporter.py index 0f3ea6a0..ae1c0c1b 100644 --- a/tests/test_cpp_transporter.py +++ b/tests/test_cpp_transporter.py @@ -7,6 +7,16 @@ from pybind11_tests import cpp_transporter 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_transporter_success(): t_h = home_planet.Traveler("home") cap = t_h.__cpp_transporter__( diff --git a/tests/test_cpp_transporter_traveler_bindings.h b/tests/test_cpp_transporter_traveler_bindings.h index 16244ef8..a295c5bb 100644 --- a/tests/test_cpp_transporter_traveler_bindings.h +++ b/tests/test_cpp_transporter_traveler_bindings.h @@ -14,13 +14,21 @@ namespace py = pybind11; inline void wrap_traveler(py::module_ m) { py::class_(m, "Traveler") .def(py::init()) - .def_readwrite("luggage", &Traveler::luggage); + .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_(m, "PremiumTraveler") .def(py::init()) - .def_readwrite("points", &PremiumTraveler::points); + .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; }); } From 80ca6839978a32705ee0f2ecbdb5f6bf3ba30b82 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 30 Aug 2024 15:59:39 -0700 Subject: [PATCH 31/39] `class_dunder_cpp_transporter()`: replace `obj.cast()` with `std::string(obj)` --- include/pybind11/detail/type_caster_base.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index da66648b..ac210ac7 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -787,13 +787,13 @@ inline object class_dunder_cpp_transporter(handle self, const str &pybind11_platform_abi_id, const capsule &cap_cpp_type_info, const str &pointer_kind) { - auto pointer_kind_cpp = pointer_kind.cast(); - if (pybind11_platform_abi_id.cast() != PYBIND11_PLATFORM_ABI_ID) { + if (std::string(pybind11_platform_abi_id) != PYBIND11_PLATFORM_ABI_ID) { return none(); } if (std::strcmp(cap_cpp_type_info.name(), "const std::type_info *") != 0) { return none(); } + std::string pointer_kind_cpp(pointer_kind); if (pointer_kind_cpp != "raw_pointer_ephemeral") { throw std::runtime_error("Invalid pointer_kind: \"" + pointer_kind_cpp + "\""); } From 9cffdc93be107ffa4b0cf8014d963bc35e175aad Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 30 Aug 2024 16:04:56 -0700 Subject: [PATCH 32/39] Add (simple) copyright notices in all newly added files. --- tests/exo_planet_c_api.cpp | 2 ++ tests/exo_planet_pybind11.cpp | 2 ++ tests/test_cpp_transporter.cpp | 2 ++ tests/test_cpp_transporter.py | 2 ++ tests/test_cpp_transporter_traveler_bindings.h | 2 ++ tests/test_cpp_transporter_traveler_types.h | 2 ++ 6 files changed, 12 insertions(+) diff --git a/tests/exo_planet_c_api.cpp b/tests/exo_planet_c_api.cpp index c6ce9113..32197d17 100644 --- a/tests/exo_planet_c_api.cpp +++ b/tests/exo_planet_c_api.cpp @@ -1,3 +1,5 @@ +// Copyright (c) 2024 The pybind Community. + // THIS MUST STAY AT THE TOP! #include // EXCLUSIVELY for PYBIND11_PLATFORM_ABI_ID // Potential future direction to maximize reusability: diff --git a/tests/exo_planet_pybind11.cpp b/tests/exo_planet_pybind11.cpp index 1556d58c..4fa2983e 100644 --- a/tests/exo_planet_pybind11.cpp +++ b/tests/exo_planet_pybind11.cpp @@ -1,3 +1,5 @@ +// Copyright (c) 2024 The pybind Community. + #if defined(PYBIND11_INTERNALS_VERSION) # undef PYBIND11_INTERNALS_VERSION #endif diff --git a/tests/test_cpp_transporter.cpp b/tests/test_cpp_transporter.cpp index 8b1fc678..ac6d6977 100644 --- a/tests/test_cpp_transporter.cpp +++ b/tests/test_cpp_transporter.cpp @@ -1,3 +1,5 @@ +// Copyright (c) 2024 The pybind Community. + #include "pybind11_tests.h" #include "test_cpp_transporter_traveler_bindings.h" diff --git a/tests/test_cpp_transporter.py b/tests/test_cpp_transporter.py index ae1c0c1b..bcb4d884 100644 --- a/tests/test_cpp_transporter.py +++ b/tests/test_cpp_transporter.py @@ -1,3 +1,5 @@ +# Copyright (c) 2024 The pybind Community. + from __future__ import annotations import exo_planet_c_api diff --git a/tests/test_cpp_transporter_traveler_bindings.h b/tests/test_cpp_transporter_traveler_bindings.h index a295c5bb..fb4c1286 100644 --- a/tests/test_cpp_transporter_traveler_bindings.h +++ b/tests/test_cpp_transporter_traveler_bindings.h @@ -1,3 +1,5 @@ +// Copyright (c) 2024 The pybind Community. + #pragma once #include diff --git a/tests/test_cpp_transporter_traveler_types.h b/tests/test_cpp_transporter_traveler_types.h index 1f737102..6e6ba3a4 100644 --- a/tests/test_cpp_transporter_traveler_types.h +++ b/tests/test_cpp_transporter_traveler_types.h @@ -1,3 +1,5 @@ +// Copyright (c) 2024 The pybind Community. + #pragma once #include From ce45db1c951b6e48e9a8591a223008989873b708 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 30 Aug 2024 16:17:16 -0700 Subject: [PATCH 33/39] Globally replace cpp_transporter with cpp_conduit --- CMakeLists.txt | 2 +- .../{cpp_transporter.h => cpp_conduit.h} | 21 ++++++-------- include/pybind11/detail/type_caster_base.h | 16 +++++------ include/pybind11/pybind11.h | 6 ++-- tests/CMakeLists.txt | 4 +-- tests/exo_planet_c_api.cpp | 28 +++++++------------ tests/exo_planet_pybind11.cpp | 4 +-- tests/extra_python_package/test_files.py | 2 +- ...p_transporter.cpp => test_cpp_conduit.cpp} | 8 +++--- ...cpp_transporter.py => test_cpp_conduit.py} | 18 ++++++------ ...h => test_cpp_conduit_traveler_bindings.h} | 6 ++-- ...es.h => test_cpp_conduit_traveler_types.h} | 4 +-- 12 files changed, 54 insertions(+), 65 deletions(-) rename include/pybind11/detail/{cpp_transporter.h => cpp_conduit.h} (71%) rename tests/{test_cpp_transporter.cpp => test_cpp_conduit.cpp} (73%) rename tests/{test_cpp_transporter.py => test_cpp_conduit.py} (89%) rename tests/{test_cpp_transporter_traveler_bindings.h => test_cpp_conduit_traveler_bindings.h} (90%) rename tests/{test_cpp_transporter_traveler_types.h => test_cpp_conduit_traveler_types.h} (86%) diff --git a/CMakeLists.txt b/CMakeLists.txt index a70eb1e4..17630cf8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -129,7 +129,7 @@ endif() set(PYBIND11_HEADERS include/pybind11/detail/class.h include/pybind11/detail/common.h - include/pybind11/detail/cpp_transporter.h + include/pybind11/detail/cpp_conduit.h include/pybind11/detail/descr.h include/pybind11/detail/init.h include/pybind11/detail/internals.h diff --git a/include/pybind11/detail/cpp_transporter.h b/include/pybind11/detail/cpp_conduit.h similarity index 71% rename from include/pybind11/detail/cpp_transporter.h rename to include/pybind11/detail/cpp_conduit.h index 4bc55b70..1b41c156 100644 --- a/include/pybind11/detail/cpp_transporter.h +++ b/include/pybind11/detail/cpp_conduit.h @@ -28,12 +28,12 @@ inline bool is_instance_method_of_type(PyTypeObject *type_obj, PyObject *attr_na return bool((descr != nullptr) && PyInstanceMethod_Check(descr)); } -inline object try_get_cpp_transporter_method(PyObject *obj) { +inline object try_get_cpp_conduit_method(PyObject *obj) { if (PyType_Check(obj)) { return object(); } PyTypeObject *type_obj = Py_TYPE(obj); - str attr_name("__cpp_transporter__"); + str attr_name("__cpp_conduit__"); 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())) { @@ -53,22 +53,19 @@ inline object try_get_cpp_transporter_method(PyObject *obj) { return reinterpret_steal(method); } -inline void *try_raw_pointer_ephemeral_from_cpp_transporter(handle src, - const std::type_info *cpp_type_info) { - object method = try_get_cpp_transporter_method(src.ptr()); +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 cap_cpp_type_info(const_cast(static_cast(cpp_type_info)), - "const std::type_info *"); - object cpp_transporter - = method(PYBIND11_PLATFORM_ABI_ID, cap_cpp_type_info, "raw_pointer_ephemeral"); - if (isinstance(cpp_transporter)) { - return reinterpret_borrow(cpp_transporter).get_pointer(); + capsule cap_cpp_type_info(const_cast(static_cast(cpp_type_info)), "const std::type_info *"); + object cpp_conduit = method(PYBIND11_PLATFORM_ABI_ID, cap_cpp_type_info, "raw_pointer_ephemeral"); + if (isinstance(cpp_conduit)) { + return reinterpret_borrow(cpp_conduit).get_pointer(); } } return nullptr; } -#define PYBIND11_HAS_CPP_TRANSPORTER +#define PYBIND11_HAS_CPP_CONDUIT PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index ac210ac7..5086ce16 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -12,7 +12,7 @@ #include #include "common.h" -#include "cpp_transporter.h" +#include "cpp_conduit.h" #include "descr.h" #include "internals.h" #include "typeid.h" @@ -614,8 +614,8 @@ class type_caster_generic { } return false; } - bool try_cpp_transporter(handle src) { - value = try_raw_pointer_ephemeral_from_cpp_transporter(src, cpptype); + bool try_cpp_conduit(handle src) { + value = try_raw_pointer_ephemeral_from_cpp_conduit(src, cpptype); if (value != nullptr) { return true; } @@ -752,7 +752,7 @@ class type_caster_generic { return true; } - if (convert && cpptype && this_.try_cpp_transporter(src)) { + if (convert && cpptype && this_.try_cpp_conduit(src)) { return true; } @@ -783,10 +783,10 @@ class type_caster_generic { void *value = nullptr; }; -inline object class_dunder_cpp_transporter(handle self, - const str &pybind11_platform_abi_id, - const capsule &cap_cpp_type_info, - const str &pointer_kind) { +inline object class_dunder_cpp_conduit(handle self, + const str &pybind11_platform_abi_id, + const capsule &cap_cpp_type_info, + const str &pointer_kind) { if (std::string(pybind11_platform_abi_id) != PYBIND11_PLATFORM_ABI_ID) { return none(); } diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 434eb672..194e3dbc 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -611,7 +611,7 @@ class cpp_function : public function { /* Create a nice pydoc rec including all signatures and docstrings of the functions in the overload chain */ if (chain && options::show_function_signatures() - && strcmp(rec->name, "__cpp_transporter__") != 0) { + && strcmp(rec->name, "__cpp_conduit__") != 0) { // First a generic signature signatures += rec->name; signatures += "(*args, **kwargs)\n"; @@ -621,7 +621,7 @@ class cpp_function : public function { bool first_user_def = true; for (auto *it = chain_start; it != nullptr; it = it->next) { if (options::show_function_signatures() - && strcmp(rec->name, "__cpp_transporter__") != 0) { + && strcmp(rec->name, "__cpp_conduit__") != 0) { if (index > 0) { signatures += '\n'; } @@ -1654,7 +1654,7 @@ class class_ : public detail::generic_type { = instances[std::type_index(typeid(type))]; }); } - def("__cpp_transporter__", class_dunder_cpp_transporter); + def("__cpp_conduit__", class_dunder_cpp_conduit); } template ::value, int> = 0> diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 40b1bd5d..45a1dcbe 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -148,7 +148,7 @@ set(PYBIND11_TEST_FILES test_stl_binders test_tagbased_polymorphic test_thread - test_cpp_transporter + test_cpp_conduit test_type_caster_pyobject_ptr test_type_caster_std_function_specializations test_union @@ -219,7 +219,7 @@ tests_extra_targets("test_exceptions.py;test_local_bindings.py;test_stl.py;test_ # And add additional targets for other tests. 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_cpp_transporter.py" "exo_planet_pybind11;exo_planet_c_api") +tests_extra_targets("test_cpp_conduit.py" "exo_planet_pybind11;exo_planet_c_api") set(PYBIND11_EIGEN_REPO "https://gitlab.com/libeigen/eigen.git" diff --git a/tests/exo_planet_c_api.cpp b/tests/exo_planet_c_api.cpp index 32197d17..894322c7 100644 --- a/tests/exo_planet_c_api.cpp +++ b/tests/exo_planet_c_api.cpp @@ -9,14 +9,14 @@ // 1. A C++ compiler, WITHOUT requiring -fexceptions. // 2. Python.h -#include "test_cpp_transporter_traveler_types.h" +#include "test_cpp_conduit_traveler_types.h" #include #include namespace { -void *get_cpp_transporter_void_ptr(PyObject *py_obj, const std::type_info *cpp_type_info) { +void *get_cpp_conduit_void_ptr(PyObject *py_obj, const std::type_info *cpp_type_info) { PyObject *cap_cpp_type_info = PyCapsule_New(const_cast(static_cast(cpp_type_info)), "const std::type_info *", @@ -24,18 +24,13 @@ void *get_cpp_transporter_void_ptr(PyObject *py_obj, const std::type_info *cpp_t if (cap_cpp_type_info == nullptr) { return nullptr; } - PyObject *cpp_transporter = PyObject_CallMethod(py_obj, - "__cpp_transporter__", - "sOs", - PYBIND11_PLATFORM_ABI_ID, - cap_cpp_type_info, - "raw_pointer_ephemeral"); + PyObject *cpp_conduit = PyObject_CallMethod(py_obj, "__cpp_conduit__", "sOs", PYBIND11_PLATFORM_ABI_ID, cap_cpp_type_info, "raw_pointer_ephemeral"); Py_DECREF(cap_cpp_type_info); - if (cpp_transporter == nullptr) { + if (cpp_conduit == nullptr) { return nullptr; } - void *void_ptr = PyCapsule_GetPointer(cpp_transporter, cpp_type_info->name()); - Py_DECREF(cpp_transporter); + void *void_ptr = PyCapsule_GetPointer(cpp_conduit, cpp_type_info->name()); + Py_DECREF(cpp_conduit); if (PyErr_Occurred()) { return nullptr; } @@ -43,8 +38,8 @@ void *get_cpp_transporter_void_ptr(PyObject *py_obj, const std::type_info *cpp_t } template -T *get_cpp_transporter_type_ptr(PyObject *py_obj) { - void *void_ptr = get_cpp_transporter_void_ptr(py_obj, &typeid(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; } @@ -52,8 +47,7 @@ T *get_cpp_transporter_type_ptr(PyObject *py_obj) { } extern "C" PyObject *wrapGetLuggage(PyObject * /*self*/, PyObject *traveler) { - const auto *cpp_traveler - = get_cpp_transporter_type_ptr(traveler); + const auto *cpp_traveler = get_cpp_conduit_type_ptr(traveler); if (cpp_traveler == nullptr) { return nullptr; } @@ -61,9 +55,7 @@ extern "C" PyObject *wrapGetLuggage(PyObject * /*self*/, PyObject *traveler) { } extern "C" PyObject *wrapGetPoints(PyObject * /*self*/, PyObject *premium_traveler) { - const auto *cpp_premium_traveler - = get_cpp_transporter_type_ptr( - premium_traveler); + const auto *cpp_premium_traveler = get_cpp_conduit_type_ptr(premium_traveler); if (cpp_premium_traveler == nullptr) { return nullptr; } diff --git a/tests/exo_planet_pybind11.cpp b/tests/exo_planet_pybind11.cpp index 4fa2983e..5e6a8d06 100644 --- a/tests/exo_planet_pybind11.cpp +++ b/tests/exo_planet_pybind11.cpp @@ -5,6 +5,6 @@ #endif #define PYBIND11_INTERNALS_VERSION 900000001 -#include "test_cpp_transporter_traveler_bindings.h" +#include "test_cpp_conduit_traveler_bindings.h" -PYBIND11_MODULE(exo_planet_pybind11, m) { pybind11_tests::test_cpp_transporter::wrap_traveler(m); } +PYBIND11_MODULE(exo_planet_pybind11, m) { pybind11_tests::test_cpp_conduit::wrap_traveler(m); } diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index 5f249db8..b8521935 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -53,7 +53,7 @@ detail_headers = { "include/pybind11/detail/class.h", "include/pybind11/detail/common.h", - "include/pybind11/detail/cpp_transporter.h", + "include/pybind11/detail/cpp_conduit.h", "include/pybind11/detail/descr.h", "include/pybind11/detail/init.h", "include/pybind11/detail/internals.h", diff --git a/tests/test_cpp_transporter.cpp b/tests/test_cpp_conduit.cpp similarity index 73% rename from tests/test_cpp_transporter.cpp rename to tests/test_cpp_conduit.cpp index ac6d6977..d0a480bf 100644 --- a/tests/test_cpp_transporter.cpp +++ b/tests/test_cpp_conduit.cpp @@ -1,14 +1,14 @@ // Copyright (c) 2024 The pybind Community. #include "pybind11_tests.h" -#include "test_cpp_transporter_traveler_bindings.h" +#include "test_cpp_conduit_traveler_bindings.h" #include namespace pybind11_tests { -namespace test_cpp_transporter { +namespace test_cpp_conduit { -TEST_SUBMODULE(cpp_transporter, m) { +TEST_SUBMODULE(cpp_conduit, m) { m.attr("PYBIND11_PLATFORM_ABI_ID") = PYBIND11_PLATFORM_ABI_ID; m.attr("cap_cpp_type_info_Traveler") = py::capsule(&typeid(Traveler), "const std::type_info *"); @@ -17,5 +17,5 @@ TEST_SUBMODULE(cpp_transporter, m) { wrap_traveler(m); } -} // namespace test_cpp_transporter +} // namespace test_cpp_conduit } // namespace pybind11_tests diff --git a/tests/test_cpp_transporter.py b/tests/test_cpp_conduit.py similarity index 89% rename from tests/test_cpp_transporter.py rename to tests/test_cpp_conduit.py index bcb4d884..e6300825 100644 --- a/tests/test_cpp_transporter.py +++ b/tests/test_cpp_conduit.py @@ -6,7 +6,7 @@ import exo_planet_pybind11 import pytest -from pybind11_tests import cpp_transporter as home_planet +from pybind11_tests import cpp_conduit as home_planet def test_traveler_getattr_actually_exists(): @@ -19,9 +19,9 @@ def test_premium_traveler_getattr_actually_exists(): assert t_h.secret_name == "PremiumTraveler GetAttr: secret_name points: 7" -def test_call_cpp_transporter_success(): +def test_call_cpp_conduit_success(): t_h = home_planet.Traveler("home") - cap = t_h.__cpp_transporter__( + cap = t_h.__cpp_conduit__( home_planet.PYBIND11_PLATFORM_ABI_ID, home_planet.cap_cpp_type_info_Traveler, "raw_pointer_ephemeral", @@ -29,9 +29,9 @@ def test_call_cpp_transporter_success(): assert cap.__class__.__name__ == "PyCapsule" -def test_call_cpp_transporter_platform_abi_id_mismatch(): +def test_call_cpp_conduit_platform_abi_id_mismatch(): t_h = home_planet.Traveler("home") - cap = t_h.__cpp_transporter__( + cap = t_h.__cpp_conduit__( home_planet.PYBIND11_PLATFORM_ABI_ID + "MISMATCH", home_planet.cap_cpp_type_info_Traveler, "raw_pointer_ephemeral", @@ -39,9 +39,9 @@ def test_call_cpp_transporter_platform_abi_id_mismatch(): assert cap is None -def test_call_cpp_transporter_cap_cpp_type_info_mismatch(): +def test_call_cpp_conduit_cap_cpp_type_info_mismatch(): t_h = home_planet.Traveler("home") - cap = t_h.__cpp_transporter__( + cap = t_h.__cpp_conduit__( home_planet.PYBIND11_PLATFORM_ABI_ID, home_planet.cap_cpp_type_info_int, "raw_pointer_ephemeral", @@ -49,12 +49,12 @@ def test_call_cpp_transporter_cap_cpp_type_info_mismatch(): assert cap is None -def test_call_cpp_transporter_pointer_kind_invalid(): +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.__cpp_transporter__( + t_h.__cpp_conduit__( home_planet.PYBIND11_PLATFORM_ABI_ID, home_planet.cap_cpp_type_info_Traveler, "raw_pointer_ephemreal", diff --git a/tests/test_cpp_transporter_traveler_bindings.h b/tests/test_cpp_conduit_traveler_bindings.h similarity index 90% rename from tests/test_cpp_transporter_traveler_bindings.h rename to tests/test_cpp_conduit_traveler_bindings.h index fb4c1286..a6c6c521 100644 --- a/tests/test_cpp_transporter_traveler_bindings.h +++ b/tests/test_cpp_conduit_traveler_bindings.h @@ -4,12 +4,12 @@ #include -#include "test_cpp_transporter_traveler_types.h" +#include "test_cpp_conduit_traveler_types.h" #include namespace pybind11_tests { -namespace test_cpp_transporter { +namespace test_cpp_conduit { namespace py = pybind11; @@ -35,5 +35,5 @@ inline void wrap_traveler(py::module_ m) { m.def("get_points", [](const PremiumTraveler &person) { return person.points; }); } -} // namespace test_cpp_transporter +} // namespace test_cpp_conduit } // namespace pybind11_tests diff --git a/tests/test_cpp_transporter_traveler_types.h b/tests/test_cpp_conduit_traveler_types.h similarity index 86% rename from tests/test_cpp_transporter_traveler_types.h rename to tests/test_cpp_conduit_traveler_types.h index 6e6ba3a4..14768dc6 100644 --- a/tests/test_cpp_transporter_traveler_types.h +++ b/tests/test_cpp_conduit_traveler_types.h @@ -5,7 +5,7 @@ #include namespace pybind11_tests { -namespace test_cpp_transporter { +namespace test_cpp_conduit { struct Traveler { explicit Traveler(const std::string &luggage) : luggage(luggage) {} @@ -18,5 +18,5 @@ struct PremiumTraveler : Traveler { int points; }; -} // namespace test_cpp_transporter +} // namespace test_cpp_conduit } // namespace pybind11_tests From 4b77d6cc8f647b71a06b826e1dedf57b8cf0f311 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 23:17:49 +0000 Subject: [PATCH 34/39] style: pre-commit fixes --- include/pybind11/detail/cpp_conduit.h | 9 ++++++--- include/pybind11/pybind11.h | 3 +-- tests/exo_planet_c_api.cpp | 14 +++++++++++--- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/include/pybind11/detail/cpp_conduit.h b/include/pybind11/detail/cpp_conduit.h index 1b41c156..dc31cc48 100644 --- a/include/pybind11/detail/cpp_conduit.h +++ b/include/pybind11/detail/cpp_conduit.h @@ -53,11 +53,14 @@ inline object try_get_cpp_conduit_method(PyObject *obj) { return reinterpret_steal(method); } -inline void *try_raw_pointer_ephemeral_from_cpp_conduit(handle src, const std::type_info *cpp_type_info) { +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 cap_cpp_type_info(const_cast(static_cast(cpp_type_info)), "const std::type_info *"); - object cpp_conduit = method(PYBIND11_PLATFORM_ABI_ID, cap_cpp_type_info, "raw_pointer_ephemeral"); + capsule cap_cpp_type_info(const_cast(static_cast(cpp_type_info)), + "const std::type_info *"); + object cpp_conduit + = method(PYBIND11_PLATFORM_ABI_ID, cap_cpp_type_info, "raw_pointer_ephemeral"); if (isinstance(cpp_conduit)) { return reinterpret_borrow(cpp_conduit).get_pointer(); } diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 194e3dbc..9c5276aa 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -620,8 +620,7 @@ class cpp_function : public function { // Then specific overload signatures bool first_user_def = true; for (auto *it = chain_start; it != nullptr; it = it->next) { - if (options::show_function_signatures() - && strcmp(rec->name, "__cpp_conduit__") != 0) { + if (options::show_function_signatures() && strcmp(rec->name, "__cpp_conduit__") != 0) { if (index > 0) { signatures += '\n'; } diff --git a/tests/exo_planet_c_api.cpp b/tests/exo_planet_c_api.cpp index 894322c7..008bc6d2 100644 --- a/tests/exo_planet_c_api.cpp +++ b/tests/exo_planet_c_api.cpp @@ -24,7 +24,12 @@ void *get_cpp_conduit_void_ptr(PyObject *py_obj, const std::type_info *cpp_type_ if (cap_cpp_type_info == nullptr) { return nullptr; } - PyObject *cpp_conduit = PyObject_CallMethod(py_obj, "__cpp_conduit__", "sOs", PYBIND11_PLATFORM_ABI_ID, cap_cpp_type_info, "raw_pointer_ephemeral"); + PyObject *cpp_conduit = PyObject_CallMethod(py_obj, + "__cpp_conduit__", + "sOs", + PYBIND11_PLATFORM_ABI_ID, + cap_cpp_type_info, + "raw_pointer_ephemeral"); Py_DECREF(cap_cpp_type_info); if (cpp_conduit == nullptr) { return nullptr; @@ -47,7 +52,8 @@ T *get_cpp_conduit_type_ptr(PyObject *py_obj) { } extern "C" PyObject *wrapGetLuggage(PyObject * /*self*/, PyObject *traveler) { - const auto *cpp_traveler = get_cpp_conduit_type_ptr(traveler); + const auto *cpp_traveler + = get_cpp_conduit_type_ptr(traveler); if (cpp_traveler == nullptr) { return nullptr; } @@ -55,7 +61,9 @@ extern "C" PyObject *wrapGetLuggage(PyObject * /*self*/, PyObject *traveler) { } extern "C" PyObject *wrapGetPoints(PyObject * /*self*/, PyObject *premium_traveler) { - const auto *cpp_premium_traveler = get_cpp_conduit_type_ptr(premium_traveler); + const auto *cpp_premium_traveler + = get_cpp_conduit_type_ptr( + premium_traveler); if (cpp_premium_traveler == nullptr) { return nullptr; } From 1bfb369a3157329e22cd5b234db4cdaca50d53b9 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sat, 31 Aug 2024 00:19:25 -0700 Subject: [PATCH 35/39] IWYU fixes --- include/pybind11/detail/cpp_conduit.h | 2 ++ include/pybind11/detail/type_caster_base.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/pybind11/detail/cpp_conduit.h b/include/pybind11/detail/cpp_conduit.h index dc31cc48..ff0e9ee3 100644 --- a/include/pybind11/detail/cpp_conduit.h +++ b/include/pybind11/detail/cpp_conduit.h @@ -7,6 +7,8 @@ #include "common.h" #include "internals.h" +#include + PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index 5086ce16..3115459a 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -20,9 +20,9 @@ #include #include -#include #include #include +#include #include #include #include From afb30a6170a43384db6f2bca81a9003f35602c42 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sat, 31 Aug 2024 09:01:56 -0700 Subject: [PATCH 36/39] Rename `class_dunder_cpp_conduit()` -> `cpp_conduit_method()` --- include/pybind11/detail/type_caster_base.h | 8 ++++---- include/pybind11/pybind11.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index 3115459a..a0a80c62 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -783,10 +783,10 @@ class type_caster_generic { void *value = nullptr; }; -inline object class_dunder_cpp_conduit(handle self, - const str &pybind11_platform_abi_id, - const capsule &cap_cpp_type_info, - const str &pointer_kind) { +inline object cpp_conduit_method(handle self, + const str &pybind11_platform_abi_id, + const capsule &cap_cpp_type_info, + const str &pointer_kind) { if (std::string(pybind11_platform_abi_id) != PYBIND11_PLATFORM_ABI_ID) { return none(); } diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 9c5276aa..8401ba2d 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1653,7 +1653,7 @@ class class_ : public detail::generic_type { = instances[std::type_index(typeid(type))]; }); } - def("__cpp_conduit__", class_dunder_cpp_conduit); + def("__cpp_conduit__", cpp_conduit_method); } template ::value, int> = 0> From bae99590321afde3fe743a7372b85e9923de6af3 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sat, 31 Aug 2024 09:27:27 -0700 Subject: [PATCH 37/39] Change `pybind11_platform_abi_id`, `pointer_kind` argument types from `str` to `bytes`. This avoids the unicode decode/encode roundtrips: * More robust (no decode/encode errors). * Minor runtime optimization. --- include/pybind11/detail/cpp_conduit.h | 4 ++-- include/pybind11/detail/type_caster_base.h | 16 ++++++++++------ tests/exo_planet_c_api.cpp | 2 +- tests/test_cpp_conduit.cpp | 2 +- tests/test_cpp_conduit.py | 10 +++++----- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/include/pybind11/detail/cpp_conduit.h b/include/pybind11/detail/cpp_conduit.h index ff0e9ee3..aa207acd 100644 --- a/include/pybind11/detail/cpp_conduit.h +++ b/include/pybind11/detail/cpp_conduit.h @@ -61,8 +61,8 @@ inline void *try_raw_pointer_ephemeral_from_cpp_conduit(handle src, if (method) { capsule cap_cpp_type_info(const_cast(static_cast(cpp_type_info)), "const std::type_info *"); - object cpp_conduit - = method(PYBIND11_PLATFORM_ABI_ID, cap_cpp_type_info, "raw_pointer_ephemeral"); + object cpp_conduit = method( + bytes(PYBIND11_PLATFORM_ABI_ID), cap_cpp_type_info, bytes("raw_pointer_ephemeral")); if (isinstance(cpp_conduit)) { return reinterpret_borrow(cpp_conduit).get_pointer(); } diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index a0a80c62..783ceaf6 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -784,18 +784,22 @@ class type_caster_generic { }; inline object cpp_conduit_method(handle self, - const str &pybind11_platform_abi_id, + const bytes &pybind11_platform_abi_id, const capsule &cap_cpp_type_info, - const str &pointer_kind) { - if (std::string(pybind11_platform_abi_id) != PYBIND11_PLATFORM_ABI_ID) { + 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(cap_cpp_type_info.name(), "const std::type_info *") != 0) { return none(); } - std::string pointer_kind_cpp(pointer_kind); - if (pointer_kind_cpp != "raw_pointer_ephemeral") { - throw std::runtime_error("Invalid pointer_kind: \"" + pointer_kind_cpp + "\""); + if (cpp_str(pointer_kind) != "raw_pointer_ephemeral") { + throw std::runtime_error("Invalid pointer_kind: \"" + std::string(pointer_kind) + "\""); } const auto *cpp_type_info = cap_cpp_type_info.get_pointer(); type_caster_generic caster(*cpp_type_info); diff --git a/tests/exo_planet_c_api.cpp b/tests/exo_planet_c_api.cpp index 008bc6d2..a4acc7ce 100644 --- a/tests/exo_planet_c_api.cpp +++ b/tests/exo_planet_c_api.cpp @@ -26,7 +26,7 @@ void *get_cpp_conduit_void_ptr(PyObject *py_obj, const std::type_info *cpp_type_ } PyObject *cpp_conduit = PyObject_CallMethod(py_obj, "__cpp_conduit__", - "sOs", + "yOy", PYBIND11_PLATFORM_ABI_ID, cap_cpp_type_info, "raw_pointer_ephemeral"); diff --git a/tests/test_cpp_conduit.cpp b/tests/test_cpp_conduit.cpp index d0a480bf..d785a2ab 100644 --- a/tests/test_cpp_conduit.cpp +++ b/tests/test_cpp_conduit.cpp @@ -9,7 +9,7 @@ namespace pybind11_tests { namespace test_cpp_conduit { TEST_SUBMODULE(cpp_conduit, m) { - m.attr("PYBIND11_PLATFORM_ABI_ID") = PYBIND11_PLATFORM_ABI_ID; + m.attr("PYBIND11_PLATFORM_ABI_ID") = py::bytes(PYBIND11_PLATFORM_ABI_ID); m.attr("cap_cpp_type_info_Traveler") = py::capsule(&typeid(Traveler), "const std::type_info *"); m.attr("cap_cpp_type_info_int") = py::capsule(&typeid(int), "const std::type_info *"); diff --git a/tests/test_cpp_conduit.py b/tests/test_cpp_conduit.py index e6300825..b7a25374 100644 --- a/tests/test_cpp_conduit.py +++ b/tests/test_cpp_conduit.py @@ -24,7 +24,7 @@ def test_call_cpp_conduit_success(): cap = t_h.__cpp_conduit__( home_planet.PYBIND11_PLATFORM_ABI_ID, home_planet.cap_cpp_type_info_Traveler, - "raw_pointer_ephemeral", + b"raw_pointer_ephemeral", ) assert cap.__class__.__name__ == "PyCapsule" @@ -32,9 +32,9 @@ def test_call_cpp_conduit_success(): def test_call_cpp_conduit_platform_abi_id_mismatch(): t_h = home_planet.Traveler("home") cap = t_h.__cpp_conduit__( - home_planet.PYBIND11_PLATFORM_ABI_ID + "MISMATCH", + home_planet.PYBIND11_PLATFORM_ABI_ID + b"MISMATCH", home_planet.cap_cpp_type_info_Traveler, - "raw_pointer_ephemeral", + b"raw_pointer_ephemeral", ) assert cap is None @@ -44,7 +44,7 @@ def test_call_cpp_conduit_cap_cpp_type_info_mismatch(): cap = t_h.__cpp_conduit__( home_planet.PYBIND11_PLATFORM_ABI_ID, home_planet.cap_cpp_type_info_int, - "raw_pointer_ephemeral", + b"raw_pointer_ephemeral", ) assert cap is None @@ -57,7 +57,7 @@ def test_call_cpp_conduit_pointer_kind_invalid(): t_h.__cpp_conduit__( home_planet.PYBIND11_PLATFORM_ABI_ID, home_planet.cap_cpp_type_info_Traveler, - "raw_pointer_ephemreal", + b"raw_pointer_ephemreal", ) From 31134bcc88d4c14b6b651cb9674ec08ecb7f5897 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 1 Sep 2024 07:37:08 -0700 Subject: [PATCH 38/39] Systematically rename `cap_cpp_type_info` -> `cpp_type_info_capsule` (no functional changes). --- include/pybind11/detail/cpp_conduit.h | 9 +++++---- include/pybind11/detail/type_caster_base.h | 6 +++--- tests/exo_planet_c_api.cpp | 8 ++++---- tests/test_cpp_conduit.cpp | 4 ++-- tests/test_cpp_conduit.py | 10 +++++----- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/include/pybind11/detail/cpp_conduit.h b/include/pybind11/detail/cpp_conduit.h index aa207acd..38f530e1 100644 --- a/include/pybind11/detail/cpp_conduit.h +++ b/include/pybind11/detail/cpp_conduit.h @@ -59,10 +59,11 @@ 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 cap_cpp_type_info(const_cast(static_cast(cpp_type_info)), - "const std::type_info *"); - object cpp_conduit = method( - bytes(PYBIND11_PLATFORM_ABI_ID), cap_cpp_type_info, bytes("raw_pointer_ephemeral")); + capsule cpp_type_info_capsule(const_cast(static_cast(cpp_type_info)), + "const std::type_info *"); + object cpp_conduit = method(bytes(PYBIND11_PLATFORM_ABI_ID), + cpp_type_info_capsule, + bytes("raw_pointer_ephemeral")); if (isinstance(cpp_conduit)) { return reinterpret_borrow(cpp_conduit).get_pointer(); } diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index 783ceaf6..a4633291 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -785,7 +785,7 @@ class type_caster_generic { inline object cpp_conduit_method(handle self, const bytes &pybind11_platform_abi_id, - const capsule &cap_cpp_type_info, + const capsule &cpp_type_info_capsule, const bytes &pointer_kind) { #ifdef PYBIND11_HAS_STRING_VIEW using cpp_str = std::string_view; @@ -795,13 +795,13 @@ inline object cpp_conduit_method(handle self, if (cpp_str(pybind11_platform_abi_id) != PYBIND11_PLATFORM_ABI_ID) { return none(); } - if (std::strcmp(cap_cpp_type_info.name(), "const std::type_info *") != 0) { + if (std::strcmp(cpp_type_info_capsule.name(), "const std::type_info *") != 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 = cap_cpp_type_info.get_pointer(); + const auto *cpp_type_info = cpp_type_info_capsule.get_pointer(); type_caster_generic caster(*cpp_type_info); if (!caster.load(self, false)) { return none(); diff --git a/tests/exo_planet_c_api.cpp b/tests/exo_planet_c_api.cpp index a4acc7ce..a872fb7e 100644 --- a/tests/exo_planet_c_api.cpp +++ b/tests/exo_planet_c_api.cpp @@ -17,20 +17,20 @@ namespace { void *get_cpp_conduit_void_ptr(PyObject *py_obj, const std::type_info *cpp_type_info) { - PyObject *cap_cpp_type_info + PyObject *cpp_type_info_capsule = PyCapsule_New(const_cast(static_cast(cpp_type_info)), "const std::type_info *", nullptr); - if (cap_cpp_type_info == nullptr) { + if (cpp_type_info_capsule == nullptr) { return nullptr; } PyObject *cpp_conduit = PyObject_CallMethod(py_obj, "__cpp_conduit__", "yOy", PYBIND11_PLATFORM_ABI_ID, - cap_cpp_type_info, + cpp_type_info_capsule, "raw_pointer_ephemeral"); - Py_DECREF(cap_cpp_type_info); + Py_DECREF(cpp_type_info_capsule); if (cpp_conduit == nullptr) { return nullptr; } diff --git a/tests/test_cpp_conduit.cpp b/tests/test_cpp_conduit.cpp index d785a2ab..c0241c31 100644 --- a/tests/test_cpp_conduit.cpp +++ b/tests/test_cpp_conduit.cpp @@ -10,9 +10,9 @@ namespace test_cpp_conduit { TEST_SUBMODULE(cpp_conduit, m) { m.attr("PYBIND11_PLATFORM_ABI_ID") = py::bytes(PYBIND11_PLATFORM_ABI_ID); - m.attr("cap_cpp_type_info_Traveler") + m.attr("cpp_type_info_capsule_Traveler") = py::capsule(&typeid(Traveler), "const std::type_info *"); - m.attr("cap_cpp_type_info_int") = py::capsule(&typeid(int), "const std::type_info *"); + m.attr("cpp_type_info_capsule_int") = py::capsule(&typeid(int), "const std::type_info *"); wrap_traveler(m); } diff --git a/tests/test_cpp_conduit.py b/tests/test_cpp_conduit.py index b7a25374..2f60527f 100644 --- a/tests/test_cpp_conduit.py +++ b/tests/test_cpp_conduit.py @@ -23,7 +23,7 @@ def test_call_cpp_conduit_success(): t_h = home_planet.Traveler("home") cap = t_h.__cpp_conduit__( home_planet.PYBIND11_PLATFORM_ABI_ID, - home_planet.cap_cpp_type_info_Traveler, + home_planet.cpp_type_info_capsule_Traveler, b"raw_pointer_ephemeral", ) assert cap.__class__.__name__ == "PyCapsule" @@ -33,17 +33,17 @@ def test_call_cpp_conduit_platform_abi_id_mismatch(): t_h = home_planet.Traveler("home") cap = t_h.__cpp_conduit__( home_planet.PYBIND11_PLATFORM_ABI_ID + b"MISMATCH", - home_planet.cap_cpp_type_info_Traveler, + home_planet.cpp_type_info_capsule_Traveler, b"raw_pointer_ephemeral", ) assert cap is None -def test_call_cpp_conduit_cap_cpp_type_info_mismatch(): +def test_call_cpp_conduit_cpp_type_info_capsule_mismatch(): t_h = home_planet.Traveler("home") cap = t_h.__cpp_conduit__( home_planet.PYBIND11_PLATFORM_ABI_ID, - home_planet.cap_cpp_type_info_int, + home_planet.cpp_type_info_capsule_int, b"raw_pointer_ephemeral", ) assert cap is None @@ -56,7 +56,7 @@ def test_call_cpp_conduit_pointer_kind_invalid(): ): t_h.__cpp_conduit__( home_planet.PYBIND11_PLATFORM_ABI_ID, - home_planet.cap_cpp_type_info_Traveler, + home_planet.cpp_type_info_capsule_Traveler, b"raw_pointer_ephemreal", ) From 8ccddce06c21979800f1767d93ca09d404186ab5 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 1 Sep 2024 07:42:39 -0700 Subject: [PATCH 39/39] Systematically replace `cpp_type_info_capsule` `name`: `"const std::type_info *"` -> `typeid(std::type_info).name()` (this IS a functional change). This provides an extra layer of protection against C++ ABI mismatches: * The first and most important layer is that the `PYBIND11_PLATFORM_ABI_ID`s must match between extensions. * The second layer is that the `typeid(std::type_info).name()`s must match between extensions. --- include/pybind11/detail/cpp_conduit.h | 2 +- include/pybind11/detail/type_caster_base.h | 2 +- tests/exo_planet_c_api.cpp | 2 +- tests/test_cpp_conduit.cpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/pybind11/detail/cpp_conduit.h b/include/pybind11/detail/cpp_conduit.h index 38f530e1..70843753 100644 --- a/include/pybind11/detail/cpp_conduit.h +++ b/include/pybind11/detail/cpp_conduit.h @@ -60,7 +60,7 @@ inline void *try_raw_pointer_ephemeral_from_cpp_conduit(handle src, object method = try_get_cpp_conduit_method(src.ptr()); if (method) { capsule cpp_type_info_capsule(const_cast(static_cast(cpp_type_info)), - "const std::type_info *"); + typeid(std::type_info).name()); object cpp_conduit = method(bytes(PYBIND11_PLATFORM_ABI_ID), cpp_type_info_capsule, bytes("raw_pointer_ephemeral")); diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index a4633291..e40e44ba 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -795,7 +795,7 @@ inline object cpp_conduit_method(handle self, if (cpp_str(pybind11_platform_abi_id) != PYBIND11_PLATFORM_ABI_ID) { return none(); } - if (std::strcmp(cpp_type_info_capsule.name(), "const std::type_info *") != 0) { + if (std::strcmp(cpp_type_info_capsule.name(), typeid(std::type_info).name()) != 0) { return none(); } if (cpp_str(pointer_kind) != "raw_pointer_ephemeral") { diff --git a/tests/exo_planet_c_api.cpp b/tests/exo_planet_c_api.cpp index a872fb7e..60b33e30 100644 --- a/tests/exo_planet_c_api.cpp +++ b/tests/exo_planet_c_api.cpp @@ -19,7 +19,7 @@ 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(static_cast(cpp_type_info)), - "const std::type_info *", + typeid(std::type_info).name(), nullptr); if (cpp_type_info_capsule == nullptr) { return nullptr; diff --git a/tests/test_cpp_conduit.cpp b/tests/test_cpp_conduit.cpp index c0241c31..77f149d3 100644 --- a/tests/test_cpp_conduit.cpp +++ b/tests/test_cpp_conduit.cpp @@ -11,8 +11,8 @@ 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), "const std::type_info *"); - m.attr("cpp_type_info_capsule_int") = py::capsule(&typeid(int), "const std::type_info *"); + = 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); }