mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-14 01:23:53 +00:00
py::class_<T>'s `def_property` and `def_property_static` can now take a `nullptr` as the getter to allow a write-only property to be established (mirroring Python's `property()` built-in when `None` is given for the getter). This also updates properties to use the new nullptr constructor internally.
This commit is contained in:
parent
71178922fd
commit
0a0758ce3a
@ -15,6 +15,9 @@ v2.3.0 (Not yet released)
|
|||||||
for non-MSVC compilers).
|
for non-MSVC compilers).
|
||||||
`#934 <https://github.com/pybind/pybind11/pull/934>`_.
|
`#934 <https://github.com/pybind/pybind11/pull/934>`_.
|
||||||
|
|
||||||
|
* Added support for write only properties.
|
||||||
|
`#1144 <https://github.com/pybind/pybind11/pull/1144>`_.
|
||||||
|
|
||||||
v2.2.1 (September 14, 2017)
|
v2.2.1 (September 14, 2017)
|
||||||
-----------------------------------------------------
|
-----------------------------------------------------
|
||||||
|
|
||||||
|
@ -155,6 +155,9 @@ the setter and getter functions:
|
|||||||
.def_property("name", &Pet::getName, &Pet::setName)
|
.def_property("name", &Pet::getName, &Pet::setName)
|
||||||
// ... remainder ...
|
// ... remainder ...
|
||||||
|
|
||||||
|
Write only properties can be defined by passing ``nullptr`` as the
|
||||||
|
input for the read function.
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
Similar functions :func:`class_::def_readwrite_static`,
|
Similar functions :func:`class_::def_readwrite_static`,
|
||||||
|
@ -51,6 +51,7 @@ NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
|||||||
class cpp_function : public function {
|
class cpp_function : public function {
|
||||||
public:
|
public:
|
||||||
cpp_function() { }
|
cpp_function() { }
|
||||||
|
cpp_function(std::nullptr_t) { }
|
||||||
|
|
||||||
/// Construct a cpp_function from a vanilla function pointer
|
/// Construct a cpp_function from a vanilla function pointer
|
||||||
template <typename Return, typename... Args, typename... Extra>
|
template <typename Return, typename... Args, typename... Extra>
|
||||||
@ -951,18 +952,18 @@ protected:
|
|||||||
tinfo->get_buffer_data = get_buffer_data;
|
tinfo->get_buffer_data = get_buffer_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rec_func must be set for either fget or fset.
|
||||||
void def_property_static_impl(const char *name,
|
void def_property_static_impl(const char *name,
|
||||||
handle fget, handle fset,
|
handle fget, handle fset,
|
||||||
detail::function_record *rec_fget) {
|
detail::function_record *rec_func) {
|
||||||
const auto is_static = !(rec_fget->is_method && rec_fget->scope);
|
const auto is_static = rec_func && !(rec_func->is_method && rec_func->scope);
|
||||||
const auto has_doc = rec_fget->doc && pybind11::options::show_user_defined_docstrings();
|
const auto has_doc = rec_func && rec_func->doc && pybind11::options::show_user_defined_docstrings();
|
||||||
|
|
||||||
auto property = handle((PyObject *) (is_static ? get_internals().static_property_type
|
auto property = handle((PyObject *) (is_static ? get_internals().static_property_type
|
||||||
: &PyProperty_Type));
|
: &PyProperty_Type));
|
||||||
attr(name) = property(fget.ptr() ? fget : none(),
|
attr(name) = property(fget.ptr() ? fget : none(),
|
||||||
fset.ptr() ? fset : none(),
|
fset.ptr() ? fset : none(),
|
||||||
/*deleter*/none(),
|
/*deleter*/none(),
|
||||||
pybind11::str(has_doc ? rec_fget->doc : ""));
|
pybind11::str(has_doc ? rec_func->doc : ""));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1196,7 +1197,7 @@ public:
|
|||||||
/// Uses cpp_function's return_value_policy by default
|
/// Uses cpp_function's return_value_policy by default
|
||||||
template <typename... Extra>
|
template <typename... Extra>
|
||||||
class_ &def_property_readonly(const char *name, const cpp_function &fget, const Extra& ...extra) {
|
class_ &def_property_readonly(const char *name, const cpp_function &fget, const Extra& ...extra) {
|
||||||
return def_property(name, fget, cpp_function(), extra...);
|
return def_property(name, fget, nullptr, extra...);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Uses return_value_policy::reference by default
|
/// Uses return_value_policy::reference by default
|
||||||
@ -1208,7 +1209,7 @@ public:
|
|||||||
/// Uses cpp_function's return_value_policy by default
|
/// Uses cpp_function's return_value_policy by default
|
||||||
template <typename... Extra>
|
template <typename... Extra>
|
||||||
class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const Extra& ...extra) {
|
class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const Extra& ...extra) {
|
||||||
return def_property_static(name, fget, cpp_function(), extra...);
|
return def_property_static(name, fget, nullptr, extra...);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Uses return_value_policy::reference_internal by default
|
/// Uses return_value_policy::reference_internal by default
|
||||||
@ -1238,21 +1239,25 @@ public:
|
|||||||
template <typename... Extra>
|
template <typename... Extra>
|
||||||
class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) {
|
class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) {
|
||||||
auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset);
|
auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset);
|
||||||
|
auto *rec_active = rec_fget;
|
||||||
|
if (rec_fget) {
|
||||||
char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */
|
char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */
|
||||||
detail::process_attributes<Extra...>::init(extra..., rec_fget);
|
detail::process_attributes<Extra...>::init(extra..., rec_fget);
|
||||||
if (rec_fget->doc && rec_fget->doc != doc_prev) {
|
if (rec_fget->doc && rec_fget->doc != doc_prev) {
|
||||||
free(doc_prev);
|
free(doc_prev);
|
||||||
rec_fget->doc = strdup(rec_fget->doc);
|
rec_fget->doc = strdup(rec_fget->doc);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (rec_fset) {
|
if (rec_fset) {
|
||||||
doc_prev = rec_fset->doc;
|
char *doc_prev = rec_fset->doc;
|
||||||
detail::process_attributes<Extra...>::init(extra..., rec_fset);
|
detail::process_attributes<Extra...>::init(extra..., rec_fset);
|
||||||
if (rec_fset->doc && rec_fset->doc != doc_prev) {
|
if (rec_fset->doc && rec_fset->doc != doc_prev) {
|
||||||
free(doc_prev);
|
free(doc_prev);
|
||||||
rec_fset->doc = strdup(rec_fset->doc);
|
rec_fset->doc = strdup(rec_fset->doc);
|
||||||
}
|
}
|
||||||
|
if (! rec_active) rec_active = rec_fset;
|
||||||
}
|
}
|
||||||
def_property_static_impl(name, fget, fset, rec_fget);
|
def_property_static_impl(name, fget, fset, rec_active);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,12 +279,20 @@ TEST_SUBMODULE(methods_and_attributes, m) {
|
|||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def_readonly("def_readonly", &TestProperties::value)
|
.def_readonly("def_readonly", &TestProperties::value)
|
||||||
.def_readwrite("def_readwrite", &TestProperties::value)
|
.def_readwrite("def_readwrite", &TestProperties::value)
|
||||||
|
.def_property("def_writeonly", nullptr,
|
||||||
|
[](TestProperties& s,int v) { s.value = v; } )
|
||||||
|
.def_property("def_property_writeonly", nullptr, &TestProperties::set)
|
||||||
.def_property_readonly("def_property_readonly", &TestProperties::get)
|
.def_property_readonly("def_property_readonly", &TestProperties::get)
|
||||||
.def_property("def_property", &TestProperties::get, &TestProperties::set)
|
.def_property("def_property", &TestProperties::get, &TestProperties::set)
|
||||||
|
.def_property("def_property_impossible", nullptr, nullptr)
|
||||||
.def_readonly_static("def_readonly_static", &TestProperties::static_value)
|
.def_readonly_static("def_readonly_static", &TestProperties::static_value)
|
||||||
.def_readwrite_static("def_readwrite_static", &TestProperties::static_value)
|
.def_readwrite_static("def_readwrite_static", &TestProperties::static_value)
|
||||||
|
.def_property_static("def_writeonly_static", nullptr,
|
||||||
|
[](py::object, int v) { TestProperties::static_value = v; })
|
||||||
.def_property_readonly_static("def_property_readonly_static",
|
.def_property_readonly_static("def_property_readonly_static",
|
||||||
[](py::object) { return TestProperties::static_get(); })
|
[](py::object) { return TestProperties::static_get(); })
|
||||||
|
.def_property_static("def_property_writeonly_static", nullptr,
|
||||||
|
[](py::object, int v) { return TestProperties::static_set(v); })
|
||||||
.def_property_static("def_property_static",
|
.def_property_static("def_property_static",
|
||||||
[](py::object) { return TestProperties::static_get(); },
|
[](py::object) { return TestProperties::static_get(); },
|
||||||
[](py::object, int v) { TestProperties::static_set(v); })
|
[](py::object, int v) { TestProperties::static_set(v); })
|
||||||
|
@ -98,6 +98,21 @@ def test_properties():
|
|||||||
instance.def_property = 3
|
instance.def_property = 3
|
||||||
assert instance.def_property == 3
|
assert instance.def_property == 3
|
||||||
|
|
||||||
|
with pytest.raises(AttributeError) as excinfo:
|
||||||
|
dummy = instance.def_property_writeonly # noqa: F841 unused var
|
||||||
|
assert "unreadable attribute" in str(excinfo)
|
||||||
|
|
||||||
|
instance.def_property_writeonly = 4
|
||||||
|
assert instance.def_property_readonly == 4
|
||||||
|
|
||||||
|
with pytest.raises(AttributeError) as excinfo:
|
||||||
|
dummy = instance.def_property_impossible # noqa: F841 unused var
|
||||||
|
assert "unreadable attribute" in str(excinfo)
|
||||||
|
|
||||||
|
with pytest.raises(AttributeError) as excinfo:
|
||||||
|
instance.def_property_impossible = 5
|
||||||
|
assert "can't set attribute" in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
def test_static_properties():
|
def test_static_properties():
|
||||||
assert m.TestProperties.def_readonly_static == 1
|
assert m.TestProperties.def_readonly_static == 1
|
||||||
@ -108,13 +123,27 @@ def test_static_properties():
|
|||||||
m.TestProperties.def_readwrite_static = 2
|
m.TestProperties.def_readwrite_static = 2
|
||||||
assert m.TestProperties.def_readwrite_static == 2
|
assert m.TestProperties.def_readwrite_static == 2
|
||||||
|
|
||||||
assert m.TestProperties.def_property_readonly_static == 2
|
|
||||||
with pytest.raises(AttributeError) as excinfo:
|
with pytest.raises(AttributeError) as excinfo:
|
||||||
m.TestProperties.def_property_readonly_static = 3
|
dummy = m.TestProperties.def_writeonly_static # noqa: F841 unused var
|
||||||
|
assert "unreadable attribute" in str(excinfo)
|
||||||
|
|
||||||
|
m.TestProperties.def_writeonly_static = 3
|
||||||
|
assert m.TestProperties.def_readonly_static == 3
|
||||||
|
|
||||||
|
assert m.TestProperties.def_property_readonly_static == 3
|
||||||
|
with pytest.raises(AttributeError) as excinfo:
|
||||||
|
m.TestProperties.def_property_readonly_static = 99
|
||||||
assert "can't set attribute" in str(excinfo)
|
assert "can't set attribute" in str(excinfo)
|
||||||
|
|
||||||
m.TestProperties.def_property_static = 3
|
m.TestProperties.def_property_static = 4
|
||||||
assert m.TestProperties.def_property_static == 3
|
assert m.TestProperties.def_property_static == 4
|
||||||
|
|
||||||
|
with pytest.raises(AttributeError) as excinfo:
|
||||||
|
dummy = m.TestProperties.def_property_writeonly_static
|
||||||
|
assert "unreadable attribute" in str(excinfo)
|
||||||
|
|
||||||
|
m.TestProperties.def_property_writeonly_static = 5
|
||||||
|
assert m.TestProperties.def_property_static == 5
|
||||||
|
|
||||||
# Static property read and write via instance
|
# Static property read and write via instance
|
||||||
instance = m.TestProperties()
|
instance = m.TestProperties()
|
||||||
@ -127,6 +156,13 @@ def test_static_properties():
|
|||||||
assert m.TestProperties.def_readwrite_static == 2
|
assert m.TestProperties.def_readwrite_static == 2
|
||||||
assert instance.def_readwrite_static == 2
|
assert instance.def_readwrite_static == 2
|
||||||
|
|
||||||
|
with pytest.raises(AttributeError) as excinfo:
|
||||||
|
dummy = instance.def_property_writeonly_static # noqa: F841 unused var
|
||||||
|
assert "unreadable attribute" in str(excinfo)
|
||||||
|
|
||||||
|
instance.def_property_writeonly_static = 4
|
||||||
|
assert instance.def_property_static == 4
|
||||||
|
|
||||||
# It should be possible to override properties in derived classes
|
# It should be possible to override properties in derived classes
|
||||||
assert m.TestPropertiesOverride().def_readonly == 99
|
assert m.TestPropertiesOverride().def_readonly == 99
|
||||||
assert m.TestPropertiesOverride.def_readonly_static == 99
|
assert m.TestPropertiesOverride.def_readonly_static == 99
|
||||||
|
Loading…
Reference in New Issue
Block a user