3434// / further ABI-incompatible changes may be made before the ABI is officially
3535// / changed to the new version.
3636#ifndef PYBIND11_INTERNALS_VERSION
37- # define PYBIND11_INTERNALS_VERSION 4
37+ # if PY_VERSION_HEX >= 0x030C0000
38+ // Version bump for Python 3.12+, before first 3.12 beta release.
39+ # define PYBIND11_INTERNALS_VERSION 5
40+ # else
41+ # define PYBIND11_INTERNALS_VERSION 4
42+ # endif
3843#endif
3944
45+ // This requirement is mainly to reduce the support burden (see PR #4570).
46+ static_assert (PY_VERSION_HEX < 0x030C0000 || PYBIND11_INTERNALS_VERSION >= 5 ,
47+ " pybind11 ABI version 5 is the minimum for Python 3.12+" );
48+
4049PYBIND11_NAMESPACE_BEGIN (PYBIND11_NAMESPACE)
4150
4251using ExceptionTranslator = void (*)(std::exception_ptr);
@@ -421,6 +430,38 @@ inline void translate_local_exception(std::exception_ptr p) {
421430}
422431#endif
423432
433+ inline object get_python_state_dict () {
434+ object state_dict;
435+ #if PYBIND11_INTERNALS_VERSION <= 4 || PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION)
436+ state_dict = reinterpret_borrow<object>(PyEval_GetBuiltins ());
437+ #else
438+ # if PY_VERSION_HEX < 0x03090000
439+ PyInterpreterState *istate = _PyInterpreterState_Get ();
440+ # else
441+ PyInterpreterState *istate = PyInterpreterState_Get ();
442+ # endif
443+ if (istate) {
444+ state_dict = reinterpret_borrow<object>(PyInterpreterState_GetDict (istate));
445+ }
446+ #endif
447+ if (!state_dict) {
448+ raise_from (PyExc_SystemError, " pybind11::detail::get_python_state_dict() FAILED" );
449+ }
450+ return state_dict;
451+ }
452+
453+ inline object get_internals_obj_from_state_dict (handle state_dict) {
454+ return reinterpret_borrow<object>(dict_getitemstring (state_dict.ptr (), PYBIND11_INTERNALS_ID));
455+ }
456+
457+ inline internals **get_internals_pp_from_capsule (handle obj) {
458+ void *raw_ptr = PyCapsule_GetPointer (obj.ptr (), /* name=*/ nullptr );
459+ if (raw_ptr == nullptr ) {
460+ raise_from (PyExc_SystemError, " pybind11::detail::get_internals_pp_from_capsule() FAILED" );
461+ }
462+ return static_cast <internals **>(raw_ptr);
463+ }
464+
424465// / Return a reference to the current `internals` data
425466PYBIND11_NOINLINE internals &get_internals () {
426467 auto **&internals_pp = get_internals_pp ();
@@ -445,12 +486,12 @@ PYBIND11_NOINLINE internals &get_internals() {
445486#endif
446487 error_scope err_scope;
447488
448- PYBIND11_STR_TYPE id (PYBIND11_INTERNALS_ID );
449- auto builtins = handle ( PyEval_GetBuiltins ());
450- if (builtins. contains (id) && isinstance<capsule>(builtins[id])) {
451- internals_pp = static_cast <internals **>( capsule (builtins[id]));
452-
453- // We loaded builtins through python's builtins , which means that our `error_already_set`
489+ dict state_dict = get_python_state_dict ( );
490+ if (object internals_obj = get_internals_obj_from_state_dict (state_dict)) {
491+ internals_pp = get_internals_pp_from_capsule (internals_obj);
492+ }
493+ if (internals_pp && *internals_pp) {
494+ // We loaded the internals through `state_dict` , which means that our `error_already_set`
454495 // and `builtin_exception` may be different local classes than the ones set up in the
455496 // initial exception translator, below, so add another for our local exception classes.
456497 //
@@ -484,7 +525,7 @@ PYBIND11_NOINLINE internals &get_internals() {
484525# endif
485526 internals_ptr->istate = tstate->interp ;
486527#endif
487- builtins[id ] = capsule (internals_pp);
528+ state_dict[PYBIND11_INTERNALS_ID ] = capsule (internals_pp);
488529 internals_ptr->registered_exception_translators .push_front (&translate_exception);
489530 internals_ptr->static_property_type = make_static_property_type ();
490531 internals_ptr->default_metaclass = make_default_metaclass ();
0 commit comments