diff --git a/CMakeLists.txt b/CMakeLists.txt index b65529c9b0..3157a57e1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -190,6 +190,7 @@ endif() set(PYBIND11_HEADERS include/pybind11/detail/argument_vector.h + include/pybind11/detail/class-inl.h include/pybind11/detail/class.h include/pybind11/detail/common.h include/pybind11/detail/cpp_conduit.h diff --git a/include/pybind11/detail/class-inl.h b/include/pybind11/detail/class-inl.h new file mode 100644 index 0000000000..57d12fee44 --- /dev/null +++ b/include/pybind11/detail/class-inl.h @@ -0,0 +1,783 @@ +/* + pybind11/detail/class-inl.h: Out-of-line definitions for class.h + + Copyright (c) 2017 Wenzel Jakob + + 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 class.h; when PYBIND11_PRECOMPILED is defined it is only +// compiled into the pybind11 static library (see src/). + +#pragma once + +#include "class.h" + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +PYBIND11_INLINE std::string get_fully_qualified_tp_name(PyTypeObject *type) { +#if !defined(PYPY_VERSION) + return type->tp_name; +#else + auto module_name = handle((PyObject *) type).attr("__module__").cast(); + if (module_name == PYBIND11_BUILTINS_MODULE) + return type->tp_name; + else + return std::move(module_name) + "." + type->tp_name; +#endif +} + +PYBIND11_INLINE PyTypeObject *type_incref(PyTypeObject *type) { + Py_INCREF(type); + return type; +} + +#if !defined(PYPY_VERSION) +extern "C" PYBIND11_INLINE PyObject * +pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls) { + return PyProperty_Type.tp_descr_get(self, cls, cls); +} + +extern "C" PYBIND11_INLINE int +pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value) { + PyObject *cls = PyType_Check(obj) ? obj : (PyObject *) Py_TYPE(obj); + return PyProperty_Type.tp_descr_set(self, cls, value); +} + +PYBIND11_INLINE PyTypeObject *make_static_property_type() { + constexpr auto *name = "pybind11_static_property"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto *heap_type = reinterpret_cast(PyType_Type.tp_alloc(&PyType_Type, 0)); + if (!heap_type) { + pybind11_fail("make_static_property_type(): error allocating type!"); + } + + heap_type->ht_name = name_obj.inc_ref().ptr(); +# ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +# endif + + auto *type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyProperty_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + type->tp_descr_get = pybind11_static_get; + type->tp_descr_set = pybind11_static_set; + +# if PY_VERSION_HEX >= 0x030C0000 + // Since Python-3.12 property-derived types are required to + // have dynamic attributes (to set `__doc__`) + enable_dynamic_attributes(heap_type); +# endif + + if (PyType_Ready(type) < 0) { + pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); + } + + setattr(reinterpret_cast(type), "__module__", str(PYBIND11_DUMMY_MODULE_NAME)); + PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); + + return type; +} + +#else // PYPY +PYBIND11_INLINE PyTypeObject *make_static_property_type() { + auto d = dict(); + PyObject *result = PyRun_String(R"(\ +class pybind11_static_property(property): + def __get__(self, obj, cls): + return property.__get__(self, cls, cls) + + def __set__(self, obj, value): + cls = obj if isinstance(obj, type) else type(obj) + property.__set__(self, cls, value) +)", + Py_file_input, + d.ptr(), + d.ptr()); + if (result == nullptr) + throw error_already_set(); + Py_DECREF(result); + return (PyTypeObject *) d["pybind11_static_property"].cast().release().ptr(); +} + +#endif // PYPY +extern "C" PYBIND11_INLINE int +pybind11_meta_setattro(PyObject *obj, PyObject *name, PyObject *value) { + // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw + // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + + // The following assignment combinations are possible: + // 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)` + // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` + // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment + auto *const static_prop = (PyObject *) get_internals().static_property_type; + const auto call_descr_set = (descr != nullptr) && (value != nullptr) + && (PyObject_IsInstance(descr, static_prop) != 0) + && (PyObject_IsInstance(value, static_prop) == 0); + if (call_descr_set) { + // Call `static_property.__set__()` instead of replacing the `static_property`. +#if !defined(PYPY_VERSION) + return Py_TYPE(descr)->tp_descr_set(descr, obj, value); +#else + if (PyObject *result = PyObject_CallMethod(descr, "__set__", "OO", obj, value)) { + Py_DECREF(result); + return 0; + } else { + return -1; + } +#endif + } else { + // Replace existing attribute. + return PyType_Type.tp_setattro(obj, name, value); + } +} + +extern "C" PYBIND11_INLINE PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) { + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + if (descr && PyInstanceMethod_Check(descr)) { + Py_INCREF(descr); + return descr; + } + return PyType_Type.tp_getattro(obj, name); +} + +extern "C" PYBIND11_INLINE PyObject * +pybind11_meta_call(PyObject *type, PyObject *args, PyObject *kwargs) { + + // use the default metaclass call to create/initialize the object + PyObject *self = PyType_Type.tp_call(type, args, kwargs); + if (self == nullptr) { + return nullptr; + } + + // Ensure that the base __init__ function(s) were called + values_and_holders vhs(self); + for (const auto &vh : vhs) { + if (!vh.holder_constructed() && !vhs.is_redundant_value_and_holder(vh)) { + PyErr_Format(PyExc_TypeError, + "%.200s.__init__() must be called when overriding __init__", + get_fully_qualified_tp_name(vh.type->type).c_str()); + Py_DECREF(self); + return nullptr; + } + } + + return self; +} + +extern "C" PYBIND11_INLINE void pybind11_meta_dealloc(PyObject *obj) { + with_internals_if_internals([obj](internals &internals) { + auto *type = (PyTypeObject *) obj; + + // A pybind11-registered type will: + // 1) be found in internals.registered_types_py + // 2) have exactly one associated `detail::type_info` + auto found_type = internals.registered_types_py.find(type); + if (found_type != internals.registered_types_py.end() && found_type->second.size() == 1 + && found_type->second[0]->type == type) { + + auto *tinfo = found_type->second[0]; + auto tindex = std::type_index(*tinfo->cpptype); + internals.direct_conversions.erase(tindex); + + auto &local_internals = get_local_internals(); + if (tinfo->module_local) { + local_internals.registered_types_cpp.erase(tinfo->cpptype); + } else { + internals.registered_types_cpp.erase(tindex); +#if PYBIND11_INTERNALS_VERSION >= 12 + internals.registered_types_cpp_fast.erase(tinfo->cpptype); + for (const std::type_info *alias : tinfo->alias_chain) { + auto num_erased = internals.registered_types_cpp_fast.erase(alias); + (void) num_erased; + assert(num_erased > 0); + } +#endif + } + internals.registered_types_py.erase(tinfo->type); + + // Actually just `std::erase_if`, but that's only available in C++20 + auto &cache = internals.inactive_override_cache; + for (auto it = cache.begin(), last = cache.end(); it != last;) { + if (it->first == (PyObject *) tinfo->type) { + it = cache.erase(it); + } else { + ++it; + } + } + + delete tinfo; + } + }); + + PyType_Type.tp_dealloc(obj); +} + +PYBIND11_INLINE PyTypeObject *make_default_metaclass() { + constexpr auto *name = "pybind11_type"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto *heap_type = reinterpret_cast(PyType_Type.tp_alloc(&PyType_Type, 0)); + if (!heap_type) { + pybind11_fail("make_default_metaclass(): error allocating metaclass!"); + } + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto *type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyType_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_call = pybind11_meta_call; + + type->tp_setattro = pybind11_meta_setattro; + type->tp_getattro = pybind11_meta_getattro; + + type->tp_dealloc = pybind11_meta_dealloc; + + if (PyType_Ready(type) < 0) { + pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); + } + + setattr(reinterpret_cast(type), "__module__", str(PYBIND11_DUMMY_MODULE_NAME)); + PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); + + return type; +} + +PYBIND11_INLINE void traverse_offset_bases(void *valueptr, + const detail::type_info *tinfo, + instance *self, + bool (*f)(void * /*parentptr*/, instance * /*self*/)) { + for (handle h : reinterpret_borrow(tinfo->type->tp_bases)) { + if (auto *parent_tinfo = get_type_info(reinterpret_cast(h.ptr()))) { + for (auto &c : parent_tinfo->implicit_casts) { + if (c.first == tinfo->cpptype) { + auto *parentptr = c.second(valueptr); + if (parentptr != valueptr) { + f(parentptr, self); + } + traverse_offset_bases(parentptr, parent_tinfo, self, f); + break; + } + } + } + } +} + +#ifdef Py_GIL_DISABLED +PYBIND11_INLINE void enable_try_inc_ref(PyObject *obj) { +# if PY_VERSION_HEX >= 0x030E00A4 + PyUnstable_EnableTryIncRef(obj); +# else + if (_Py_IsImmortal(obj)) { + return; + } + for (;;) { + Py_ssize_t shared = _Py_atomic_load_ssize_relaxed(&obj->ob_ref_shared); + if ((shared & _Py_REF_SHARED_FLAG_MASK) != 0) { + // Nothing to do if it's in WEAKREFS, QUEUED, or MERGED states. + return; + } + if (_Py_atomic_compare_exchange_ssize( + &obj->ob_ref_shared, &shared, shared | _Py_REF_MAYBE_WEAKREF)) { + return; + } + } +# endif +} + +#endif +PYBIND11_INLINE bool register_instance_impl(void *ptr, instance *self) { + assert(ptr); +#ifdef Py_GIL_DISABLED + enable_try_inc_ref(reinterpret_cast(self)); +#endif + with_instance_map(ptr, [&](instance_map &instances) { instances.emplace(ptr, self); }); + return true; // unused, but gives the same signature as the deregister func +} + +PYBIND11_INLINE bool deregister_instance_impl(void *ptr, instance *self) { + assert(ptr); + return with_instance_map(ptr, [&](instance_map &instances) { + auto range = instances.equal_range(ptr); + for (auto it = range.first; it != range.second; ++it) { + if (self == it->second) { + instances.erase(it); + return true; + } + } + return false; + }); +} + +PYBIND11_INLINE void register_instance(instance *self, void *valptr, const type_info *tinfo) { + register_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) { + traverse_offset_bases(valptr, tinfo, self, register_instance_impl); + } +} + +PYBIND11_INLINE bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { + bool ret = deregister_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) { + traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); + } + return ret; +} + +PYBIND11_INLINE PyObject *make_new_instance(PyTypeObject *type) { +#if defined(PYPY_VERSION) + // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first + // inherited object is a plain Python type (i.e. not derived from an extension type). Fix it. + ssize_t instance_size = static_cast(sizeof(instance)); + if (type->tp_basicsize < instance_size) { + type->tp_basicsize = instance_size; + } +#endif + PyObject *self = type->tp_alloc(type, 0); + auto *inst = reinterpret_cast(self); + // Allocate the value/holder internals: + inst->allocate_layout(); + + return self; +} + +extern "C" PYBIND11_INLINE PyObject * +pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *) { + return make_new_instance(type); +} + +extern "C" PYBIND11_INLINE int pybind11_object_init(PyObject *self, PyObject *, PyObject *) { + PyTypeObject *type = Py_TYPE(self); + std::string msg = get_fully_qualified_tp_name(type) + ": No constructor defined!"; + set_error(PyExc_TypeError, msg.c_str()); + return -1; +} + +PYBIND11_INLINE void add_patient(PyObject *nurse, PyObject *patient) { + auto *instance = reinterpret_cast(nurse); + instance->has_patients = true; + Py_INCREF(patient); + + with_internals([&](internals &internals) { internals.patients[nurse].push_back(patient); }); +} + +PYBIND11_INLINE void clear_patients(PyObject *self) { + auto *instance = reinterpret_cast(self); + std::vector patients; + + with_internals([&](internals &internals) { + auto pos = internals.patients.find(self); + + if (pos == internals.patients.end()) { + pybind11_fail( + "FATAL: Internal consistency check failed: Invalid clear_patients() call."); + } + + // Clearing the patients can cause more Python code to run, which + // can invalidate the iterator. Extract the vector of patients + // from the unordered_map first. + patients = std::move(pos->second); + internals.patients.erase(pos); + }); + + instance->has_patients = false; + for (PyObject *&patient : patients) { + Py_CLEAR(patient); + } +} + +PYBIND11_INLINE void clear_instance(PyObject *self) { + auto *instance = reinterpret_cast(self); + + // Deallocate any values/holders, if present: + for (auto &v_h : values_and_holders(instance)) { + if (v_h) { + + // We have to deregister before we call dealloc because, for virtual MI types, we still + // need to be able to get the parent pointers. + if (v_h.instance_registered() + && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) { + pybind11_fail( + "pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); + } + + if (instance->owned || v_h.holder_constructed()) { + v_h.type->dealloc(v_h); + } + } else if (v_h.holder_constructed()) { + v_h.type->dealloc(v_h); // Disowned instance. + } + } + // Deallocate the value/holder layout internals: + instance->deallocate_layout(); + + if (instance->weakrefs) { + PyObject_ClearWeakRefs(self); + } + + PyObject **dict_ptr = _PyObject_GetDictPtr(self); + if (dict_ptr) { + Py_CLEAR(*dict_ptr); + } + + if (instance->has_patients) { + clear_patients(self); + } +} + +extern "C" PYBIND11_INLINE void pybind11_object_dealloc(PyObject *self) { + auto *type = Py_TYPE(self); + + // If this is a GC tracked object, untrack it first + // Note that the track call is implicitly done by the + // default tp_alloc, which we never override. + if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) != 0) { + PyObject_GC_UnTrack(self); + } + +#if PY_VERSION_HEX >= 0x030D0000 + // PyObject_ClearManagedDict() is available from Python 3.13+. It must be + // called before tp_free() because on Python 3.14+ tp_free no longer + // implicitly clears the managed dict, which would abandon the refcounts of + // objects stored in __dict__ of py::dynamic_attr() types, causing permanent + // memory leaks. + if (PyType_HasFeature(type, Py_TPFLAGS_MANAGED_DICT)) { + PyObject_ClearManagedDict(self); + } +#endif + + clear_instance(self); + + type->tp_free(self); + + // This was not needed before Python 3.8 (Python issue 35810) + // https://github.com/pybind/pybind11/issues/1946 + Py_DECREF(type); +} + +PYBIND11_INLINE PyObject *make_object_base_type(PyTypeObject *metaclass) { + constexpr auto *name = "pybind11_object"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto *heap_type = reinterpret_cast(metaclass->tp_alloc(metaclass, 0)); + if (!heap_type) { + pybind11_fail("make_object_base_type(): error allocating type!"); + } + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto *type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyBaseObject_Type); + type->tp_basicsize = static_cast(sizeof(instance)); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_new = pybind11_object_new; + type->tp_init = pybind11_object_init; + type->tp_dealloc = pybind11_object_dealloc; + + /* Support weak references (needed for the keep_alive feature) */ + type->tp_weaklistoffset = offsetof(instance, weakrefs); + + if (PyType_Ready(type) < 0) { + pybind11_fail("PyType_Ready failed in make_object_base_type(): " + error_string()); + } + + setattr(reinterpret_cast(type), "__module__", str(PYBIND11_DUMMY_MODULE_NAME)); + PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); + + assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + return reinterpret_cast(heap_type); +} + +extern "C" PYBIND11_INLINE int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { +#if PY_VERSION_HEX >= 0x030D0000 + int ret = PyObject_VisitManagedDict(self, visit, arg); + if (ret) { + return ret; + } +#else + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_VISIT(dict); +#endif + // https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse + Py_VISIT(Py_TYPE(self)); + return 0; +} + +extern "C" PYBIND11_INLINE int pybind11_clear(PyObject *self) { +#if PY_VERSION_HEX >= 0x030D0000 + PyObject_ClearManagedDict(self); +#else + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_CLEAR(dict); +#endif + return 0; +} + +PYBIND11_INLINE void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { + auto *type = &heap_type->ht_type; + type->tp_flags |= Py_TPFLAGS_HAVE_GC; +#ifdef PYBIND11_BACKWARD_COMPATIBILITY_TP_DICTOFFSET + type->tp_dictoffset = type->tp_basicsize; // place dict at the end + type->tp_basicsize += (ssize_t) sizeof(PyObject *); // and allocate enough space for it +#else + type->tp_flags |= Py_TPFLAGS_MANAGED_DICT; +#endif + type->tp_traverse = pybind11_traverse; + type->tp_clear = pybind11_clear; + + static PyGetSetDef getset[] + = {{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict, nullptr, nullptr}, + {nullptr, nullptr, nullptr, nullptr, nullptr}}; + type->tp_getset = getset; +} + +extern "C" PYBIND11_INLINE int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) { + // Look for a `get_buffer` implementation in this type's info or any bases (following MRO). + type_info *tinfo = nullptr; + for (auto type : reinterpret_borrow(Py_TYPE(obj)->tp_mro)) { + tinfo = get_type_info((PyTypeObject *) type.ptr()); + if (tinfo && tinfo->get_buffer) { + break; + } + } + if (view == nullptr || !tinfo || !tinfo->get_buffer) { + if (view) { + view->obj = nullptr; + } + set_error(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); + return -1; + } + std::memset(view, 0, sizeof(Py_buffer)); + std::unique_ptr info = nullptr; + try { + info.reset(tinfo->get_buffer(obj, tinfo->get_buffer_data)); + } catch (...) { + try_translate_exceptions(); + raise_from(PyExc_BufferError, "Error getting buffer"); + return -1; + } + if (info == nullptr) { + pybind11_fail("FATAL UNEXPECTED SITUATION: tinfo->get_buffer() returned nullptr."); + } + + if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) { + // view->obj = nullptr; // Was just memset to 0, so not necessary + set_error(PyExc_BufferError, "Writable buffer requested for readonly storage"); + return -1; + } + + // Fill in all the information, and then downgrade as requested by the caller, or raise an + // error if that's not possible. + view->itemsize = info->itemsize; + view->len = view->itemsize; + for (auto s : info->shape) { + view->len *= s; + } + view->ndim = static_cast(info->ndim); + view->shape = info->shape.data(); + view->strides = info->strides.data(); + view->readonly = static_cast(info->readonly); + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) { + view->format = const_cast(info->format.c_str()); + } + + // Note, all contiguity flags imply PyBUF_STRIDES and lower. + if ((flags & PyBUF_C_CONTIGUOUS) == PyBUF_C_CONTIGUOUS) { + if (PyBuffer_IsContiguous(view, 'C') == 0) { + std::memset(view, 0, sizeof(Py_buffer)); + set_error(PyExc_BufferError, + "C-contiguous buffer requested for discontiguous storage"); + return -1; + } + } else if ((flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS) { + if (PyBuffer_IsContiguous(view, 'F') == 0) { + std::memset(view, 0, sizeof(Py_buffer)); + set_error(PyExc_BufferError, + "Fortran-contiguous buffer requested for discontiguous storage"); + return -1; + } + } else if ((flags & PyBUF_ANY_CONTIGUOUS) == PyBUF_ANY_CONTIGUOUS) { + if (PyBuffer_IsContiguous(view, 'A') == 0) { + std::memset(view, 0, sizeof(Py_buffer)); + set_error(PyExc_BufferError, "Contiguous buffer requested for discontiguous storage"); + return -1; + } + + } else if ((flags & PyBUF_STRIDES) != PyBUF_STRIDES) { + // If no strides are requested, the buffer must be C-contiguous. + // https://docs.python.org/3/c-api/buffer.html#contiguity-requests + if (PyBuffer_IsContiguous(view, 'C') == 0) { + std::memset(view, 0, sizeof(Py_buffer)); + set_error(PyExc_BufferError, + "C-contiguous buffer requested for discontiguous storage"); + return -1; + } + + view->strides = nullptr; + + // Since this is a contiguous buffer, it can also pretend to be 1D. + if ((flags & PyBUF_ND) != PyBUF_ND) { + view->shape = nullptr; + view->ndim = 0; + } + } + + // Set these after all checks so they don't leak out into the caller, and can be automatically + // cleaned up on error. + view->buf = info->ptr; + view->internal = info.release(); + view->obj = obj; + Py_INCREF(view->obj); + return 0; +} + +extern "C" PYBIND11_INLINE void pybind11_releasebuffer(PyObject *, Py_buffer *view) { + delete (buffer_info *) view->internal; +} + +PYBIND11_INLINE void enable_buffer_protocol(PyHeapTypeObject *heap_type) { + heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; + + heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; + heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; +} + +PYBIND11_INLINE PyObject *make_new_python_type(const type_record &rec) { + auto name = reinterpret_steal(PYBIND11_FROM_STRING(rec.name)); + + auto qualname = name; + if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) { + qualname = reinterpret_steal( + PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); + } + + object module_ = get_module_name_if_available(rec.scope); + const auto *full_name = c_str( +#if !defined(PYPY_VERSION) + module_ ? str(module_).cast() + "." + rec.name : +#endif + rec.name); + + char *tp_doc = nullptr; + if (rec.doc && options::show_user_defined_docstrings()) { + /* Allocate memory for docstring (Python will free this later on) */ + size_t size = std::strlen(rec.doc) + 1; +#if PY_VERSION_HEX >= 0x030D0000 + tp_doc = static_cast(PyMem_MALLOC(size)); +#else + tp_doc = (char *) PyObject_MALLOC(size); +#endif + std::memcpy((void *) tp_doc, rec.doc, size); + } + + auto &internals = get_internals(); + auto bases = tuple(rec.bases); + auto *base = (bases.empty()) ? internals.instance_base : bases[0].ptr(); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto *metaclass = rec.metaclass.ptr() ? reinterpret_cast(rec.metaclass.ptr()) + : internals.default_metaclass; + + auto *heap_type = reinterpret_cast(metaclass->tp_alloc(metaclass, 0)); + if (!heap_type) { + pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); + } + + heap_type->ht_name = name.release().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = qualname.inc_ref().ptr(); +#endif + + auto *type = &heap_type->ht_type; + type->tp_name = full_name; + type->tp_doc = tp_doc; + type->tp_base = type_incref(reinterpret_cast(base)); + type->tp_basicsize = static_cast(sizeof(instance)); + if (!bases.empty()) { + type->tp_bases = bases.release().ptr(); + } + + /* Don't inherit base __init__ */ + type->tp_init = pybind11_object_init; + + /* Supported protocols */ + type->tp_as_number = &heap_type->as_number; + type->tp_as_sequence = &heap_type->as_sequence; + type->tp_as_mapping = &heap_type->as_mapping; + type->tp_as_async = &heap_type->as_async; + + /* Flags */ + type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE; + if (!rec.is_final) { + type->tp_flags |= Py_TPFLAGS_BASETYPE; + } + + if (rec.dynamic_attr) { + enable_dynamic_attributes(heap_type); + } + + if (rec.buffer_protocol) { + enable_buffer_protocol(heap_type); + } + + if (rec.custom_type_setup_callback) { + rec.custom_type_setup_callback(heap_type); + } + + if (PyType_Ready(type) < 0) { + pybind11_fail(std::string(rec.name) + ": PyType_Ready failed: " + error_string()); + } + + assert(!rec.dynamic_attr || PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + + /* Register type with the parent scope */ + if (rec.scope) { + setattr(rec.scope, rec.name, reinterpret_cast(type)); + } else { + Py_INCREF(type); // Keep it alive forever (reference leak) + } + + if (module_) { // Needed by pydoc + setattr(reinterpret_cast(type), "__module__", module_); + } + + PYBIND11_SET_OLDPY_QUALNAME(type, qualname); + + return reinterpret_cast(type); +} + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/detail/class.h b/include/pybind11/detail/class.h index 06a761de54..edc4044869 100644 --- a/include/pybind11/detail/class.h +++ b/include/pybind11/detail/class.h @@ -27,107 +27,38 @@ PYBIND11_NAMESPACE_BEGIN(detail) setattr((PyObject *) obj, "__qualname__", nameobj) #endif -inline std::string get_fully_qualified_tp_name(PyTypeObject *type) { -#if !defined(PYPY_VERSION) - return type->tp_name; -#else - auto module_name = handle((PyObject *) type).attr("__module__").cast(); - if (module_name == PYBIND11_BUILTINS_MODULE) - return type->tp_name; - else - return std::move(module_name) + "." + type->tp_name; -#endif -} +PYBIND11_WARNING_PUSH +// Several of these functions are forward-declared in other headers (internals.h, +// type_caster_base.h, trampoline_self_life_support.h, cpp_conduit.h), which cannot +// include this file; the declarations here are the canonical set. +PYBIND11_WARNING_DISABLE_GCC("-Wredundant-decls") -inline PyTypeObject *type_incref(PyTypeObject *type) { - Py_INCREF(type); - return type; -} +std::string get_fully_qualified_tp_name(PyTypeObject *type); + +PyTypeObject *type_incref(PyTypeObject *type); #if !defined(PYPY_VERSION) /// `pybind11_static_property.__get__()`: Always pass the class instead of the instance. -extern "C" inline PyObject *pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls) { - return PyProperty_Type.tp_descr_get(self, cls, cls); -} +extern "C" PyObject *pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls); /// `pybind11_static_property.__set__()`: Just like the above `__get__()`. -extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value) { - PyObject *cls = PyType_Check(obj) ? obj : (PyObject *) Py_TYPE(obj); - return PyProperty_Type.tp_descr_set(self, cls, value); -} +extern "C" int pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value); // Forward declaration to use in `make_static_property_type()` -inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type); +void enable_dynamic_attributes(PyHeapTypeObject *heap_type); /** A `static_property` is the same as a `property` but the `__get__()` and `__set__()` methods are modified to always use the object type instead of a concrete instance. Return value: New reference. */ -inline PyTypeObject *make_static_property_type() { - constexpr auto *name = "pybind11_static_property"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto *heap_type = reinterpret_cast(PyType_Type.tp_alloc(&PyType_Type, 0)); - if (!heap_type) { - pybind11_fail("make_static_property_type(): error allocating type!"); - } - - heap_type->ht_name = name_obj.inc_ref().ptr(); -# ifdef PYBIND11_BUILTIN_QUALNAME - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -# endif - - auto *type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyProperty_Type); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - type->tp_descr_get = pybind11_static_get; - type->tp_descr_set = pybind11_static_set; - -# if PY_VERSION_HEX >= 0x030C0000 - // Since Python-3.12 property-derived types are required to - // have dynamic attributes (to set `__doc__`) - enable_dynamic_attributes(heap_type); -# endif - - if (PyType_Ready(type) < 0) { - pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); - } - - setattr(reinterpret_cast(type), "__module__", str(PYBIND11_DUMMY_MODULE_NAME)); - PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); - - return type; -} +PyTypeObject *make_static_property_type(); #else // PYPY /** PyPy has some issues with the above C API, so we evaluate Python code instead. This function will only be called once so performance isn't really a concern. Return value: New reference. */ -inline PyTypeObject *make_static_property_type() { - auto d = dict(); - PyObject *result = PyRun_String(R"(\ -class pybind11_static_property(property): - def __get__(self, obj, cls): - return property.__get__(self, cls, cls) - - def __set__(self, obj, value): - cls = obj if isinstance(obj, type) else type(obj) - property.__set__(self, cls, value) -)", - Py_file_input, - d.ptr(), - d.ptr()); - if (result == nullptr) - throw error_already_set(); - Py_DECREF(result); - return (PyTypeObject *) d["pybind11_static_property"].cast().release().ptr(); -} +PyTypeObject *make_static_property_type(); #endif // PYPY @@ -135,36 +66,7 @@ class pybind11_static_property(property): By default, Python replaces the `static_property` itself, but for wrapped C++ types we need to call `static_property.__set__()` in order to propagate the new value to the underlying C++ data structure. */ -extern "C" inline int pybind11_meta_setattro(PyObject *obj, PyObject *name, PyObject *value) { - // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw - // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). - PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); - - // The following assignment combinations are possible: - // 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)` - // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` - // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment - auto *const static_prop = (PyObject *) get_internals().static_property_type; - const auto call_descr_set = (descr != nullptr) && (value != nullptr) - && (PyObject_IsInstance(descr, static_prop) != 0) - && (PyObject_IsInstance(value, static_prop) == 0); - if (call_descr_set) { - // Call `static_property.__set__()` instead of replacing the `static_property`. -#if !defined(PYPY_VERSION) - return Py_TYPE(descr)->tp_descr_set(descr, obj, value); -#else - if (PyObject *result = PyObject_CallMethod(descr, "__set__", "OO", obj, value)) { - Py_DECREF(result); - return 0; - } else { - return -1; - } -#endif - } else { - // Replace existing attribute. - return PyType_Type.tp_setattro(obj, name, value); - } -} +extern "C" int pybind11_meta_setattro(PyObject *obj, PyObject *name, PyObject *value); /** * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing @@ -172,356 +74,64 @@ extern "C" inline int pybind11_meta_setattro(PyObject *obj, PyObject *name, PyOb * when called on a class, or a PyMethod, when called on an instance. Override that behaviour here * to do a special case bypass for PyInstanceMethod_Types. */ -extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) { - PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); - if (descr && PyInstanceMethod_Check(descr)) { - Py_INCREF(descr); - return descr; - } - return PyType_Type.tp_getattro(obj, name); -} +extern "C" PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name); /// metaclass `__call__` function that is used to create all pybind11 objects. -extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, PyObject *kwargs) { - - // use the default metaclass call to create/initialize the object - PyObject *self = PyType_Type.tp_call(type, args, kwargs); - if (self == nullptr) { - return nullptr; - } - - // Ensure that the base __init__ function(s) were called - values_and_holders vhs(self); - for (const auto &vh : vhs) { - if (!vh.holder_constructed() && !vhs.is_redundant_value_and_holder(vh)) { - PyErr_Format(PyExc_TypeError, - "%.200s.__init__() must be called when overriding __init__", - get_fully_qualified_tp_name(vh.type->type).c_str()); - Py_DECREF(self); - return nullptr; - } - } - - return self; -} +extern "C" PyObject *pybind11_meta_call(PyObject *type, PyObject *args, PyObject *kwargs); /// Cleanup the type-info for a pybind11-registered type. -extern "C" inline void pybind11_meta_dealloc(PyObject *obj) { - with_internals_if_internals([obj](internals &internals) { - auto *type = (PyTypeObject *) obj; - - // A pybind11-registered type will: - // 1) be found in internals.registered_types_py - // 2) have exactly one associated `detail::type_info` - auto found_type = internals.registered_types_py.find(type); - if (found_type != internals.registered_types_py.end() && found_type->second.size() == 1 - && found_type->second[0]->type == type) { - - auto *tinfo = found_type->second[0]; - auto tindex = std::type_index(*tinfo->cpptype); - internals.direct_conversions.erase(tindex); - - auto &local_internals = get_local_internals(); - if (tinfo->module_local) { - local_internals.registered_types_cpp.erase(tinfo->cpptype); - } else { - internals.registered_types_cpp.erase(tindex); -#if PYBIND11_INTERNALS_VERSION >= 12 - internals.registered_types_cpp_fast.erase(tinfo->cpptype); - for (const std::type_info *alias : tinfo->alias_chain) { - auto num_erased = internals.registered_types_cpp_fast.erase(alias); - (void) num_erased; - assert(num_erased > 0); - } -#endif - } - internals.registered_types_py.erase(tinfo->type); - - // Actually just `std::erase_if`, but that's only available in C++20 - auto &cache = internals.inactive_override_cache; - for (auto it = cache.begin(), last = cache.end(); it != last;) { - if (it->first == (PyObject *) tinfo->type) { - it = cache.erase(it); - } else { - ++it; - } - } - - delete tinfo; - } - }); - - PyType_Type.tp_dealloc(obj); -} +extern "C" void pybind11_meta_dealloc(PyObject *obj); /** This metaclass is assigned by default to all pybind11 types and is required in order for static properties to function correctly. Users may override this using `py::metaclass`. Return value: New reference. */ -inline PyTypeObject *make_default_metaclass() { - constexpr auto *name = "pybind11_type"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto *heap_type = reinterpret_cast(PyType_Type.tp_alloc(&PyType_Type, 0)); - if (!heap_type) { - pybind11_fail("make_default_metaclass(): error allocating metaclass!"); - } - - heap_type->ht_name = name_obj.inc_ref().ptr(); -#ifdef PYBIND11_BUILTIN_QUALNAME - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -#endif - - auto *type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyType_Type); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - - type->tp_call = pybind11_meta_call; - - type->tp_setattro = pybind11_meta_setattro; - type->tp_getattro = pybind11_meta_getattro; - - type->tp_dealloc = pybind11_meta_dealloc; - - if (PyType_Ready(type) < 0) { - pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); - } - - setattr(reinterpret_cast(type), "__module__", str(PYBIND11_DUMMY_MODULE_NAME)); - PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); - - return type; -} +PyTypeObject *make_default_metaclass(); /// For multiple inheritance types we need to recursively register/deregister base pointers for any /// base classes with pointers that are difference from the instance value pointer so that we can /// correctly recognize an offset base class pointer. This calls a function with any offset base /// ptrs. -inline void traverse_offset_bases(void *valueptr, - const detail::type_info *tinfo, - instance *self, - bool (*f)(void * /*parentptr*/, instance * /*self*/)) { - for (handle h : reinterpret_borrow(tinfo->type->tp_bases)) { - if (auto *parent_tinfo = get_type_info(reinterpret_cast(h.ptr()))) { - for (auto &c : parent_tinfo->implicit_casts) { - if (c.first == tinfo->cpptype) { - auto *parentptr = c.second(valueptr); - if (parentptr != valueptr) { - f(parentptr, self); - } - traverse_offset_bases(parentptr, parent_tinfo, self, f); - break; - } - } - } - } -} +void traverse_offset_bases(void *valueptr, + const detail::type_info *tinfo, + instance *self, + bool (*f)(void * /*parentptr*/, instance * /*self*/)); #ifdef Py_GIL_DISABLED -inline void enable_try_inc_ref(PyObject *obj) { -# if PY_VERSION_HEX >= 0x030E00A4 - PyUnstable_EnableTryIncRef(obj); -# else - if (_Py_IsImmortal(obj)) { - return; - } - for (;;) { - Py_ssize_t shared = _Py_atomic_load_ssize_relaxed(&obj->ob_ref_shared); - if ((shared & _Py_REF_SHARED_FLAG_MASK) != 0) { - // Nothing to do if it's in WEAKREFS, QUEUED, or MERGED states. - return; - } - if (_Py_atomic_compare_exchange_ssize( - &obj->ob_ref_shared, &shared, shared | _Py_REF_MAYBE_WEAKREF)) { - return; - } - } -# endif -} +void enable_try_inc_ref(PyObject *obj); #endif -inline bool register_instance_impl(void *ptr, instance *self) { - assert(ptr); -#ifdef Py_GIL_DISABLED - enable_try_inc_ref(reinterpret_cast(self)); -#endif - with_instance_map(ptr, [&](instance_map &instances) { instances.emplace(ptr, self); }); - return true; // unused, but gives the same signature as the deregister func -} -inline bool deregister_instance_impl(void *ptr, instance *self) { - assert(ptr); - return with_instance_map(ptr, [&](instance_map &instances) { - auto range = instances.equal_range(ptr); - for (auto it = range.first; it != range.second; ++it) { - if (self == it->second) { - instances.erase(it); - return true; - } - } - return false; - }); -} - -inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { - register_instance_impl(valptr, self); - if (!tinfo->simple_ancestors) { - traverse_offset_bases(valptr, tinfo, self, register_instance_impl); - } -} - -inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { - bool ret = deregister_instance_impl(valptr, self); - if (!tinfo->simple_ancestors) { - traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); - } - return ret; -} +bool register_instance_impl(void *ptr, instance *self); +bool deregister_instance_impl(void *ptr, instance *self); + +void register_instance(instance *self, void *valptr, const type_info *tinfo); + +bool deregister_instance(instance *self, void *valptr, const type_info *tinfo); /// Instance creation function for all pybind11 types. It allocates the internal instance layout /// for holding C++ objects and holders. Allocation is done lazily (the first time the instance is /// cast to a reference or pointer), and initialization is done by an `__init__` function. -inline PyObject *make_new_instance(PyTypeObject *type) { -#if defined(PYPY_VERSION) - // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first - // inherited object is a plain Python type (i.e. not derived from an extension type). Fix it. - ssize_t instance_size = static_cast(sizeof(instance)); - if (type->tp_basicsize < instance_size) { - type->tp_basicsize = instance_size; - } -#endif - PyObject *self = type->tp_alloc(type, 0); - auto *inst = reinterpret_cast(self); - // Allocate the value/holder internals: - inst->allocate_layout(); - - return self; -} +PyObject *make_new_instance(PyTypeObject *type); /// Instance creation function for all pybind11 types. It only allocates space for the /// C++ object, but doesn't call the constructor -- an `__init__` function must do that. -extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *) { - return make_new_instance(type); -} +extern "C" PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *); /// An `__init__` function constructs the C++ object. Users should provide at least one /// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the /// following default function will be used which simply throws an exception. -extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) { - PyTypeObject *type = Py_TYPE(self); - std::string msg = get_fully_qualified_tp_name(type) + ": No constructor defined!"; - set_error(PyExc_TypeError, msg.c_str()); - return -1; -} - -inline void add_patient(PyObject *nurse, PyObject *patient) { - auto *instance = reinterpret_cast(nurse); - instance->has_patients = true; - Py_INCREF(patient); - - with_internals([&](internals &internals) { internals.patients[nurse].push_back(patient); }); -} - -inline void clear_patients(PyObject *self) { - auto *instance = reinterpret_cast(self); - std::vector patients; - - with_internals([&](internals &internals) { - auto pos = internals.patients.find(self); - - if (pos == internals.patients.end()) { - pybind11_fail( - "FATAL: Internal consistency check failed: Invalid clear_patients() call."); - } - - // Clearing the patients can cause more Python code to run, which - // can invalidate the iterator. Extract the vector of patients - // from the unordered_map first. - patients = std::move(pos->second); - internals.patients.erase(pos); - }); - - instance->has_patients = false; - for (PyObject *&patient : patients) { - Py_CLEAR(patient); - } -} +extern "C" int pybind11_object_init(PyObject *self, PyObject *, PyObject *); + +void add_patient(PyObject *nurse, PyObject *patient); + +void clear_patients(PyObject *self); /// Clears all internal data from the instance and removes it from registered instances in /// preparation for deallocation. -inline void clear_instance(PyObject *self) { - auto *instance = reinterpret_cast(self); - - // Deallocate any values/holders, if present: - for (auto &v_h : values_and_holders(instance)) { - if (v_h) { - - // We have to deregister before we call dealloc because, for virtual MI types, we still - // need to be able to get the parent pointers. - if (v_h.instance_registered() - && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) { - pybind11_fail( - "pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); - } - - if (instance->owned || v_h.holder_constructed()) { - v_h.type->dealloc(v_h); - } - } else if (v_h.holder_constructed()) { - v_h.type->dealloc(v_h); // Disowned instance. - } - } - // Deallocate the value/holder layout internals: - instance->deallocate_layout(); - - if (instance->weakrefs) { - PyObject_ClearWeakRefs(self); - } - - PyObject **dict_ptr = _PyObject_GetDictPtr(self); - if (dict_ptr) { - Py_CLEAR(*dict_ptr); - } - - if (instance->has_patients) { - clear_patients(self); - } -} +void clear_instance(PyObject *self); /// Instance destructor function for all pybind11 types. It calls `type_info.dealloc` /// to destroy the C++ object itself, while the rest is Python bookkeeping. -extern "C" inline void pybind11_object_dealloc(PyObject *self) { - auto *type = Py_TYPE(self); - - // If this is a GC tracked object, untrack it first - // Note that the track call is implicitly done by the - // default tp_alloc, which we never override. - if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) != 0) { - PyObject_GC_UnTrack(self); - } - -#if PY_VERSION_HEX >= 0x030D0000 - // PyObject_ClearManagedDict() is available from Python 3.13+. It must be - // called before tp_free() because on Python 3.14+ tp_free no longer - // implicitly clears the managed dict, which would abandon the refcounts of - // objects stored in __dict__ of py::dynamic_attr() types, causing permanent - // memory leaks. - if (PyType_HasFeature(type, Py_TPFLAGS_MANAGED_DICT)) { - PyObject_ClearManagedDict(self); - } -#endif - - clear_instance(self); - - type->tp_free(self); - - // This was not needed before Python 3.8 (Python issue 35810) - // https://github.com/pybind/pybind11/issues/1946 - Py_DECREF(type); -} +extern "C" void pybind11_object_dealloc(PyObject *self); PYBIND11_WARNING_PUSH PYBIND11_WARNING_DISABLE_GCC("-Wredundant-decls") @@ -533,316 +143,35 @@ PYBIND11_WARNING_POP /** Create the type which can be used as a common base for all classes. This is needed in order to satisfy Python's requirements for multiple inheritance. Return value: New reference. */ -inline PyObject *make_object_base_type(PyTypeObject *metaclass) { - constexpr auto *name = "pybind11_object"; - auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto *heap_type = reinterpret_cast(metaclass->tp_alloc(metaclass, 0)); - if (!heap_type) { - pybind11_fail("make_object_base_type(): error allocating type!"); - } - - heap_type->ht_name = name_obj.inc_ref().ptr(); -#ifdef PYBIND11_BUILTIN_QUALNAME - heap_type->ht_qualname = name_obj.inc_ref().ptr(); -#endif - - auto *type = &heap_type->ht_type; - type->tp_name = name; - type->tp_base = type_incref(&PyBaseObject_Type); - type->tp_basicsize = static_cast(sizeof(instance)); - type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; - - type->tp_new = pybind11_object_new; - type->tp_init = pybind11_object_init; - type->tp_dealloc = pybind11_object_dealloc; - - /* Support weak references (needed for the keep_alive feature) */ - type->tp_weaklistoffset = offsetof(instance, weakrefs); - - if (PyType_Ready(type) < 0) { - pybind11_fail("PyType_Ready failed in make_object_base_type(): " + error_string()); - } - - setattr(reinterpret_cast(type), "__module__", str(PYBIND11_DUMMY_MODULE_NAME)); - PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); - - assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); - return reinterpret_cast(heap_type); -} +PyObject *make_object_base_type(PyTypeObject *metaclass); /// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`. -extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { -#if PY_VERSION_HEX >= 0x030D0000 - int ret = PyObject_VisitManagedDict(self, visit, arg); - if (ret) { - return ret; - } -#else - PyObject *&dict = *_PyObject_GetDictPtr(self); - Py_VISIT(dict); -#endif - // https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse - Py_VISIT(Py_TYPE(self)); - return 0; -} +extern "C" int pybind11_traverse(PyObject *self, visitproc visit, void *arg); /// dynamic_attr: Allow the GC to clear the dictionary. -extern "C" inline int pybind11_clear(PyObject *self) { -#if PY_VERSION_HEX >= 0x030D0000 - PyObject_ClearManagedDict(self); -#else - PyObject *&dict = *_PyObject_GetDictPtr(self); - Py_CLEAR(dict); -#endif - return 0; -} +extern "C" int pybind11_clear(PyObject *self); /// Give instances of this type a `__dict__` and opt into garbage collection. -inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { - auto *type = &heap_type->ht_type; - type->tp_flags |= Py_TPFLAGS_HAVE_GC; -#ifdef PYBIND11_BACKWARD_COMPATIBILITY_TP_DICTOFFSET - type->tp_dictoffset = type->tp_basicsize; // place dict at the end - type->tp_basicsize += (ssize_t) sizeof(PyObject *); // and allocate enough space for it -#else - type->tp_flags |= Py_TPFLAGS_MANAGED_DICT; -#endif - type->tp_traverse = pybind11_traverse; - type->tp_clear = pybind11_clear; - - static PyGetSetDef getset[] - = {{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict, nullptr, nullptr}, - {nullptr, nullptr, nullptr, nullptr, nullptr}}; - type->tp_getset = getset; -} +void enable_dynamic_attributes(PyHeapTypeObject *heap_type); /// buffer_protocol: Fill in the view as specified by flags. -extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) { - // Look for a `get_buffer` implementation in this type's info or any bases (following MRO). - type_info *tinfo = nullptr; - for (auto type : reinterpret_borrow(Py_TYPE(obj)->tp_mro)) { - tinfo = get_type_info((PyTypeObject *) type.ptr()); - if (tinfo && tinfo->get_buffer) { - break; - } - } - if (view == nullptr || !tinfo || !tinfo->get_buffer) { - if (view) { - view->obj = nullptr; - } - set_error(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); - return -1; - } - std::memset(view, 0, sizeof(Py_buffer)); - std::unique_ptr info = nullptr; - try { - info.reset(tinfo->get_buffer(obj, tinfo->get_buffer_data)); - } catch (...) { - try_translate_exceptions(); - raise_from(PyExc_BufferError, "Error getting buffer"); - return -1; - } - if (info == nullptr) { - pybind11_fail("FATAL UNEXPECTED SITUATION: tinfo->get_buffer() returned nullptr."); - } - - if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) { - // view->obj = nullptr; // Was just memset to 0, so not necessary - set_error(PyExc_BufferError, "Writable buffer requested for readonly storage"); - return -1; - } - - // Fill in all the information, and then downgrade as requested by the caller, or raise an - // error if that's not possible. - view->itemsize = info->itemsize; - view->len = view->itemsize; - for (auto s : info->shape) { - view->len *= s; - } - view->ndim = static_cast(info->ndim); - view->shape = info->shape.data(); - view->strides = info->strides.data(); - view->readonly = static_cast(info->readonly); - if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) { - view->format = const_cast(info->format.c_str()); - } - - // Note, all contiguity flags imply PyBUF_STRIDES and lower. - if ((flags & PyBUF_C_CONTIGUOUS) == PyBUF_C_CONTIGUOUS) { - if (PyBuffer_IsContiguous(view, 'C') == 0) { - std::memset(view, 0, sizeof(Py_buffer)); - set_error(PyExc_BufferError, - "C-contiguous buffer requested for discontiguous storage"); - return -1; - } - } else if ((flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS) { - if (PyBuffer_IsContiguous(view, 'F') == 0) { - std::memset(view, 0, sizeof(Py_buffer)); - set_error(PyExc_BufferError, - "Fortran-contiguous buffer requested for discontiguous storage"); - return -1; - } - } else if ((flags & PyBUF_ANY_CONTIGUOUS) == PyBUF_ANY_CONTIGUOUS) { - if (PyBuffer_IsContiguous(view, 'A') == 0) { - std::memset(view, 0, sizeof(Py_buffer)); - set_error(PyExc_BufferError, "Contiguous buffer requested for discontiguous storage"); - return -1; - } - - } else if ((flags & PyBUF_STRIDES) != PyBUF_STRIDES) { - // If no strides are requested, the buffer must be C-contiguous. - // https://docs.python.org/3/c-api/buffer.html#contiguity-requests - if (PyBuffer_IsContiguous(view, 'C') == 0) { - std::memset(view, 0, sizeof(Py_buffer)); - set_error(PyExc_BufferError, - "C-contiguous buffer requested for discontiguous storage"); - return -1; - } - - view->strides = nullptr; - - // Since this is a contiguous buffer, it can also pretend to be 1D. - if ((flags & PyBUF_ND) != PyBUF_ND) { - view->shape = nullptr; - view->ndim = 0; - } - } - - // Set these after all checks so they don't leak out into the caller, and can be automatically - // cleaned up on error. - view->buf = info->ptr; - view->internal = info.release(); - view->obj = obj; - Py_INCREF(view->obj); - return 0; -} +extern "C" int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags); /// buffer_protocol: Release the resources of the buffer. -extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) { - delete (buffer_info *) view->internal; -} +extern "C" void pybind11_releasebuffer(PyObject *, Py_buffer *view); /// Give this type a buffer interface. -inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { - heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; - - heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; - heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; -} +void enable_buffer_protocol(PyHeapTypeObject *heap_type); /** Create a brand new Python type according to the `type_record` specification. Return value: New reference. */ -inline PyObject *make_new_python_type(const type_record &rec) { - auto name = reinterpret_steal(PYBIND11_FROM_STRING(rec.name)); +PyObject *make_new_python_type(const type_record &rec); - auto qualname = name; - if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) { - qualname = reinterpret_steal( - PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); - } - - object module_ = get_module_name_if_available(rec.scope); - const auto *full_name = c_str( -#if !defined(PYPY_VERSION) - module_ ? str(module_).cast() + "." + rec.name : -#endif - rec.name); - - char *tp_doc = nullptr; - if (rec.doc && options::show_user_defined_docstrings()) { - /* Allocate memory for docstring (Python will free this later on) */ - size_t size = std::strlen(rec.doc) + 1; -#if PY_VERSION_HEX >= 0x030D0000 - tp_doc = static_cast(PyMem_MALLOC(size)); -#else - tp_doc = (char *) PyObject_MALLOC(size); -#endif - std::memcpy((void *) tp_doc, rec.doc, size); - } - - auto &internals = get_internals(); - auto bases = tuple(rec.bases); - auto *base = (bases.empty()) ? internals.instance_base : bases[0].ptr(); - - /* Danger zone: from now (and until PyType_Ready), make sure to - issue no Python C API calls which could potentially invoke the - garbage collector (the GC will call type_traverse(), which will in - turn find the newly constructed type in an invalid state) */ - auto *metaclass = rec.metaclass.ptr() ? reinterpret_cast(rec.metaclass.ptr()) - : internals.default_metaclass; - - auto *heap_type = reinterpret_cast(metaclass->tp_alloc(metaclass, 0)); - if (!heap_type) { - pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); - } - - heap_type->ht_name = name.release().ptr(); -#ifdef PYBIND11_BUILTIN_QUALNAME - heap_type->ht_qualname = qualname.inc_ref().ptr(); -#endif - - auto *type = &heap_type->ht_type; - type->tp_name = full_name; - type->tp_doc = tp_doc; - type->tp_base = type_incref(reinterpret_cast(base)); - type->tp_basicsize = static_cast(sizeof(instance)); - if (!bases.empty()) { - type->tp_bases = bases.release().ptr(); - } - - /* Don't inherit base __init__ */ - type->tp_init = pybind11_object_init; - - /* Supported protocols */ - type->tp_as_number = &heap_type->as_number; - type->tp_as_sequence = &heap_type->as_sequence; - type->tp_as_mapping = &heap_type->as_mapping; - type->tp_as_async = &heap_type->as_async; - - /* Flags */ - type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE; - if (!rec.is_final) { - type->tp_flags |= Py_TPFLAGS_BASETYPE; - } - - if (rec.dynamic_attr) { - enable_dynamic_attributes(heap_type); - } - - if (rec.buffer_protocol) { - enable_buffer_protocol(heap_type); - } - - if (rec.custom_type_setup_callback) { - rec.custom_type_setup_callback(heap_type); - } - - if (PyType_Ready(type) < 0) { - pybind11_fail(std::string(rec.name) + ": PyType_Ready failed: " + error_string()); - } - - assert(!rec.dynamic_attr || PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); - - /* Register type with the parent scope */ - if (rec.scope) { - setattr(rec.scope, rec.name, reinterpret_cast(type)); - } else { - Py_INCREF(type); // Keep it alive forever (reference leak) - } - - if (module_) { // Needed by pydoc - setattr(reinterpret_cast(type), "__module__", module_); - } - - PYBIND11_SET_OLDPY_QUALNAME(type, qualname); - - return reinterpret_cast(type); -} +PYBIND11_WARNING_POP PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) + +#ifndef PYBIND11_PRECOMPILED +# include "class-inl.h" // IWYU pragma: export +#endif diff --git a/include/pybind11/detail/cpp_conduit.h b/include/pybind11/detail/cpp_conduit.h index 49c199e14f..c9ce32756c 100644 --- a/include/pybind11/detail/cpp_conduit.h +++ b/include/pybind11/detail/cpp_conduit.h @@ -13,7 +13,7 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) // Forward declaration needed here: Refactoring opportunity. -extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *); +extern "C" PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *); inline bool type_is_managed_by_our_internals(PyTypeObject *type_obj) { #if defined(PYPY_VERSION) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index ebf382cc77..fede8aef37 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -179,9 +179,9 @@ PYBIND11_NAMESPACE_BEGIN(detail) #define PYBIND11_DUMMY_MODULE_NAME "pybind11_builtins" // Forward declarations -inline PyTypeObject *make_static_property_type(); -inline PyTypeObject *make_default_metaclass(); -inline PyObject *make_object_base_type(PyTypeObject *metaclass); +PyTypeObject *make_static_property_type(); +PyTypeObject *make_default_metaclass(); +PyObject *make_object_base_type(PyTypeObject *metaclass); inline void translate_exception(std::exception_ptr p); inline PyThreadState *get_thread_state_unchecked() { diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index 161b9884fa..7fd9c449eb 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -626,13 +626,13 @@ struct cast_sources { // Forward declarations void keep_alive_impl(handle nurse, handle patient); -inline PyObject *make_new_instance(PyTypeObject *type); +PyObject *make_new_instance(PyTypeObject *type); PYBIND11_WARNING_PUSH PYBIND11_WARNING_DISABLE_GCC("-Wredundant-decls") // PYBIND11:REMINDER: Needs refactoring of existing pybind11 code. -inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo); +bool deregister_instance(instance *self, void *valptr, const type_info *tinfo); PYBIND11_WARNING_POP diff --git a/include/pybind11/trampoline_self_life_support.h b/include/pybind11/trampoline_self_life_support.h index cbfec7f974..be08d9491d 100644 --- a/include/pybind11/trampoline_self_life_support.h +++ b/include/pybind11/trampoline_self_life_support.h @@ -12,7 +12,7 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) // PYBIND11:REMINDER: Needs refactoring of existing pybind11 code. -inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo); +bool deregister_instance(instance *self, void *valptr, const type_info *tinfo); PYBIND11_NAMESPACE_END(detail) // The original core idea for this struct goes back to PyCLIF: diff --git a/src/class.cpp b/src/class.cpp new file mode 100644 index 0000000000..5418b4e359 --- /dev/null +++ b/src/class.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 +#include diff --git a/src/pybind11_combined.cpp b/src/pybind11_combined.cpp index 751b0b1dbe..2bfe127509 100644 --- a/src/pybind11_combined.cpp +++ b/src/pybind11_combined.cpp @@ -11,6 +11,7 @@ # error "pybind11 library sources must be compiled with PYBIND11_PRECOMPILED defined." #endif +#include #include #include #include diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index b02892a738..840df80ac2 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -82,6 +82,7 @@ detail_headers = { "include/pybind11/detail/argument_vector.h", + "include/pybind11/detail/class-inl.h", "include/pybind11/detail/class.h", "include/pybind11/detail/common.h", "include/pybind11/detail/cpp_conduit.h", @@ -129,6 +130,7 @@ } sdist_src_files = { + "src/class.cpp", "src/internals.cpp", "src/pybind11_combined.cpp", "src/pytypes.cpp",