This commit is contained in:
Ralf W. Grosse-Kunstleve 2025-02-17 16:47:32 -08:00 committed by GitHub
commit 7047539be8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 35 additions and 147 deletions

View File

@ -101,20 +101,6 @@ jobs:
python: '3.12' python: '3.12'
args: > args: >
-DCMAKE_CXX_FLAGS="/DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT /GR /EHsc" -DCMAKE_CXX_FLAGS="/DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT /GR /EHsc"
# Exercise PYBIND11_SMART_HOLDER_DISABLE
# with recent (or ideally latest) released Python version.
- runs-on: ubuntu-latest
python: '3.12'
args: >
-DCMAKE_CXX_FLAGS="-DPYBIND11_SMART_HOLDER_DISABLE"
- runs-on: macos-13
python: '3.12'
args: >
-DCMAKE_CXX_FLAGS="-DPYBIND11_SMART_HOLDER_DISABLE"
- runs-on: windows-2022
python: '3.12'
args: >
-DCMAKE_CXX_FLAGS="/DPYBIND11_SMART_HOLDER_DISABLE /GR /EHsc"
exclude: exclude:
# The setup-python action currently doesn't have graalpy for windows # The setup-python action currently doesn't have graalpy for windows
- python: 'graalpy-24.1' - python: 'graalpy-24.1'

View File

