Add is_redundant_value_and_holder() and use to avoid forcing __init__ overrides when they are not needed.

This commit is contained in:
Ralf W. Grosse-Kunstleve 2023-07-28 15:20:03 -07:00
parent 7c7d78d87f
commit d7088364f9
3 changed files with 26 additions and 14 deletions

View File

@ -180,6 +180,17 @@ extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name
return PyType_Type.tp_getattro(obj, name);
}
// Band-aid workaround to fix a subtle but serious bug in a minimalistic fashion. See PR #4762.
inline bool is_redundant_value_and_holder(const std::vector<type_info *> &bases,
std::size_t ix_base) {
for (std::size_t i = 0; i < ix_base; i++) {
if (PyType_IsSubtype(bases[i]->type, bases[ix_base]->type) != 0) {
return true;
}
}
return false;
}
/// metaclass `__call__` function that is used to create all pybind11 objects.
extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, PyObject *kwargs) {
@ -189,18 +200,20 @@ extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, P
return nullptr;
}
// This must be a pybind11 instance
auto *instance = reinterpret_cast<detail::instance *>(self);
// Ensure that the base __init__ function(s) were called
for (const auto &vh : values_and_holders(instance)) {
if (!vh.holder_constructed()) {
const auto &bases = all_type_info((PyTypeObject *) type);
values_and_holders vhs(reinterpret_cast<detail::instance *>(self));
assert(bases.size() == vhs.size());
std::size_t ix_base = 0;
for (const auto &vh : vhs) {
if (!vh.holder_constructed() && !is_redundant_value_and_holder(bases, ix_base)) {
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;
}
ix_base++;
}
return self;

View File

@ -102,6 +102,7 @@ public:
inline std::pair<decltype(internals::registered_types_py)::iterator, bool>
all_type_info_get_cache(PyTypeObject *type);
// Band-aid workaround to fix a subtle but serious bug in a minimalistic fashion. See PR #4762.
inline void all_type_info_add_base_most_derived_first(std::vector<type_info *> &bases,
type_info *addl_base) {
for (auto it = bases.begin(); it != bases.end(); it++) {

View File

@ -8,10 +8,8 @@ class PC(m.CppBase):
pass
class PPCCInit(PC, m.CppDrvd):
def __init__(self, value):
PC.__init__(self, value)
m.CppDrvd.__init__(self, value + 1)
class PPCC(PC, m.CppDrvd):
pass
def test_PC():
@ -21,14 +19,14 @@ def test_PC():
assert d.get_base_value() == 13
def test_PPCCInit():
d = PPCCInit(11)
assert d.get_drvd_value() == 36
def test_PPCC():
d = PPCC(11)
assert d.get_drvd_value() == 33
d.reset_drvd_value(55)
assert d.get_drvd_value() == 55
assert d.get_base_value() == 12
assert d.get_base_value_from_drvd() == 12
assert d.get_base_value() == 11
assert d.get_base_value_from_drvd() == 11
d.reset_base_value(20)
assert d.get_base_value() == 20
assert d.get_base_value_from_drvd() == 20