From b8cb5ca7bd8719e9502ed83f283d7f4639410aa7 Mon Sep 17 00:00:00 2001 From: Dean Moldovan Date: Fri, 14 Oct 2016 18:01:17 +0200 Subject: [PATCH] Fix dynamic attribute inheritance in C++ `PyType_Ready` would usually perform the inheritance for us, but it can't adjust `tp_basicsize` appropriately. --- include/pybind11/attr.h | 3 +++ tests/test_methods_and_attributes.cpp | 5 +++++ tests/test_methods_and_attributes.py | 17 +++++++++-------- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index 15bb2e4a1..1ea925c18 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -179,6 +179,9 @@ struct type_record { bases.append((PyObject *) base_info->type); + if (base_info->type->tp_dictoffset != 0) + dynamic_attr = true; + if (caster) base_info->implicit_casts.push_back(std::make_pair(type, caster)); } diff --git a/tests/test_methods_and_attributes.cpp b/tests/test_methods_and_attributes.cpp index 4948dc09a..c78e64b56 100644 --- a/tests/test_methods_and_attributes.cpp +++ b/tests/test_methods_and_attributes.cpp @@ -59,6 +59,8 @@ public: ~DynamicClass() { print_destroyed(this); } }; +class CppDerivedDynamicClass : public DynamicClass { }; + test_initializer methods_and_attributes([](py::module &m) { py::class_(m, "ExampleMandA") .def(py::init<>()) @@ -90,4 +92,7 @@ test_initializer methods_and_attributes([](py::module &m) { py::class_(m, "DynamicClass", py::dynamic_attr()) .def(py::init()); + + py::class_(m, "CppDerivedDynamicClass") + .def(py::init()); }); diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index 04f2d12a6..1c7b3daa0 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -48,7 +48,7 @@ def test_methods_and_attributes(): def test_dynamic_attributes(): - from pybind11_tests import DynamicClass + from pybind11_tests import DynamicClass, CppDerivedDynamicClass instance = DynamicClass() assert not hasattr(instance, "foo") @@ -76,16 +76,17 @@ def test_dynamic_attributes(): assert cstats.alive() == 0 # Derived classes should work as well - class Derived(DynamicClass): + class PythonDerivedDynamicClass(DynamicClass): pass - derived = Derived() - derived.foobar = 100 - assert derived.foobar == 100 + for cls in CppDerivedDynamicClass, PythonDerivedDynamicClass: + derived = cls() + derived.foobar = 100 + assert derived.foobar == 100 - assert cstats.alive() == 1 - del derived - assert cstats.alive() == 0 + assert cstats.alive() == 1 + del derived + assert cstats.alive() == 0 def test_cyclic_gc():