diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c8906451..36d564e78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,20 +101,6 @@ jobs: python: '3.12' args: > -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: # The setup-python action currently doesn't have graalpy for windows - python: 'graalpy-24.1' diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index dc9570f92..5380fd555 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -339,9 +339,7 @@ struct type_record { /// Solves pybind/pybind11#1446 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; -#endif PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) { auto *base_info = detail::get_type_info(base, false); diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index eab3e1c0d..765812a4b 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -22,6 +22,8 @@ // Additional convention: 0xD = dev #define PYBIND11_VERSION_HEX 0x030000D1 +#define PYBIND11_SMART_HOLDER_ENABLED // TODO(rwgk): purge + // Define some generic pybind11 helper macros for warning management. // // Note that compiler-specific push/pop pairs are baked into the @@ -605,11 +607,8 @@ struct instance { bool simple_instance_registered : 1; /// If true, get_internals().patients has an entry for this object 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. bool is_alias : 1; -#endif /// Initializes all of the above type/values/holders data (but not the instance values /// themselves) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 892e611d6..a206b17dd 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -37,18 +37,12 @@ /// further ABI-incompatible changes may be made before the ABI is officially /// changed to the new version. #ifndef PYBIND11_INTERNALS_VERSION -# if PYBIND11_VERSION_MAJOR >= 3 -# define PYBIND11_INTERNALS_VERSION 106 -# else -# define PYBIND11_INTERNALS_VERSION 6 -# endif +# define PYBIND11_INTERNALS_VERSION 7 #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+"); -static_assert(PYBIND11_INTERNALS_VERSION >= 4, - "pybind11 ABI version 4 is the minimum for all platforms."); +#if PYBIND11_INTERNALS_VERSION < 7 +# error "PYBIND11_INTERNALS_VERSION 7 is the minimum for all platforms for pybind11v3." +#endif PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) @@ -67,40 +61,29 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass); // Thread Specific Storage (TSS) API. // Avoid unnecessary allocation of `Py_tss_t`, since we cannot use // `Py_LIMITED_API` anyway. -#if PYBIND11_INTERNALS_VERSION > 4 -# define PYBIND11_TLS_KEY_REF Py_tss_t & -# if defined(__clang__) -# define PYBIND11_TLS_KEY_INIT(var) \ - _Pragma("clang diagnostic push") /**/ \ - _Pragma("clang diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \ - Py_tss_t var \ - = Py_tss_NEEDS_INIT; \ - _Pragma("clang diagnostic pop") -# elif defined(__GNUC__) && !defined(__INTEL_COMPILER) -# define PYBIND11_TLS_KEY_INIT(var) \ - _Pragma("GCC diagnostic push") /**/ \ - _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \ - Py_tss_t var \ - = Py_tss_NEEDS_INIT; \ - _Pragma("GCC diagnostic pop") -# else -# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t var = Py_tss_NEEDS_INIT; -# endif -# define PYBIND11_TLS_KEY_CREATE(var) (PyThread_tss_create(&(var)) == 0) -# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get(&(key)) -# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set(&(key), (value)) -# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set(&(key), nullptr) -# define PYBIND11_TLS_FREE(key) PyThread_tss_delete(&(key)) +#define PYBIND11_TLS_KEY_REF Py_tss_t & +#if defined(__clang__) +# define PYBIND11_TLS_KEY_INIT(var) \ + _Pragma("clang diagnostic push") /**/ \ + _Pragma("clang diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \ + Py_tss_t var \ + = Py_tss_NEEDS_INIT; \ + _Pragma("clang diagnostic pop") +#elif defined(__GNUC__) && !defined(__INTEL_COMPILER) +# define PYBIND11_TLS_KEY_INIT(var) \ + _Pragma("GCC diagnostic push") /**/ \ + _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \ + Py_tss_t var \ + = Py_tss_NEEDS_INIT; \ + _Pragma("GCC diagnostic pop") #else -# define PYBIND11_TLS_KEY_REF Py_tss_t * -# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr; -# define PYBIND11_TLS_KEY_CREATE(var) \ - (((var) = PyThread_tss_alloc()) != nullptr && (PyThread_tss_create((var)) == 0)) -# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key)) -# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value)) -# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr) -# define PYBIND11_TLS_FREE(key) PyThread_tss_free(key) +# 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)) // 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 @@ -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, // which works. If not under a known-good stl, provide our own name-based hash and equality // functions that use the type name. -#if (PYBIND11_INTERNALS_VERSION <= 4 && defined(__GLIBCXX__)) \ - || (PYBIND11_INTERNALS_VERSION >= 5 && !defined(_LIBCPP_VERSION)) +#if !defined(_LIBCPP_VERSION) inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; } using type_hash = std::hash; using type_equal_to = std::equal_to; @@ -197,35 +179,26 @@ struct internals { std::forward_list registered_exception_translators; std::unordered_map shared_data; // Custom data to be shared across // extensions -#if PYBIND11_INTERNALS_VERSION == 4 - std::vector unused_loader_patient_stack_remove_at_v5; -#endif - std::forward_list static_strings; // Stores the std::strings backing - // detail::c_str() + std::forward_list static_strings; // Stores the std::strings backing + // detail::c_str() PyTypeObject *static_property_type; PyTypeObject *default_metaclass; PyObject *instance_base; // Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined: PYBIND11_TLS_KEY_INIT(tstate) -#if PYBIND11_INTERNALS_VERSION > 4 PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key) -#endif // PYBIND11_INTERNALS_VERSION > 4 // Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined: 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 // We want unique addresses since we use pointer equality to compare function records std::string function_record_capsule_name = internals_function_record_capsule_name; -#endif internals() = default; internals(const internals &other) = delete; internals &operator=(const internals &other) = delete; ~internals() { -#if PYBIND11_INTERNALS_VERSION > 4 PYBIND11_TLS_FREE(loader_life_support_tls_key); -#endif // PYBIND11_INTERNALS_VERSION > 4 // This destructor is called *after* Py_Finalize() in finalize_interpreter(). // That *SHOULD BE* fine. The following details what happens when PyThread_tss_free is @@ -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 { undefined, std_unique_ptr, // Default, lacking interop with std::shared_ptr. @@ -250,13 +219,6 @@ enum class holder_enum_t : uint8_t { 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. /// Changes to this struct also require bumping `PYBIND11_INTERNALS_VERSION`. struct type_info { @@ -285,9 +247,7 @@ struct type_info { bool default_holder : 1; /* true if this is a type registered with py::module_local */ bool module_local : 1; -#ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT holder_enum_t holder_enum_v = holder_enum_t::undefined; -#endif }; #define PYBIND11_INTERNALS_ID \ @@ -414,7 +374,7 @@ inline void translate_local_exception(std::exception_ptr p) { inline object get_python_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(PyEval_GetBuiltins()); #else # if PY_VERSION_HEX < 0x03090000 @@ -512,13 +472,12 @@ PYBIND11_NOINLINE internals &get_internals() { } PYBIND11_TLS_REPLACE_VALUE(internals_ptr->tstate, tstate); -#if PYBIND11_INTERNALS_VERSION > 4 // NOLINTNEXTLINE(bugprone-assignment-in-if-condition) if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->loader_life_support_tls_key)) { pybind11_fail("get_internals: could not successfully initialize the " "loader_life_support TSS key!"); } -#endif + internals_ptr->istate = tstate->interp; state_dict[PYBIND11_INTERNALS_ID] = capsule(reinterpret_cast(internals_pp)); internals_ptr->registered_exception_translators.push_front(&translate_exception); @@ -548,40 +507,6 @@ PYBIND11_NOINLINE internals &get_internals() { struct local_internals { type_map registered_types_cpp; std::forward_list 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(ptr)->loader_life_support_tls_key; - } -#endif // PYBIND11_INTERNALS_VERSION == 4 }; /// 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() { // 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(); #else return nullptr; diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index ed32e9c31..097d176f6 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -47,11 +47,7 @@ private: // Store stack pointer in thread-local storage. static PYBIND11_TLS_KEY_REF get_stack_tls_key() { -#if PYBIND11_INTERNALS_VERSION == 4 - return get_local_internals().loader_life_support_tls_key; -#else return get_internals().loader_life_support_tls_key; -#endif } static loader_life_support *get_stack_top() { return static_cast(PYBIND11_TLS_GET_VALUE(get_stack_tls_key())); diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index a09aafec6..dfbcbcf0f 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1448,9 +1448,7 @@ protected: tinfo->simple_ancestors = true; tinfo->default_holder = rec.default_holder; tinfo->module_local = rec.module_local; -#ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT tinfo->holder_enum_v = rec.holder_enum_v; -#endif with_internals([&](internals &internals) { auto tindex = std::type_index(*rec.type); diff --git a/tests/test_callbacks.cpp b/tests/test_callbacks.cpp index f28f0a9e2..20dbaa6b2 100644 --- a/tests/test_callbacks.cpp +++ b/tests/test_callbacks.cpp @@ -269,12 +269,7 @@ TEST_SUBMODULE(callbacks, m) { rec_capsule.set_name(rec_capsule_name); 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 py::capsule rec_capsule2(std::malloc(1), [](void *data) { std::free(data); }); m.add_object("custom_function2", PyCFunction_New(custom_def, rec_capsule2.ptr())); -#else - m.add_object("custom_function2", py::none()); -#endif } diff --git a/tests/test_callbacks.py b/tests/test_callbacks.py index 00b9015d0..7919ad0ae 100644 --- a/tests/test_callbacks.py +++ b/tests/test_callbacks.py @@ -217,9 +217,7 @@ def test_custom_func(): assert m.roundtrip(m.custom_function)(4) == 36 -@pytest.mark.skipif( - m.custom_function2 is None, reason="Current PYBIND11_INTERNALS_VERSION too low" -) +@pytest.mark.skipif("env.GRAALPY", reason="TODO debug segfault") def test_custom_func2(): assert m.custom_function2(3) == 27 assert m.roundtrip(m.custom_function2)(3) == 27 diff --git a/tests/test_unnamed_namespace_a.cpp b/tests/test_unnamed_namespace_a.cpp index 2152e64bd..26e9cb751 100644 --- a/tests/test_unnamed_namespace_a.cpp +++ b/tests/test_unnamed_namespace_a.cpp @@ -10,7 +10,6 @@ TEST_SUBMODULE(unnamed_namespace_a, m) { } else { m.attr("unnamed_namespace_a_any_struct") = py::none(); } - m.attr("PYBIND11_INTERNALS_VERSION") = PYBIND11_INTERNALS_VERSION; m.attr("defined_WIN32_or__WIN32") = #if defined(WIN32) || defined(_WIN32) true; diff --git a/tests/test_unnamed_namespace_a.py b/tests/test_unnamed_namespace_a.py index 0fa1fa323..fabf1312a 100644 --- a/tests/test_unnamed_namespace_a.py +++ b/tests/test_unnamed_namespace_a.py @@ -5,13 +5,7 @@ import pytest from pybind11_tests import unnamed_namespace_a as m from pybind11_tests import unnamed_namespace_b as mb -XFAIL_CONDITION = ( - "(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_CONDITION = "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"