mirror of
https://github.com/pybind/pybind11.git
synced 2025-02-16 21:57:55 +00:00
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)`.
This commit is contained in:
parent
db200955b9
commit
e0e2ea3378
@ -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
|
|||||||
}
|
}
|
||||||
#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 {
|
|||||||
|
|
||||||
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) {
|
|||||||
[](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():
|
|||||||
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"""
|
||||||
|
Loading…
Reference in New Issue
Block a user