Commit e0e2ea33 by Dean Moldovan

Fix overriding static properties in derived classes

Fixes #775.

Assignments of the form `Type.static_prop = value` should be translated to
`Type.static_prop.__set__(value)` except when `isinstance(value, static_prop)`.
parent db200955
...@@ -124,8 +124,15 @@ extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyOb ...@@ -124,8 +124,15 @@ extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyOb
// descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`).
PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); 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
const auto static_prop = (PyObject *) get_internals().static_property_type;
const auto call_descr_set = descr && PyObject_IsInstance(descr, static_prop)
&& !PyObject_IsInstance(value, static_prop);
if (call_descr_set) {
// Call `static_property.__set__()` instead of replacing the `static_property`. // Call `static_property.__set__()` instead of replacing the `static_property`.
if (descr && PyObject_IsInstance(descr, (PyObject *) get_internals().static_property_type)) {
#if !defined(PYPY_VERSION) #if !defined(PYPY_VERSION)
return Py_TYPE(descr)->tp_descr_set(descr, obj, value); return Py_TYPE(descr)->tp_descr_set(descr, obj, value);
#else #else
...@@ -137,6 +144,7 @@ extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyOb ...@@ -137,6 +144,7 @@ extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyOb
} }
#endif #endif
} else { } else {
// Replace existing attribute.
return PyType_Type.tp_setattro(obj, name, value); return PyType_Type.tp_setattro(obj, name, value);
} }
} }
......
...@@ -75,6 +75,13 @@ struct TestProperties { ...@@ -75,6 +75,13 @@ struct TestProperties {
int TestProperties::static_value = 1; int TestProperties::static_value = 1;
struct TestPropertiesOverride : TestProperties {
int value = 99;
static int static_value;
};
int TestPropertiesOverride::static_value = 99;
struct SimpleValue { int value = 1; }; struct SimpleValue { int value = 1; };
struct TestPropRVP { struct TestPropRVP {
...@@ -219,6 +226,11 @@ test_initializer methods_and_attributes([](py::module &m) { ...@@ -219,6 +226,11 @@ test_initializer methods_and_attributes([](py::module &m) {
[](py::object cls) { return cls; }, [](py::object cls) { return cls; },
[](py::object cls, py::function f) { f(cls); }); [](py::object cls, py::function f) { f(cls); });
py::class_<TestPropertiesOverride, TestProperties>(m, "TestPropertiesOverride")
.def(py::init<>())
.def_readonly("def_readonly", &TestPropertiesOverride::value)
.def_readonly_static("def_readonly_static", &TestPropertiesOverride::static_value);
py::class_<SimpleValue>(m, "SimpleValue") py::class_<SimpleValue>(m, "SimpleValue")
.def_readwrite("value", &SimpleValue::value); .def_readwrite("value", &SimpleValue::value);
......
...@@ -110,6 +110,12 @@ def test_static_properties(): ...@@ -110,6 +110,12 @@ def test_static_properties():
assert Type.def_readwrite_static == 2 assert Type.def_readwrite_static == 2
assert instance.def_readwrite_static == 2 assert instance.def_readwrite_static == 2
# It should be possible to override properties in derived classes
from pybind11_tests import TestPropertiesOverride as TypeOverride
assert TypeOverride().def_readonly == 99
assert TypeOverride.def_readonly_static == 99
def test_static_cls(): def test_static_cls():
"""Static property getter and setters expect the type object as the their only argument""" """Static property getter and setters expect the type object as the their only argument"""
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment