Skip to content

Commit 7405976

Browse files
Fixes issue #3801 (#3807)
* Fixes issue * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix lint error * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix flake8 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix clang tidy * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix again * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix test * Add comments * Try fix Valgrind * Resolve comments * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 9d4b4df commit 7405976

File tree

3 files changed

+127
-88
lines changed

3 files changed

+127
-88
lines changed

Diff for: include/pybind11/detail/smart_holder_type_casters.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,11 @@ class modified_type_caster_generic_load_impl {
292292
loaded_v_h = value_and_holder();
293293
return true;
294294
}
295-
if (convert && cpptype && try_as_void_ptr_capsule(src)) {
296-
return true;
295+
if (convert && cpptype) {
296+
const auto &bases = all_type_info(srctype);
297+
if (bases.empty() && try_as_void_ptr_capsule(src)) {
298+
return true;
299+
}
297300
}
298301
return false;
299302
}

Diff for: tests/test_class_sh_void_ptr_capsule.cpp

+56-79
Original file line numberDiff line numberDiff line change
@@ -7,122 +7,99 @@
77
namespace pybind11_tests {
88
namespace class_sh_void_ptr_capsule {
99

10-
// Conveniently, the helper serves to keep track of `capsule_generated`.
11-
struct HelperBase {
12-
HelperBase() = default;
13-
HelperBase(const HelperBase &) = delete;
14-
virtual ~HelperBase() = default;
15-
16-
bool capsule_generated = false;
17-
virtual int get() const { return 100; }
18-
};
10+
struct Valid {};
1911

20-
struct Valid : public HelperBase {
21-
int get() const override { return 101; }
12+
struct NoConversion {};
2213

23-
PyObject *as_pybind11_tests_class_sh_void_ptr_capsule_Valid() {
24-
void *vptr = dynamic_cast<void *>(this);
25-
capsule_generated = true;
26-
// We assume vptr out lives the capsule, so we use nullptr for the
27-
// destructor.
28-
return PyCapsule_New(vptr, "::pybind11_tests::class_sh_void_ptr_capsule::Valid", nullptr);
29-
}
30-
};
14+
struct NoCapsuleReturned {};
3115

32-
struct NoConversion : public HelperBase {
33-
int get() const override { return 102; }
34-
};
16+
struct AsAnotherObject {};
3517

36-
struct NoCapsuleReturned : public HelperBase {
37-
int get() const override { return 103; }
18+
// Create a void pointer capsule for test. The encapsulated object does not
19+
// matter for this test case.
20+
PyObject *create_test_void_ptr_capsule() {
21+
static int just_to_have_something_to_point_to = 0;
22+
return PyCapsule_New(static_cast<void *>(&just_to_have_something_to_point_to), "int", nullptr);
23+
}
3824

39-
PyObject *as_pybind11_tests_class_sh_void_ptr_capsule_NoCapsuleReturned() {
40-
capsule_generated = true;
41-
Py_XINCREF(Py_None);
42-
return Py_None;
43-
}
44-
};
25+
int get_from_valid_capsule(const Valid *) { return 1; }
4526

46-
struct AsAnotherObject : public HelperBase {
47-
int get() const override { return 104; }
27+
int get_from_shared_ptr_valid_capsule(const std::shared_ptr<Valid> &) { return 2; }
4828

49-
PyObject *as_pybind11_tests_class_sh_void_ptr_capsule_Valid() {
50-
void *vptr = dynamic_cast<void *>(this);
51-
capsule_generated = true;
52-
// We assume vptr out lives the capsule, so we use nullptr for the
53-
// destructor.
54-
return PyCapsule_New(vptr, "::pybind11_tests::class_sh_void_ptr_capsule::Valid", nullptr);
55-
}
56-
};
29+
int get_from_unique_ptr_valid_capsule(std::unique_ptr<Valid>) { return 3; }
30+
31+
int get_from_no_conversion_capsule(const NoConversion *) { return 4; }
32+
33+
int get_from_no_capsule_returned(const NoCapsuleReturned *) { return 5; }
5734

5835
// https://github.com/pybind/pybind11/issues/3788
5936
struct TypeWithGetattr {
6037
TypeWithGetattr() = default;
6138
int get_42() const { return 42; }
6239
};
6340

64-
int get_from_valid_capsule(const Valid *c) { return c->get(); }
65-
66-
int get_from_shared_ptr_valid_capsule(const std::shared_ptr<Valid> &c) { return c->get(); }
41+
// https://github.com/pybind/pybind11/issues/3804
42+
struct Base1 {
43+
int a1{};
44+
};
45+
struct Base2 {
46+
int a2{};
47+
};
6748

68-
int get_from_unique_ptr_valid_capsule(std::unique_ptr<Valid> c) { return c->get(); }
49+
struct Base12 : Base1, Base2 {
50+
int foo() const { return 0; }
51+
};
6952

70-
int get_from_no_conversion_capsule(const NoConversion *c) { return c->get(); }
53+
struct Derived1 : Base12 {
54+
int bar() const { return 1; }
55+
};
7156

72-
int get_from_no_capsule_returned(const NoCapsuleReturned *c) { return c->get(); }
57+
struct Derived2 : Base12 {
58+
int bar() const { return 2; }
59+
};
7360

7461
} // namespace class_sh_void_ptr_capsule
7562
} // namespace pybind11_tests
7663

