[feat/precompile-type-caster-base] feat: move internals.h, exception_translation.h, and pybind11_fail out of line (#6135)
* feat: move internals.h, exception_translation.h, and pybind11_fail out of line
The internals accessor family (get_internals, ensure_internals, the
local-internals key and capsules, exception translators) moves into
internals-inl.h; per-module identity is unchanged because the
function-local statics move with their functions into whatever binary
each module links. Also adds common-inl.h (pybind11_fail) and
exception_translation-inl.h. Tiny hot accessors and all templates stay
in the headers.
Assisted-by: ClaudeCode:claude-fable-5
* fix: drop duplicate translate_exception declaration
Assisted-by: ClaudeCode:claude-fable-5
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 35b0035..906aac2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -192,10 +192,12 @@
include/pybind11/detail/argument_vector.h
include/pybind11/detail/class-inl.h
include/pybind11/detail/class.h
+ include/pybind11/detail/common-inl.h
include/pybind11/detail/common.h
include/pybind11/detail/cpp_conduit.h
include/pybind11/detail/descr.h
include/pybind11/detail/dynamic_raw_ptr_cast_if_possible.h
+ include/pybind11/detail/exception_translation-inl.h
include/pybind11/detail/exception_translation.h
include/pybind11/detail/function_record_pyobject.h
include/pybind11/detail/function_ref.h
diff --git a/include/pybind11/detail/common-inl.h b/include/pybind11/detail/common-inl.h
new file mode 100644
index 0000000..7e4d6fa
--- /dev/null
+++ b/include/pybind11/detail/common-inl.h
@@ -0,0 +1,31 @@
+/*
+ pybind11/detail/common-inl.h: Out-of-line definitions for common.h
+
+ Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+// Every function defined here must start with PYBIND11_INLINE (or
+// PYBIND11_NOINLINE_ATTR PYBIND11_INLINE). In the default header-only mode this file is
+// included at the bottom of common.h; when PYBIND11_PRECOMPILED is defined it is only
+// compiled into the pybind11 static library (see src/).
+
+#pragma once
+
+#include "common.h"
+
+PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
+
+[[noreturn]] PYBIND11_NOINLINE_ATTR PYBIND11_INLINE void pybind11_fail(const char *reason) {
+ assert(!PyErr_Occurred());
+ throw std::runtime_error(reason);
+}
+
+[[noreturn]] PYBIND11_NOINLINE_ATTR PYBIND11_INLINE void pybind11_fail(const std::string &reason) {
+ assert(!PyErr_Occurred());
+ throw std::runtime_error(reason);
+}
+
+PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h
index f65f69a..7445c01 100644
--- a/include/pybind11/detail/common.h
+++ b/include/pybind11/detail/common.h
@@ -1169,14 +1169,8 @@
/// casting error
PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally
-[[noreturn]] PYBIND11_NOINLINE void pybind11_fail(const char *reason) {
- assert(!PyErr_Occurred());
- throw std::runtime_error(reason);
-}
-[[noreturn]] PYBIND11_NOINLINE void pybind11_fail(const std::string &reason) {
- assert(!PyErr_Occurred());
- throw std::runtime_error(reason);
-}
+[[noreturn]] void pybind11_fail(const char *reason);
+[[noreturn]] void pybind11_fail(const std::string &reason);
template <typename T, typename SFINAE = void>
struct format_descriptor {};
@@ -1438,3 +1432,7 @@
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
+
+#ifndef PYBIND11_PRECOMPILED
+# include "common-inl.h" // IWYU pragma: export
+#endif
diff --git a/include/pybind11/detail/exception_translation-inl.h b/include/pybind11/detail/exception_translation-inl.h
new file mode 100644
index 0000000..de14461
--- /dev/null
+++ b/include/pybind11/detail/exception_translation-inl.h
@@ -0,0 +1,73 @@
+/*
+ pybind11/detail/exception_translation-inl.h: Out-of-line definitions for
+ exception_translation.h
+
+ Copyright (c) 2024 The Pybind Development Team.
+
+ All rights reserved. Use of this source code is governed by a
+ BSD-style license that can be found in the LICENSE file.
+*/
+
+// Every function defined here must start with PYBIND11_INLINE (or
+// PYBIND11_NOINLINE_ATTR PYBIND11_INLINE). In the default header-only mode this file is
+// included at the bottom of exception_translation.h; when PYBIND11_PRECOMPILED is defined
+// it is only compiled into the pybind11 static library (see src/).
+
+#pragma once
+
+#include "exception_translation.h"
+
+PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
+PYBIND11_NAMESPACE_BEGIN(detail)
+
+PYBIND11_INLINE bool
+apply_exception_translators(std::forward_list<ExceptionTranslator> &translators) {
+ auto last_exception = std::current_exception();
+
+ for (auto &translator : translators) {
+ try {
+ translator(last_exception);
+ return true;
+ } catch (...) {
+ last_exception = std::current_exception();
+ }
+ }
+ return false;
+}
+
+PYBIND11_INLINE void try_translate_exceptions() {
+ /* When an exception is caught, give each registered exception
+ translator a chance to translate it to a Python exception. First
+ all module-local translators will be tried in reverse order of
+ registration. If none of the module-locale translators handle
+ the exception (or there are no module-locale translators) then
+ the global translators will be tried, also in reverse order of
+ registration.
+
+ A translator may choose to do one of the following:
+
+ - catch the exception and call py::set_error()
+ to set a standard (or custom) Python exception, or
+ - do nothing and let the exception fall through to the next translator, or
+ - delegate translation to the next translator by throwing a new type of exception.
+ */
+
+ bool handled = with_exception_translators(
+ [&](std::forward_list<ExceptionTranslator> &exception_translators,
+ std::forward_list<ExceptionTranslator> &local_exception_translators) {
+ if (detail::apply_exception_translators(local_exception_translators)) {
+ return true;
+ }
+ if (detail::apply_exception_translators(exception_translators)) {
+ return true;
+ }
+ return false;
+ });
+
+ if (!handled) {
+ set_error(PyExc_SystemError, "Exception escaped from default exception translator!");
+ }
+}
+
+PYBIND11_NAMESPACE_END(detail)
+PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
diff --git a/include/pybind11/detail/exception_translation.h b/include/pybind11/detail/exception_translation.h
index 22ae8a1..ed18a7a 100644
--- a/include/pybind11/detail/exception_translation.h
+++ b/include/pybind11/detail/exception_translation.h
@@ -19,53 +19,13 @@
// Return true if one of the translators completed without raising an exception
// itself. Return of false indicates that if there are other translators
// available, they should be tried.
-inline bool apply_exception_translators(std::forward_list<ExceptionTranslator> &translators) {
- auto last_exception = std::current_exception();
+bool apply_exception_translators(std::forward_list<ExceptionTranslator> &translators);
- for (auto &translator : translators) {
- try {
- translator(last_exception);
- return true;
- } catch (...) {
- last_exception = std::current_exception();
- }
- }
- return false;
-}
-
-inline void try_translate_exceptions() {
- /* When an exception is caught, give each registered exception
- translator a chance to translate it to a Python exception. First
- all module-local translators will be tried in reverse order of
- registration. If none of the module-locale translators handle
- the exception (or there are no module-locale translators) then
- the global translators will be tried, also in reverse order of
- registration.
-
- A translator may choose to do one of the following:
-
- - catch the exception and call py::set_error()
- to set a standard (or custom) Python exception, or
- - do nothing and let the exception fall through to the next translator, or
- - delegate translation to the next translator by throwing a new type of exception.
- */
-
- bool handled = with_exception_translators(
- [&](std::forward_list<ExceptionTranslator> &exception_translators,
- std::forward_list<ExceptionTranslator> &local_exception_translators) {
- if (detail::apply_exception_translators(local_exception_translators)) {
- return true;
- }
- if (detail::apply_exception_translators(exception_translators)) {
- return true;
- }
- return false;
- });
-
- if (!handled) {
- set_error(PyExc_SystemError, "Exception escaped from default exception translator!");
- }
-}
+void try_translate_exceptions();
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
+
+#ifndef PYBIND11_PRECOMPILED
+# include "exception_translation-inl.h" // IWYU pragma: export
+#endif
diff --git a/include/pybind11/detail/internals-inl.h b/include/pybind11/detail/internals-inl.h
index ce2c803..ea19984 100644
--- a/include/pybind11/detail/internals-inl.h
+++ b/include/pybind11/detail/internals-inl.h
@@ -19,6 +19,237 @@
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)
+PYBIND11_INLINE object get_python_state_dict() {
+ object state_dict;
+#if defined(PYPY_VERSION) || defined(GRAALVM_PYTHON)
+ state_dict = reinterpret_borrow<object>(PyEval_GetBuiltins());
+#else
+ auto *istate = get_interpreter_state_unchecked();
+ if (istate) {
+ state_dict = reinterpret_borrow<object>(PyInterpreterState_GetDict(istate));
+ }
+#endif
+ if (!state_dict) {
+ raise_from(PyExc_SystemError, "pybind11::detail::get_python_state_dict() FAILED");
+ throw error_already_set();
+ }
+ return state_dict;
+}
+
+PYBIND11_INLINE uint64_t round_up_to_next_pow2(uint64_t x) {
+ // Round-up to the next power of two.
+ // See https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
+ x--;
+ x |= (x >> 1);
+ x |= (x >> 2);
+ x |= (x >> 4);
+ x |= (x >> 8);
+ x |= (x >> 16);
+ x |= (x >> 32);
+ x++;
+ return x;
+}
+
+PYBIND11_INLINE std::atomic_bool &has_seen_non_main_interpreter() {
+ static std::atomic_bool multi(false);
+ return multi;
+}
+
+PYBIND11_INLINE bool raise_err(PyObject *exc_type, const char *msg) {
+ if (PyErr_Occurred()) {
+ raise_from(exc_type, msg);
+ return true;
+ }
+ set_error(exc_type, msg);
+ return false;
+}
+
+PYBIND11_INLINE void translate_exception(std::exception_ptr p) {
+ if (!p) {
+ return;
+ }
+ try {
+ std::rethrow_exception(p);
+ } catch (error_already_set &e) {
+ handle_nested_exception(e, p);
+ e.restore();
+ return;
+ } catch (const builtin_exception &e) {
+ // Could not use template since it's an abstract class.
+ if (const auto *nep = dynamic_cast<const std::nested_exception *>(std::addressof(e))) {
+ handle_nested_exception(*nep, p);
+ }
+ e.set_error();
+ return;
+ } catch (const std::bad_alloc &e) {
+ handle_nested_exception(e, p);
+ raise_err(PyExc_MemoryError, e.what());
+ return;
+ } catch (const std::domain_error &e) {
+ handle_nested_exception(e, p);
+ raise_err(PyExc_ValueError, e.what());
+ return;
+ } catch (const std::invalid_argument &e) {
+ handle_nested_exception(e, p);
+ raise_err(PyExc_ValueError, e.what());
+ return;
+ } catch (const std::length_error &e) {
+ handle_nested_exception(e, p);
+ raise_err(PyExc_ValueError, e.what());
+ return;
+ } catch (const std::out_of_range &e) {
+ handle_nested_exception(e, p);
+ raise_err(PyExc_IndexError, e.what());
+ return;
+ } catch (const std::range_error &e) {
+ handle_nested_exception(e, p);
+ raise_err(PyExc_ValueError, e.what());
+ return;
+ } catch (const std::overflow_error &e) {
+ handle_nested_exception(e, p);
+ raise_err(PyExc_OverflowError, e.what());
+ return;
+ } catch (const std::exception &e) {
+ handle_nested_exception(e, p);
+ raise_err(PyExc_RuntimeError, e.what());
+ return;
+ } catch (const std::nested_exception &e) {
+ handle_nested_exception(e, p);
+ raise_err(PyExc_RuntimeError, "Caught an unknown nested exception!");
+ return;
+ } catch (...) {
+ raise_err(PyExc_RuntimeError, "Caught an unknown exception!");
+ return;
+ }
+}
+
+// Only declared (and used) on non-libstdc++ platforms; see the comment on the
+// declaration in internals.h. Match the guard so precompiled builds do not
+// emit undeclared external-linkage definitions.
+#if !defined(__GLIBCXX__)
+PYBIND11_INLINE void translate_local_exception(std::exception_ptr p) {
+ try {
+ if (p) {
+ std::rethrow_exception(p);
+ }
+ } catch (error_already_set &e) {
+ e.restore();
+ return;
+ } catch (const builtin_exception &e) {
+ e.set_error();
+ return;
+ }
+}
+
+PYBIND11_INLINE void check_internals_local_exception_translator(internals *internals_ptr) {
+ if (internals_ptr) {
+ for (auto et : internals_ptr->registered_exception_translators) {
+ if (et == &translate_local_exception) {
+ return;
+ }
+ }
+ internals_ptr->registered_exception_translators.push_front(&translate_local_exception);
+ }
+}
+#endif
+
+PYBIND11_INLINE internals_pp_manager<internals> &get_internals_pp_manager() {
+#if defined(__GLIBCXX__)
+# define ON_FETCH_FN nullptr
+#else
+# define ON_FETCH_FN &check_internals_local_exception_translator
+#endif
+ return internals_pp_manager<internals>::get_instance(PYBIND11_INTERNALS_ID, ON_FETCH_FN);
+#undef ON_FETCH_FN
+}
+
+PYBIND11_NOINLINE_ATTR PYBIND11_INLINE internals &get_internals() {
+ auto &ppmgr = get_internals_pp_manager();
+ auto *pp = ppmgr.get_pp();
+ if (!pp) {
+ pybind11_fail("get_internals: get_pp() returned nullptr");
+ }
+ auto &internals_ptr = *pp;
+ if (!internals_ptr) {
+ // Slow path, something needs fetched from the state dict or created
+ gil_scoped_acquire_simple gil;
+ error_scope err_scope;
+
+ ppmgr.create_pp_content_once(&internals_ptr);
+
+ if (!internals_ptr) {
+ pybind11_fail("get_internals: create_pp_content_once() produced nullptr");
+ }
+ if (!internals_ptr->instance_base) {
+ // This calls get_internals, so cannot be called from within the internals constructor
+ // called above because internals_ptr must be set before get_internals is called again
+ internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass);
+ }
+ }
+ return *internals_ptr;
+}
+
+PYBIND11_INLINE PyObject *get_internals_capsule() {
+ auto state_dict = reinterpret_borrow<dict>(get_python_state_dict());
+ return dict_getitemstring(state_dict.ptr(), PYBIND11_INTERNALS_ID);
+}
+
+PYBIND11_INLINE const std::string &get_local_internals_key() {
+ static const std::string key
+ = PYBIND11_MODULE_LOCAL_ID + std::to_string(reinterpret_cast<uintptr_t>(&key));
+ return key;
+}
+
+PYBIND11_INLINE PyObject *get_local_internals_capsule() {
+ const auto &key = get_local_internals_key();
+ auto state_dict = reinterpret_borrow<dict>(get_python_state_dict());
+ return dict_getitemstring(state_dict.ptr(), key.c_str());
+}
+
+PYBIND11_INLINE void ensure_internals() {
+ pybind11::detail::get_internals_pp_manager().unref();
+#ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT
+ if (PyInterpreterState_Get() != PyInterpreterState_Main()) {
+ has_seen_non_main_interpreter() = true;
+ }
+#endif
+ pybind11::detail::get_internals();
+}
+
+PYBIND11_INLINE internals_pp_manager<local_internals> &get_local_internals_pp_manager() {
+ // Use the address of a static variable as part of the key, so that the value is uniquely tied
+ // to where the module is loaded in memory
+ return internals_pp_manager<local_internals>::get_instance(get_local_internals_key().c_str(),
+ nullptr);
+}
+
+PYBIND11_INLINE local_internals &get_local_internals() {
+ auto &ppmgr = get_local_internals_pp_manager();
+ auto &internals_ptr = *ppmgr.get_pp();
+ if (!internals_ptr) {
+ gil_scoped_acquire_simple gil;
+ error_scope err_scope;
+
+ ppmgr.create_pp_content_once(&internals_ptr);
+ }
+ return *internals_ptr;
+}
+
+PYBIND11_INLINE size_t num_registered_instances() {
+ auto &internals = get_internals();
+#ifdef Py_GIL_DISABLED
+ size_t count = 0;
+ for (size_t i = 0; i <= internals.instance_shards_mask; ++i) {
+ auto &shard = internals.instance_shards[i];
+ std::unique_lock<pymutex> lock(shard.mutex);
+ count += shard.registered_instances.size();
+ }
+ return count;
+#else
+ return internals.registered_instances.size();
+#endif
+}
+
#if defined(PYBIND11_PRECOMPILED)
// Link-time configuration guard; see the declaration in internals.h.
PYBIND11_INLINE void PYBIND11_PRECOMPILED_CONFIG_CHECK() {}
diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h
index 608bda7..224198c 100644
--- a/include/pybind11/detail/internals.h
+++ b/include/pybind11/detail/internals.h
@@ -199,7 +199,7 @@
PyTypeObject *make_static_property_type();
PyTypeObject *make_default_metaclass();
PyObject *make_object_base_type(PyTypeObject *metaclass);
-inline void translate_exception(std::exception_ptr p);
+void translate_exception(std::exception_ptr p);
inline PyThreadState *get_thread_state_unchecked() {
#if defined(PYPY_VERSION) || defined(GRAALVM_PYTHON)
@@ -216,22 +216,7 @@
return tstate ? tstate->interp : nullptr;
}
-inline object get_python_state_dict() {
- object state_dict;
-#if defined(PYPY_VERSION) || defined(GRAALVM_PYTHON)
- state_dict = reinterpret_borrow<object>(PyEval_GetBuiltins());
-#else
- auto *istate = get_interpreter_state_unchecked();
- if (istate) {
- state_dict = reinterpret_borrow<object>(PyInterpreterState_GetDict(istate));
- }
-#endif
- if (!state_dict) {
- raise_from(PyExc_SystemError, "pybind11::detail::get_python_state_dict() FAILED");
- throw error_already_set();
- }
- return state_dict;
-}
+object get_python_state_dict();
// 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
@@ -339,19 +324,7 @@
static_assert(sizeof(instance_map_shard) % 64 == 0,
"instance_map_shard size is not a multiple of 64 bytes");
-inline uint64_t round_up_to_next_pow2(uint64_t x) {
- // Round-up to the next power of two.
- // See https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
- x--;
- x |= (x >> 1);
- x |= (x >> 2);
- x |= (x >> 4);
- x |= (x >> 8);
- x |= (x >> 16);
- x |= (x >> 32);
- x++;
- return x;
-}
+uint64_t round_up_to_next_pow2(uint64_t x);
#endif
class loader_life_support;
@@ -530,10 +503,7 @@
/// We use this to figure out if there are or have been multiple subinterpreters active at any
/// point. This must never go from true to false while any interpreter may be running in any
/// thread!
-inline std::atomic_bool &has_seen_non_main_interpreter() {
- static std::atomic_bool multi(false);
- return multi;
-}
+std::atomic_bool &has_seen_non_main_interpreter();
template <class T,
enable_if_t<std::is_same<std::nested_exception, remove_cvref_t<T>>::value, int> = 0>
@@ -555,88 +525,12 @@
return false;
}
-inline bool raise_err(PyObject *exc_type, const char *msg) {
- if (PyErr_Occurred()) {
- raise_from(exc_type, msg);
- return true;
- }
- set_error(exc_type, msg);
- return false;
-}
+bool raise_err(PyObject *exc_type, const char *msg);
-inline void translate_exception(std::exception_ptr p) {
- if (!p) {
- return;
- }
- try {
- std::rethrow_exception(p);
- } catch (error_already_set &e) {
- handle_nested_exception(e, p);
- e.restore();
- return;
- } catch (const builtin_exception &e) {
- // Could not use template since it's an abstract class.
- if (const auto *nep = dynamic_cast<const std::nested_exception *>(std::addressof(e))) {
- handle_nested_exception(*nep, p);
- }
- e.set_error();
- return;
- } catch (const std::bad_alloc &e) {
- handle_nested_exception(e, p);
- raise_err(PyExc_MemoryError, e.what());
- return;
- } catch (const std::domain_error &e) {
- handle_nested_exception(e, p);
- raise_err(PyExc_ValueError, e.what());
- return;
- } catch (const std::invalid_argument &e) {
- handle_nested_exception(e, p);
- raise_err(PyExc_ValueError, e.what());
- return;
- } catch (const std::length_error &e) {
- handle_nested_exception(e, p);
- raise_err(PyExc_ValueError, e.what());
- return;
- } catch (const std::out_of_range &e) {
- handle_nested_exception(e, p);
- raise_err(PyExc_IndexError, e.what());
- return;
- } catch (const std::range_error &e) {
- handle_nested_exception(e, p);
- raise_err(PyExc_ValueError, e.what());
- return;
- } catch (const std::overflow_error &e) {
- handle_nested_exception(e, p);
- raise_err(PyExc_OverflowError, e.what());
- return;
- } catch (const std::exception &e) {
- handle_nested_exception(e, p);
- raise_err(PyExc_RuntimeError, e.what());
- return;
- } catch (const std::nested_exception &e) {
- handle_nested_exception(e, p);
- raise_err(PyExc_RuntimeError, "Caught an unknown nested exception!");
- return;
- } catch (...) {
- raise_err(PyExc_RuntimeError, "Caught an unknown exception!");
- return;
- }
-}
+// translate_exception is forward-declared near the top of this header
#if !defined(__GLIBCXX__)
-inline void translate_local_exception(std::exception_ptr p) {
- try {
- if (p) {
- std::rethrow_exception(p);
- }
- } catch (error_already_set &e) {
- e.restore();
- return;
- } catch (const builtin_exception &e) {
- e.set_error();
- return;
- }
-}
+void translate_local_exception(std::exception_ptr p);
#endif
// Sentinel value for the `dtor` parameter of `atomic_get_or_create_in_state_dict`.
@@ -897,109 +791,34 @@
// libc++ with CPython doesn't require this (types are explicitly exported)
// libc++ with PyPy still need it, awaiting further investigation
#if !defined(__GLIBCXX__)
-inline void check_internals_local_exception_translator(internals *internals_ptr) {
- if (internals_ptr) {
- for (auto et : internals_ptr->registered_exception_translators) {
- if (et == &translate_local_exception) {
- return;
- }
- }
- internals_ptr->registered_exception_translators.push_front(&translate_local_exception);
- }
-}
+void check_internals_local_exception_translator(internals *internals_ptr);
#endif
-inline internals_pp_manager<internals> &get_internals_pp_manager() {
-#if defined(__GLIBCXX__)
-# define ON_FETCH_FN nullptr
-#else
-# define ON_FETCH_FN &check_internals_local_exception_translator
-#endif
- return internals_pp_manager<internals>::get_instance(PYBIND11_INTERNALS_ID, ON_FETCH_FN);
-#undef ON_FETCH_FN
-}
+internals_pp_manager<internals> &get_internals_pp_manager();
/// Return a reference to the current `internals` data
-PYBIND11_NOINLINE internals &get_internals() {
- auto &ppmgr = get_internals_pp_manager();
- auto *pp = ppmgr.get_pp();
- if (!pp) {
- pybind11_fail("get_internals: get_pp() returned nullptr");
- }
- auto &internals_ptr = *pp;
- if (!internals_ptr) {
- // Slow path, something needs fetched from the state dict or created
- gil_scoped_acquire_simple gil;
- error_scope err_scope;
-
- ppmgr.create_pp_content_once(&internals_ptr);
-
- if (!internals_ptr) {
- pybind11_fail("get_internals: create_pp_content_once() produced nullptr");
- }
- if (!internals_ptr->instance_base) {
- // This calls get_internals, so cannot be called from within the internals constructor
- // called above because internals_ptr must be set before get_internals is called again
- internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass);
- }
- }
- return *internals_ptr;
-}
+internals &get_internals();
/// Return the PyObject* for the internals capsule (borrowed reference).
/// Returns nullptr if the capsule doesn't exist yet.
-inline PyObject *get_internals_capsule() {
- auto state_dict = reinterpret_borrow<dict>(get_python_state_dict());
- return dict_getitemstring(state_dict.ptr(), PYBIND11_INTERNALS_ID);
-}
+PyObject *get_internals_capsule();
/// Return the key used for local_internals in the state dict.
/// This function ensures a consistent key is used across all call sites within the same
/// compilation unit. The key includes the address of a static variable to make it unique per
/// module (DSO), matching the behavior of get_local_internals_pp_manager().
-inline const std::string &get_local_internals_key() {
- static const std::string key
- = PYBIND11_MODULE_LOCAL_ID + std::to_string(reinterpret_cast<uintptr_t>(&key));
- return key;
-}
+const std::string &get_local_internals_key();
/// Return the PyObject* for the local_internals capsule (borrowed reference).
/// Returns nullptr if the capsule doesn't exist yet.
-inline PyObject *get_local_internals_capsule() {
- const auto &key = get_local_internals_key();
- auto state_dict = reinterpret_borrow<dict>(get_python_state_dict());
- return dict_getitemstring(state_dict.ptr(), key.c_str());
-}
+PyObject *get_local_internals_capsule();
-inline void ensure_internals() {
- pybind11::detail::get_internals_pp_manager().unref();
-#ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT
- if (PyInterpreterState_Get() != PyInterpreterState_Main()) {
- has_seen_non_main_interpreter() = true;
- }
-#endif
- pybind11::detail::get_internals();
-}
+void ensure_internals();
-inline internals_pp_manager<local_internals> &get_local_internals_pp_manager() {
- // Use the address of a static variable as part of the key, so that the value is uniquely tied
- // to where the module is loaded in memory
- return internals_pp_manager<local_internals>::get_instance(get_local_internals_key().c_str(),
- nullptr);
-}
+internals_pp_manager<local_internals> &get_local_internals_pp_manager();
/// Works like `get_internals`, but for things which are locally registered.
-inline local_internals &get_local_internals() {
- auto &ppmgr = get_local_internals_pp_manager();
- auto &internals_ptr = *ppmgr.get_pp();
- if (!internals_ptr) {
- gil_scoped_acquire_simple gil;
- error_scope err_scope;
-
- ppmgr.create_pp_content_once(&internals_ptr);
- }
- return *internals_ptr;
-}
+local_internals &get_local_internals();
#ifdef Py_GIL_DISABLED
# define PYBIND11_LOCK_INTERNALS(internals) pycritical_section lock((internals).mutex)
@@ -1038,6 +857,8 @@
local_internals.registered_exception_translators);
}
+// Stays inline even in precompiled mode: it is called on every instance-map access in
+// free-threaded builds, and an out-of-line call would block inlining and constant propagation.
inline std::uint64_t mix64(std::uint64_t z) {
// David Stafford's variant 13 of the MurmurHash3 finalizer popularized
// by the SplitMix PRNG.
@@ -1073,20 +894,7 @@
// Returns the number of registered instances for testing purposes. The result may not be
// consistent if other threads are registering or unregistering instances concurrently.
-inline size_t num_registered_instances() {
- auto &internals = get_internals();
-#ifdef Py_GIL_DISABLED
- size_t count = 0;
- for (size_t i = 0; i <= internals.instance_shards_mask; ++i) {
- auto &shard = internals.instance_shards[i];
- std::unique_lock<pymutex> lock(shard.mutex);
- count += shard.registered_instances.size();
- }
- return count;
-#else
- return internals.registered_instances.size();
-#endif
-}
+size_t num_registered_instances();
/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its
/// `c_str()`. Such strings objects have a long storage duration -- the internal strings are only
diff --git a/src/common.cpp b/src/common.cpp
new file mode 100644
index 0000000..e5c96b1
--- /dev/null
+++ b/src/common.cpp
@@ -0,0 +1,10 @@
+// Copyright (c) 2025 The Pybind Development Team.
+// All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#if !defined(PYBIND11_PRECOMPILED)
+# error "pybind11 library sources must be compiled with PYBIND11_PRECOMPILED defined."
+#endif
+
+#include <pybind11/detail/common-inl.h>
+#include <pybind11/pybind11.h>
diff --git a/src/exception_translation.cpp b/src/exception_translation.cpp
new file mode 100644
index 0000000..9de21b9
--- /dev/null
+++ b/src/exception_translation.cpp
@@ -0,0 +1,10 @@
+// Copyright (c) 2025 The Pybind Development Team.
+// All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#if !defined(PYBIND11_PRECOMPILED)
+# error "pybind11 library sources must be compiled with PYBIND11_PRECOMPILED defined."
+#endif
+
+#include <pybind11/detail/exception_translation-inl.h>
+#include <pybind11/pybind11.h>
diff --git a/src/pybind11_combined.cpp b/src/pybind11_combined.cpp
index 718ddc8..cc272ab 100644
--- a/src/pybind11_combined.cpp
+++ b/src/pybind11_combined.cpp
@@ -12,6 +12,8 @@
#endif
#include <pybind11/detail/class-inl.h>
+#include <pybind11/detail/common-inl.h>
+#include <pybind11/detail/exception_translation-inl.h>
#include <pybind11/detail/internals-inl.h>
#include <pybind11/detail/type_caster_base-inl.h>
#include <pybind11/pybind11.h>
diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py
index 79af6f6..b43a5cb 100644
--- a/tests/extra_python_package/test_files.py
+++ b/tests/extra_python_package/test_files.py
@@ -84,6 +84,7 @@
"include/pybind11/detail/argument_vector.h",
"include/pybind11/detail/class-inl.h",
"include/pybind11/detail/class.h",
+ "include/pybind11/detail/common-inl.h",
"include/pybind11/detail/common.h",
"include/pybind11/detail/cpp_conduit.h",
"include/pybind11/detail/descr.h",
@@ -102,6 +103,7 @@
"include/pybind11/detail/typeid.h",
"include/pybind11/detail/using_smart_holder.h",
"include/pybind11/detail/value_and_holder.h",
+ "include/pybind11/detail/exception_translation-inl.h",
"include/pybind11/detail/exception_translation.h",
}
@@ -132,6 +134,8 @@
sdist_src_files = {
"src/class.cpp",
+ "src/common.cpp",
+ "src/exception_translation.cpp",
"src/internals.cpp",
"src/type_caster_base.cpp",
"src/pybind11_combined.cpp",