forked from pybind/pybind11
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix a long-standing bug in the handling of Python multiple inheritanc…
…e (#30056) * Snapshot of pybind#4762 applied to pywrapcc * Universal `bases.size() != vhs.size()` (not as `assert()`) * Revert "Universal `bases.size() != vhs.size()` (not as `assert()`)" This reverts commit 4c2407d17e982e1512f43ad89bf8752c0d2c7fe0. * Streamline implementation and avoid unsafe `reinterpret_cast<instance *>()` introduced with PR pybind#2152 The `reinterpret_cast<instance *>(self)` is unsafe if `__new__` is mocked, which was actually found in the wild: the mock returned `None` for `self`. This was inconsequential because `inst` is currently cast straight back to `PyObject *` to compute `all_type_info()`, which is empty if `self` is not a pybind11 `instance`, and then `inst` is never dereferenced. However, the unsafe detour through `instance *` is easily avoided and the updated implementation is less prone to accidents while debugging or refactoring. * Fix actual undefined behavior exposed by previous changes. It turns out the previous commit message is incorrect, the `inst` pointer is actually dereferenced, in the `value_and_holder` ctor here: https://github.com/pybind/pybind11/blob/f3e0602802c7840992c97f4960515777cad6a5c7/include/pybind11/detail/type_caster_base.h#L262-L263 ``` 259 // Main constructor for a found value/holder: 260 value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) 261 : inst{i}, index{index}, type{type}, 262 vh{inst->simple_layout ? inst->simple_value_holder 263 : &inst->nonsimple.values_and_holders[vpos]} {} ``` * Add test_mock_new()
- Loading branch information
Ralf W. Grosse-Kunstleve
authored
Sep 12, 2023
1 parent
b1538fc
commit dc89e50
Showing
6 changed files
with
140 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
#include "pybind11_tests.h" | ||
|
||
namespace test_python_multiple_inheritance { | ||
|
||
// Copied from: | ||
// https://github.com/google/clif/blob/5718e4d0807fd3b6a8187dde140069120b81ecef/clif/testing/python_multiple_inheritance.h | ||
|
||
struct CppBase { | ||
explicit CppBase(int value) : base_value(value) {} | ||
int get_base_value() const { return base_value; } | ||
void reset_base_value(int new_value) { base_value = new_value; } | ||
|
||
private: | ||
int base_value; | ||
}; | ||
|
||
struct CppDrvd : CppBase { | ||
explicit CppDrvd(int value) : CppBase(value), drvd_value(value * 3) {} | ||
int get_drvd_value() const { return drvd_value; } | ||
void reset_drvd_value(int new_value) { drvd_value = new_value; } | ||
|
||
int get_base_value_from_drvd() const { return get_base_value(); } | ||
void reset_base_value_from_drvd(int new_value) { reset_base_value(new_value); } | ||
|
||
private: | ||
int drvd_value; | ||
}; | ||
|
||
} // namespace test_python_multiple_inheritance | ||
|
||
TEST_SUBMODULE(python_multiple_inheritance, m) { | ||
using namespace test_python_multiple_inheritance; | ||
|
||
py::class_<CppBase>(m, "CppBase") | ||
.def(py::init<int>()) | ||
.def("get_base_value", &CppBase::get_base_value) | ||
.def("reset_base_value", &CppBase::reset_base_value); | ||
|
||
py::class_<CppDrvd, CppBase>(m, "CppDrvd") | ||
.def(py::init<int>()) | ||
.def("get_drvd_value", &CppDrvd::get_drvd_value) | ||
.def("reset_drvd_value", &CppDrvd::reset_drvd_value) | ||
.def("get_base_value_from_drvd", &CppDrvd::get_base_value_from_drvd) | ||
.def("reset_base_value_from_drvd", &CppDrvd::reset_base_value_from_drvd); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Adapted from: | ||
# https://github.com/google/clif/blob/5718e4d0807fd3b6a8187dde140069120b81ecef/clif/testing/python/python_multiple_inheritance_test.py | ||
|
||
from pybind11_tests import python_multiple_inheritance as m | ||
|
||
|
||
class PC(m.CppBase): | ||
pass | ||
|
||
|
||
class PPCC(PC, m.CppDrvd): | ||
pass | ||
|
||
|
||
def test_PC(): | ||
d = PC(11) | ||
assert d.get_base_value() == 11 | ||
d.reset_base_value(13) | ||
assert d.get_base_value() == 13 | ||
|
||
|
||
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() == 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 | ||
d.reset_base_value_from_drvd(30) | ||
assert d.get_base_value() == 30 | ||
assert d.get_base_value_from_drvd() == 30 |