77-
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_void_ptr_capsule::HelperBase)
7864
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_void_ptr_capsule::Valid)
79-
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_void_ptr_capsule::NoConversion)
80-
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_void_ptr_capsule::NoCapsuleReturned)
81-
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_void_ptr_capsule::AsAnotherObject)
8265
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_void_ptr_capsule::TypeWithGetattr)
66+
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_void_ptr_capsule::Base1)
67+
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_void_ptr_capsule::Base2)
68+
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_void_ptr_capsule::Base12)
69+
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_void_ptr_capsule::Derived1)
70+
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_void_ptr_capsule::Derived2)
8371

8472
TEST_SUBMODULE(class_sh_void_ptr_capsule, m) {
8573
using namespace pybind11_tests::class_sh_void_ptr_capsule;
8674

87-
py::classh<HelperBase>(m, "HelperBase")
88-
.def(py::init<>())
89-
.def("get", &HelperBase::get)
90-
.def_readonly("capsule_generated", &HelperBase::capsule_generated);
91-
92-
py::classh<Valid, HelperBase>(m, "Valid")
93-
.def(py::init<>())
94-
.def("as_pybind11_tests_class_sh_void_ptr_capsule_Valid", [](Valid &self) {
95-
PyObject *capsule = self.as_pybind11_tests_class_sh_void_ptr_capsule_Valid();
96-
return pybind11::reinterpret_steal<py::capsule>(capsule);
97-
});
98-
99-
py::classh<NoConversion, HelperBase>(m, "NoConversion").def(py::init<>());
100-
101-
py::classh<NoCapsuleReturned, HelperBase>(m, "NoCapsuleReturned")
102-
.def(py::init<>())
103-
.def("as_pybind11_tests_class_sh_void_ptr_capsule_NoCapsuleReturned",
104-
[](NoCapsuleReturned &self) {
105-
PyObject *capsule
106-
= self.as_pybind11_tests_class_sh_void_ptr_capsule_NoCapsuleReturned();
107-
return pybind11::reinterpret_steal<py::capsule>(capsule);
108-
});
109-
110-
py::classh<AsAnotherObject, HelperBase>(m, "AsAnotherObject")
111-
.def(py::init<>())
112-
.def("as_pybind11_tests_class_sh_void_ptr_capsule_Valid", [](AsAnotherObject &self) {
113-
PyObject *capsule = self.as_pybind11_tests_class_sh_void_ptr_capsule_Valid();
114-
return pybind11::reinterpret_steal<py::capsule>(capsule);
115-
});
75+
py::classh<Valid>(m, "Valid");
11676

11777
m.def("get_from_valid_capsule", &get_from_valid_capsule);
11878
m.def("get_from_shared_ptr_valid_capsule", &get_from_shared_ptr_valid_capsule);
11979
m.def("get_from_unique_ptr_valid_capsule", &get_from_unique_ptr_valid_capsule);
12080
m.def("get_from_no_conversion_capsule", &get_from_no_conversion_capsule);
12181
m.def("get_from_no_capsule_returned", &get_from_no_capsule_returned);
82+
m.def("create_test_void_ptr_capsule", []() {
83+
PyObject *capsule = create_test_void_ptr_capsule();
84+
return pybind11::reinterpret_steal<py::capsule>(capsule);
85+
});
12286

12387
py::classh<TypeWithGetattr>(m, "TypeWithGetattr")
12488
.def(py::init<>())
12589
.def("get_42", &TypeWithGetattr::get_42)
12690
.def("__getattr__",
12791
[](TypeWithGetattr &, const std::string &key) { return "GetAttr: " + key; });
92+
93+
py::classh<Base1>(m, "Base1");
94+
py::classh<Base2>(m, "Base2");
95+
96+
py::classh<Base12, Base1, Base2>(m, "Base12")
97+
.def(py::init<>())
98+
.def("foo", &Base12::foo)
99+
.def("__getattr__",
100+
[](Base12 &, const std::string &key) { return "Base GetAttr: " + key; });
101+
102+
py::classh<Derived1, Base12>(m, "Derived1").def(py::init<>()).def("bar", &Derived1::bar);
103+
104+
py::classh<Derived2, Base12>(m, "Derived2").def(py::init<>()).def("bar", &Derived2::bar);
128105
}