@ -339,9 +339,7 @@ struct type_record {
/// Solves pybind/pybind11#1446 /// Solves pybind/pybind11#1446
bool release_gil_before_calling_cpp_dtor : 1; bool release_gil_before_calling_cpp_dtor : 1;
#ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT
holder_enum_t holder_enum_v = holder_enum_t::undefined; holder_enum_t holder_enum_v = holder_enum_t::undefined;
#endif
PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) { PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) {
auto *base_info = detail::get_type_info(base, false); auto *base_info = detail::get_type_info(base, false);

View File

@ -22,6 +22,8 @@
// Additional convention: 0xD = dev // Additional convention: 0xD = dev
#define PYBIND11_VERSION_HEX 0x030000D1 #define PYBIND11_VERSION_HEX 0x030000D1
#define PYBIND11_SMART_HOLDER_ENABLED // TODO(rwgk): purge
// Define some generic pybind11 helper macros for warning management. // Define some generic pybind11 helper macros for warning management.
// //
// Note that compiler-specific push/pop pairs are baked into the // Note that compiler-specific push/pop pairs are baked into the
@ -605,11 +607,8 @@ struct instance {
bool simple_instance_registered : 1; bool simple_instance_registered : 1;
/// If true, get_internals().patients has an entry for this object /// If true, get_internals().patients has an entry for this object
bool has_patients : 1; bool has_patients : 1;
// Cannot use PYBIND11_INTERNALS_VERSION >= 106 here without refactoring.
#if PYBIND11_VERSION_MAJOR >= 3
/// If true, this Python object needs to be kept alive for the lifetime of the C++ value. /// If true, this Python object needs to be kept alive for the lifetime of the C++ value.
bool is_alias : 1; bool is_alias : 1;
#endif
/// Initializes all of the above type/values/holders data (but not the instance values /// Initializes all of the above type/values/holders data (but not the instance values
/// themselves) /// themselves)

View File

@ -37,18 +37,12 @@
/// further ABI-incompatible changes may be made before the ABI is officially /// further ABI-incompatible changes may be made before the ABI is officially
/// changed to the new version. /// changed to the new version.
#ifndef PYBIND11_INTERNALS_VERSION #ifndef PYBIND11_INTERNALS_VERSION
# if PYBIND11_VERSION_MAJOR >= 3 # define PYBIND11_INTERNALS_VERSION 7
# define PYBIND11_INTERNALS_VERSION 106
# else
# define PYBIND11_INTERNALS_VERSION 6
# endif
#endif #endif
// This requirement is mainly to reduce the support burden (see PR #4570). #if PYBIND11_INTERNALS_VERSION < 7
static_assert(PY_VERSION_HEX < 0x030C0000 || PYBIND11_INTERNALS_VERSION >= 5, # error "PYBIND11_INTERNALS_VERSION 7 is the minimum for all platforms for pybind11v3."
"pybind11 ABI version 5 is the minimum for Python 3.12+"); #endif
static_assert(PYBIND11_INTERNALS_VERSION >= 4,
"pybind11 ABI version 4 is the minimum for all platforms.");
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
@ -67,40 +61,29 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass);
// Thread Specific Storage (TSS) API. // Thread Specific Storage (TSS) API.
// Avoid unnecessary allocation of `Py_tss_t`, since we cannot use // Avoid unnecessary allocation of `Py_tss_t`, since we cannot use
// `Py_LIMITED_API` anyway. // `Py_LIMITED_API` anyway.
#if PYBIND11_INTERNALS_VERSION > 4 #define PYBIND11_TLS_KEY_REF Py_tss_t &
# define PYBIND11_TLS_KEY_REF Py_tss_t & #if defined(__clang__)
# if defined(__clang__) # define PYBIND11_TLS_KEY_INIT(var) \
# define PYBIND11_TLS_KEY_INIT(var) \ _Pragma("clang diagnostic push") /**/ \
_Pragma("clang diagnostic push") /**/ \ _Pragma("clang diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \
_Pragma("clang diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \ Py_tss_t var \
Py_tss_t var \ = Py_tss_NEEDS_INIT; \
= Py_tss_NEEDS_INIT; \ _Pragma("clang diagnostic pop")
_Pragma("clang diagnostic pop") #elif defined(__GNUC__) && !defined(__INTEL_COMPILER)
# elif defined(__GNUC__) && !defined(__INTEL_COMPILER) # define PYBIND11_TLS_KEY_INIT(var) \
# define PYBIND11_TLS_KEY_INIT(var) \ _Pragma("GCC diagnostic push") /**/ \
_Pragma("GCC diagnostic push") /**/ \ _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \
_Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \ Py_tss_t var \
Py_tss_t var \ = Py_tss_NEEDS_INIT; \
= Py_tss_NEEDS_INIT; \ _Pragma("GCC diagnostic pop")
_Pragma("GCC diagnostic pop")
# else
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t var = Py_tss_NEEDS_INIT;
# endif
# define PYBIND11_TLS_KEY_CREATE(var) (PyThread_tss_create(&(var)) == 0)
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get(&(key))
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set(&(key), (value))
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set(&(key), nullptr)
# define PYBIND11_TLS_FREE(key) PyThread_tss_delete(&(key))
#else #else
# define PYBIND11_TLS_KEY_REF Py_tss_t * # define PYBIND11_TLS_KEY_INIT(var) Py_tss_t var = Py_tss_NEEDS_INIT;
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr;
# define PYBIND11_TLS_KEY_CREATE(var) \
(((var) = PyThread_tss_alloc()) != nullptr && (PyThread_tss_create((var)) == 0))
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key))
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value))
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr)
# define PYBIND11_TLS_FREE(key) PyThread_tss_free(key)
#endif #endif
#define PYBIND11_TLS_KEY_CREATE(var) (PyThread_tss_create(&(var)) == 0)
#define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get(&(key))
#define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set(&(key), (value))
#define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set(&(key), nullptr)
#define PYBIND11_TLS_FREE(key) PyThread_tss_delete(&(key))
// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly // Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly
// other STLs, this means `typeid(A)` from one module won't equal `typeid(A)` from another module // other STLs, this means `typeid(A)` from one module won't equal `typeid(A)` from another module
@ -108,8 +91,7 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass);
// libstdc++, this doesn't happen: equality and the type_index hash are based on the type name, // libstdc++, this doesn't happen: equality and the type_index hash are based on the type name,
// which works. If not under a known-good stl, provide our own name-based hash and equality // which works. If not under a known-good stl, provide our own name-based hash and equality
// functions that use the type name. // functions that use the type name.
#if (PYBIND11_INTERNALS_VERSION <= 4 && defined(__GLIBCXX__)) \ #if !defined(_LIBCPP_VERSION)
|| (PYBIND11_INTERNALS_VERSION >= 5 && !defined(_LIBCPP_VERSION))
inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; } inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; }
using type_hash = std::hash<std::type_index>; using type_hash = std::hash<std::type_index>;
using type_equal_to = std::equal_to<std::type_index>; using type_equal_to = std::equal_to<std::type_index>;
@ -197,35 +179,26 @@ struct internals {
std::forward_list<ExceptionTranslator> registered_exception_translators; std::forward_list<ExceptionTranslator> registered_exception_translators;
std::unordered_map<std::string, void *> shared_data; // Custom data to be shared across std::unordered_map<std::string, void *> shared_data; // Custom data to be shared across
// extensions // extensions
#if PYBIND11_INTERNALS_VERSION == 4 std::forward_list<std::string> static_strings; // Stores the std::strings backing
std::vector<PyObject *> unused_loader_patient_stack_remove_at_v5; // detail::c_str()
#endif
std::forward_list<std::string> static_strings; // Stores the std::strings backing
// detail::c_str()
PyTypeObject *static_property_type; PyTypeObject *static_property_type;
PyTypeObject *default_metaclass; PyTypeObject *default_metaclass;
PyObject *instance_base; PyObject *instance_base;
// Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined: // Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined:
PYBIND11_TLS_KEY_INIT(tstate) PYBIND11_TLS_KEY_INIT(tstate)
#if PYBIND11_INTERNALS_VERSION > 4
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key) PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
#endif // PYBIND11_INTERNALS_VERSION > 4
// Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined: // Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined:
PyInterpreterState *istate = nullptr; PyInterpreterState *istate = nullptr;
#if PYBIND11_INTERNALS_VERSION > 4
// Note that we have to use a std::string to allocate memory to ensure a unique address // Note that we have to use a std::string to allocate memory to ensure a unique address
// We want unique addresses since we use pointer equality to compare function records // We want unique addresses since we use pointer equality to compare function records
std::string function_record_capsule_name = internals_function_record_capsule_name; std::string function_record_capsule_name = internals_function_record_capsule_name;
#endif
internals() = default; internals() = default;
internals(const internals &other) = delete; internals(const internals &other) = delete;
internals &operator=(const internals &other) = delete; internals &operator=(const internals &other) = delete;
~internals() { ~internals() {
#if PYBIND11_INTERNALS_VERSION > 4
PYBIND11_TLS_FREE(loader_life_support_tls_key); PYBIND11_TLS_FREE(loader_life_support_tls_key);
#endif // PYBIND11_INTERNALS_VERSION > 4
// This destructor is called *after* Py_Finalize() in finalize_interpreter(). // This destructor is called *after* Py_Finalize() in finalize_interpreter().
// That *SHOULD BE* fine. The following details what happens when PyThread_tss_free is // That *SHOULD BE* fine. The following details what happens when PyThread_tss_free is
@ -238,10 +211,6 @@ struct internals {
} }
}; };
#if PYBIND11_INTERNALS_VERSION >= 106
# define PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT
enum class holder_enum_t : uint8_t { enum class holder_enum_t : uint8_t {
undefined, undefined,
std_unique_ptr, // Default, lacking interop with std::shared_ptr. std_unique_ptr, // Default, lacking interop with std::shared_ptr.
@ -250,13 +219,6 @@ enum class holder_enum_t : uint8_t {
custom_holder, custom_holder,
}; };
#endif
#if defined(PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT) \
&& !defined(PYBIND11_SMART_HOLDER_DISABLE)
# define PYBIND11_SMART_HOLDER_ENABLED
#endif
/// Additional type information which does not fit into the PyTypeObject. /// Additional type information which does not fit into the PyTypeObject.
/// Changes to this struct also require bumping `PYBIND11_INTERNALS_VERSION`. /// Changes to this struct also require bumping `PYBIND11_INTERNALS_VERSION`.
struct type_info { struct type_info {
@ -285,9 +247,7 @@ struct type_info {
bool default_holder : 1; bool default_holder : 1;
/* true if this is a type registered with py::module_local */ /* true if this is a type registered with py::module_local */
bool module_local : 1; bool module_local : 1;
#ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT
holder_enum_t holder_enum_v = holder_enum_t::undefined; holder_enum_t holder_enum_v = holder_enum_t::undefined;
#endif
}; };
#define PYBIND11_INTERNALS_ID \ #define PYBIND11_INTERNALS_ID \
@ -414,7 +374,7 @@ inline void translate_local_exception(std::exception_ptr p) {
inline object get_python_state_dict() { inline object get_python_state_dict() {
object state_dict; object state_dict;
#if PYBIND11_INTERNALS_VERSION <= 4 || defined(PYPY_VERSION) || defined(GRAALVM_PYTHON) #if defined(PYPY_VERSION) || defined(GRAALVM_PYTHON)
state_dict = reinterpret_borrow<object>(PyEval_GetBuiltins()); state_dict = reinterpret_borrow<object>(PyEval_GetBuiltins());
#else #else
# if PY_VERSION_HEX < 0x03090000 # if PY_VERSION_HEX < 0x03090000
@ -512,13 +472,12 @@ PYBIND11_NOINLINE internals &get_internals() {
} }
PYBIND11_TLS_REPLACE_VALUE(internals_ptr->tstate, tstate); PYBIND11_TLS_REPLACE_VALUE(internals_ptr->tstate, tstate);
#if PYBIND11_INTERNALS_VERSION > 4
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition) // NOLINTNEXTLINE(bugprone-assignment-in-if-condition)
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->loader_life_support_tls_key)) { if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->loader_life_support_tls_key)) {
pybind11_fail("get_internals: could not successfully initialize the " pybind11_fail("get_internals: could not successfully initialize the "
"loader_life_support TSS key!"); "loader_life_support TSS key!");
} }
#endif
internals_ptr->istate = tstate->interp; internals_ptr->istate = tstate->interp;
state_dict[PYBIND11_INTERNALS_ID] = capsule(reinterpret_cast<void *>(internals_pp)); state_dict[PYBIND11_INTERNALS_ID] = capsule(reinterpret_cast<void *>(internals_pp));
internals_ptr->registered_exception_translators.push_front(&translate_exception); internals_ptr->registered_exception_translators.push_front(&translate_exception);
@ -548,40 +507,6 @@ PYBIND11_NOINLINE internals &get_internals() {
struct local_internals { struct local_internals {
type_map<type_info *> registered_types_cpp; type_map<type_info *> registered_types_cpp;
std::forward_list<ExceptionTranslator> registered_exception_translators; std::forward_list<ExceptionTranslator> registered_exception_translators;
#if PYBIND11_INTERNALS_VERSION == 4
// For ABI compatibility, we can't store the loader_life_support TLS key in
// the `internals` struct directly. Instead, we store it in `shared_data` and
// cache a copy in `local_internals`. If we allocated a separate TLS key for
// each instance of `local_internals`, we could end up allocating hundreds of
// TLS keys if hundreds of different pybind11 modules are loaded (which is a
// plausible number).
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
// Holds the shared TLS key for the loader_life_support stack.
struct shared_loader_life_support_data {
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
shared_loader_life_support_data() {
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition)
if (!PYBIND11_TLS_KEY_CREATE(loader_life_support_tls_key)) {
pybind11_fail("local_internals: could not successfully initialize the "
"loader_life_support TLS key!");
}
}
// We can't help but leak the TLS key, because Python never unloads extension modules.
};
local_internals() {
auto &internals = get_internals();
// Get or create the `loader_life_support_stack_key`.
auto &ptr = internals.shared_data["_life_support"];
if (!ptr) {
ptr = new shared_loader_life_support_data;
}
loader_life_support_tls_key
= static_cast<shared_loader_life_support_data *>(ptr)->loader_life_support_tls_key;
}
#endif // PYBIND11_INTERNALS_VERSION == 4
}; };
/// Works like `get_internals`, but for things which are locally registered. /// Works like `get_internals`, but for things which are locally registered.
@ -688,7 +613,7 @@ const char *c_str(Args &&...args) {
inline const char *get_function_record_capsule_name() { inline const char *get_function_record_capsule_name() {
// On GraalPy, pointer equality of the names is currently not guaranteed // On GraalPy, pointer equality of the names is currently not guaranteed
#if PYBIND11_INTERNALS_VERSION > 4 && !defined(GRAALVM_PYTHON) #if !defined(GRAALVM_PYTHON)
return get_internals().function_record_capsule_name.c_str(); return get_internals().function_record_capsule_name.c_str();
#else #else
return nullptr; return nullptr;

View File

@ -47,11 +47,7 @@ private:
// Store stack pointer in thread-local storage. // Store stack pointer in thread-local storage.
static PYBIND11_TLS_KEY_REF get_stack_tls_key() { static PYBIND11_TLS_KEY_REF get_stack_tls_key() {
#if PYBIND11_INTERNALS_VERSION == 4
return get_local_internals().loader_life_support_tls_key;
#else
return get_internals().loader_life_support_tls_key; return get_internals().loader_life_support_tls_key;
#endif
} }
static loader_life_support *get_stack_top() { static loader_life_support *get_stack_top() {
return static_cast<loader_life_support *>(PYBIND11_TLS_GET_VALUE(get_stack_tls_key())); return static_cast<loader_life_support *>(PYBIND11_TLS_GET_VALUE(get_stack_tls_key()));

View File

@ -1448,9 +1448,7 @@ protected:
tinfo->simple_ancestors = true; tinfo->simple_ancestors = true;
tinfo->default_holder = rec.default_holder; tinfo->default_holder = rec.default_holder;
tinfo->module_local = rec.module_local; tinfo->module_local = rec.module_local;
#ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT
tinfo->holder_enum_v = rec.holder_enum_v; tinfo->holder_enum_v = rec.holder_enum_v;
#endif
with_internals([&](internals &internals) { with_internals([&](internals &internals) {
auto tindex = std::type_index(*rec.type); auto tindex = std::type_index(*rec.type);

View File

@ -269,12 +269,7 @@ TEST_SUBMODULE(callbacks, m) {
rec_capsule.set_name(rec_capsule_name); rec_capsule.set_name(rec_capsule_name);
m.add_object("custom_function", PyCFunction_New(custom_def, rec_capsule.ptr())); m.add_object("custom_function", PyCFunction_New(custom_def, rec_capsule.ptr()));
// This test requires a new ABI version to pass
#if PYBIND11_INTERNALS_VERSION > 4 && !defined(GRAALVM_PYTHON)
// rec_capsule with nullptr name // rec_capsule with nullptr name
py::capsule rec_capsule2(std::malloc(1), [](void *data) { std::free(data); }); py::capsule rec_capsule2(std::malloc(1), [](void *data) { std::free(data); });
m.add_object("custom_function2", PyCFunction_New(custom_def, rec_capsule2.ptr())); m.add_object("custom_function2", PyCFunction_New(custom_def, rec_capsule2.ptr()));
#else
m.add_object("custom_function2", py::none());
#endif
} }

View File

@ -217,9 +217,7 @@ def test_custom_func():
assert m.roundtrip(m.custom_function)(4) == 36 assert m.roundtrip(m.custom_function)(4) == 36
@pytest.mark.skipif( @pytest.mark.skipif("env.GRAALPY", reason="TODO debug segfault")
m.custom_function2 is None, reason="Current PYBIND11_INTERNALS_VERSION too low"
)
def test_custom_func2(): def test_custom_func2():
assert m.custom_function2(3) == 27 assert m.custom_function2(3) == 27
assert m.roundtrip(m.custom_function2)(3) == 27 assert m.roundtrip(m.custom_function2)(3) == 27

View File

@ -10,7 +10,6 @@ TEST_SUBMODULE(unnamed_namespace_a, m) {
} else { } else {
m.attr("unnamed_namespace_a_any_struct") = py::none(); m.attr("unnamed_namespace_a_any_struct") = py::none();
} }
m.attr("PYBIND11_INTERNALS_VERSION") = PYBIND11_INTERNALS_VERSION;
m.attr("defined_WIN32_or__WIN32") = m.attr("defined_WIN32_or__WIN32") =
#if defined(WIN32) || defined(_WIN32) #if defined(WIN32) || defined(_WIN32)
true; true;

View File

@ -5,13 +5,7 @@ import pytest
from pybind11_tests import unnamed_namespace_a as m from pybind11_tests import unnamed_namespace_a as m
from pybind11_tests import unnamed_namespace_b as mb from pybind11_tests import unnamed_namespace_b as mb
XFAIL_CONDITION = ( XFAIL_CONDITION = "not m.defined_WIN32_or__WIN32 and (m.defined___clang__ or m.defined__LIBCPP_VERSION)"
"(m.PYBIND11_INTERNALS_VERSION <= 4 and (m.defined___clang__ or not m.defined___GLIBCXX__))"
" or "
"(m.PYBIND11_INTERNALS_VERSION >= 5 and not m.defined_WIN32_or__WIN32"
" and "
"(m.defined___clang__ or m.defined__LIBCPP_VERSION))"
)
XFAIL_REASON = "Known issues: https://github.com/pybind/pybind11/pull/4319" XFAIL_REASON = "Known issues: https://github.com/pybind/pybind11/pull/4319"