3535// / further ABI-incompatible changes may be made before the ABI is officially
3636// / changed to the new version.
3737#ifndef PYBIND11_INTERNALS_VERSION
38- # define PYBIND11_INTERNALS_VERSION 4
38+ # if PY_VERSION_HEX >= 0x030C0000
39+ // Version bump for Python 3.12+, before first 3.12 beta release.
40+ # define PYBIND11_INTERNALS_VERSION 5
41+ # else
42+ # define PYBIND11_INTERNALS_VERSION 4
43+ # endif
3944#endif
4045
46+ // This requirement is mainly to reduce the support burden (see PR #4570).
47+ static_assert (PY_VERSION_HEX < 0x030C0000 || PYBIND11_INTERNALS_VERSION >= 5 ,
48+ " pybind11 ABI version 5 is the minimum for Python 3.12+" );
49+
4150PYBIND11_NAMESPACE_BEGIN (PYBIND11_NAMESPACE)
4251
4352using ExceptionTranslator = void (*)(std::exception_ptr);
@@ -432,6 +441,38 @@ inline void translate_local_exception(std::exception_ptr p) {
432441}
433442#endif
434443
444+ inline object get_python_state_dict () {
445+ object state_dict;
446+ #if PYBIND11_INTERNALS_VERSION <= 4 || PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION)
447+ state_dict = reinterpret_borrow<object>(PyEval_GetBuiltins ());
448+ #else
449+ # if PY_VERSION_HEX < 0x03090000
450+ PyInterpreterState *istate = _PyInterpreterState_Get ();
451+ # else
452+ PyInterpreterState *istate = PyInterpreterState_Get ();
453+ # endif
454+ if (istate) {
455+ state_dict = reinterpret_borrow<object>(PyInterpreterState_GetDict (istate));
456+ }
457+ #endif
458+ if (!state_dict) {
459+ raise_from (PyExc_SystemError, " pybind11::detail::get_python_state_dict() FAILED" );
460+ }
461+ return state_dict;
462+ }
463+
464+ inline object get_internals_obj_from_state_dict (handle state_dict) {
465+ return reinterpret_borrow<object>(dict_getitemstring (state_dict.ptr (), PYBIND11_INTERNALS_ID));
466+ }
467+
468+ inline internals **get_internals_pp_from_capsule (handle obj) {
469+ void *raw_ptr = PyCapsule_GetPointer (obj.ptr (), /* name=*/ nullptr );
470+ if (raw_ptr == nullptr ) {
471+ raise_from (PyExc_SystemError, " pybind11::detail::get_internals_pp_from_capsule() FAILED" );
472+ }
473+ return static_cast <internals **>(raw_ptr);
474+ }
475+
435476// / Return a reference to the current `internals` data
436477PYBIND11_NOINLINE internals &get_internals () {
437478 auto **&internals_pp = get_internals_pp ();
@@ -456,12 +497,12 @@ PYBIND11_NOINLINE internals &get_internals() {
456497#endif
457498 error_scope err_scope;
458499
459- PYBIND11_STR_TYPE id (PYBIND11_INTERNALS_ID );
460- auto builtins = handle ( PyEval_GetBuiltins ());
461- if (builtins. contains (id) && isinstance<capsule>(builtins[id])) {
462- internals_pp = static_cast <internals **>( capsule (builtins[id]));
463-
464- // We loaded builtins through python's builtins , which means that our `error_already_set`
500+ dict state_dict = get_python_state_dict ( );
501+ if (object internals_obj = get_internals_obj_from_state_dict (state_dict)) {
502+ internals_pp = get_internals_pp_from_capsule (internals_obj);
503+ }
504+ if (internals_pp && *internals_pp) {
505+ // We loaded the internals through `state_dict` , which means that our `error_already_set`
465506 // and `builtin_exception` may be different local classes than the ones set up in the
466507 // initial exception translator, below, so add another for our local exception classes.
467508 //
@@ -495,7 +536,7 @@ PYBIND11_NOINLINE internals &get_internals() {
495536# endif
496537 internals_ptr->istate = tstate->interp ;
497538#endif
498- builtins[id ] = capsule (internals_pp);
539+ state_dict[PYBIND11_INTERNALS_ID ] = capsule (internals_pp);
499540 internals_ptr->registered_exception_translators .push_front (&translate_exception);
500541 internals_ptr->static_property_type = make_static_property_type ();
501542 internals_ptr->default_metaclass = make_default_metaclass ();
0 commit comments