Diff for: tests/test_class_sh_void_ptr_capsule.py

+66-7
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,73 @@
33
from pybind11_tests import class_sh_void_ptr_capsule as m
44

55

6+
class Valid:
7+
def __init__(self):
8+
self.capsule_generated = False
9+
10+
def as_pybind11_tests_class_sh_void_ptr_capsule_Valid(self): # noqa: N802
11+
self.capsule_generated = True
12+
return m.create_test_void_ptr_capsule()
13+
14+
15+
class NoConversion:
16+
def __init__(self):
17+
self.capsule_generated = False
18+
19+
20+
class NoCapsuleReturned:
21+
def __init__(self):
22+
self.capsule_generated = False
23+
24+
def as_pybind11_tests_class_sh_void_ptr_capsule_NoCapsuleReturned( # noqa: N802
25+
self,
26+
):
27+
pass
28+
29+
30+
class AsAnotherObject:
31+
def __init__(self):
32+
self.capsule_generated = False
33+
34+
def as_pybind11_tests_class_sh_void_ptr_capsule_Valid(self): # noqa: N802
35+
self.capsule_generated = True
36+
return m.create_test_void_ptr_capsule()
37+
38+
639
@pytest.mark.parametrize(
740
"ctor, caller, expected, capsule_generated",
841
[
9-
(m.Valid, m.get_from_valid_capsule, 101, False),
10-
(m.NoConversion, m.get_from_no_conversion_capsule, 102, False),
11-
(m.NoCapsuleReturned, m.get_from_no_capsule_returned, 103, False),
12-
(m.AsAnotherObject, m.get_from_valid_capsule, 104, True),
42+
(Valid, m.get_from_valid_capsule, 1, True),
43+
(AsAnotherObject, m.get_from_valid_capsule, 1, True),
1344
],
1445
)
15-
def test_as_void_ptr_capsule(ctor, caller, expected, capsule_generated):
46+
def test_valid_as_void_ptr_capsule_function(ctor, caller, expected, capsule_generated):
1647
obj = ctor()
1748
assert caller(obj) == expected
1849
assert obj.capsule_generated == capsule_generated
1950

2051

52+
@pytest.mark.parametrize(
53+
"ctor, caller, expected, capsule_generated",
54+
[
55+
(NoConversion, m.get_from_no_conversion_capsule, 2, False),
56+
(NoCapsuleReturned, m.get_from_no_capsule_returned, 3, False),
57+
],
58+
)
59+
def test_invalid_as_void_ptr_capsule_function(
60+
ctor, caller, expected, capsule_generated
61+
):
62+
obj = ctor()
63+
with pytest.raises(TypeError):
64+
caller(obj)
65+
assert obj.capsule_generated == capsule_generated
66+
67+
2168
@pytest.mark.parametrize(
2269
"ctor, caller, pointer_type, capsule_generated",
2370
[
24-
(m.AsAnotherObject, m.get_from_shared_ptr_valid_capsule, "shared_ptr", True),
25-
(m.AsAnotherObject, m.get_from_unique_ptr_valid_capsule, "unique_ptr", True),
71+
(AsAnotherObject, m.get_from_shared_ptr_valid_capsule, "shared_ptr", True),
72+
(AsAnotherObject, m.get_from_unique_ptr_valid_capsule, "unique_ptr", True),
2673
],
2774
)
2875
def test_as_void_ptr_capsule_unsupported(ctor, caller, pointer_type, capsule_generated):
@@ -37,3 +84,15 @@ def test_type_with_getattr():
3784
obj = m.TypeWithGetattr()
3885
assert obj.get_42() == 42
3986
assert obj.something == "GetAttr: something"
87+
88+
89+
def test_multiple_inheritance_getattr():
90+
d1 = m.Derived1()
91+
assert d1.foo() == 0
92+
assert d1.bar() == 1
93+
assert d1.prop1 == "Base GetAttr: prop1"
94+
95+
d2 = m.Derived2()
96+
assert d2.foo() == 0
97+
assert d2.bar() == 2
98+
assert d2.prop2 == "Base GetAttr: prop2"

0 commit comments

Comments
 (0)