From dfb7d7c58d126519e64a7a70505b2dd56705987c Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Mon, 3 Feb 2020 08:01:30 +0000 Subject: [PATCH] Fix docstrings generated by `[pyo3(get, set)]` --- CHANGELOG.md | 1 + pyo3-derive-backend/src/pyclass.rs | 4 ++-- src/class/methods.rs | 3 +++ tests/test_class_basics.rs | 29 ++++++++++++++++++++++++++++- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a5859f03af..05c309818cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. * `PyClass`, `PyClassShell`, `PyObjectLayout`, `PyClassInitializer`. [#683](https://github.com/PyO3/pyo3/pull/683) * Implemented `IntoIterator` for `PySet` and `PyFrozenSet`. [#716](https://github.com/PyO3/pyo3/pull/716) * `FromPyObject` is now automatically implemented for `T: Clone` pyclasses. [#730](https://github.com/PyO3/pyo3/pull/730) +* `#[pyo3(get)]` and `#[pyo3(set)]` will now use the Rust doc-comment from the field for the Python property. [#755](https://github.com/PyO3/pyo3/pull/755) ### Fixed diff --git a/pyo3-derive-backend/src/pyclass.rs b/pyo3-derive-backend/src/pyclass.rs index d4d9e564df3..5c7d87578a6 100644 --- a/pyo3-derive-backend/src/pyclass.rs +++ b/pyo3-derive-backend/src/pyclass.rs @@ -465,8 +465,8 @@ fn impl_descriptors( .map(|desc| { let name = field.ident.as_ref().unwrap(); - // FIXME better doc? - let doc = syn::LitStr::new(&name.to_string(), name.span()); + let doc = utils::get_doc(&field.attrs, None, true) + .unwrap_or_else(|_| syn::LitStr::new(&name.to_string(), name.span())); let field_ty = &field.ty; match *desc { diff --git a/src/class/methods.rs b/src/class/methods.rs index acfaebf33b5..9826573dba8 100644 --- a/src/class/methods.rs +++ b/src/class/methods.rs @@ -108,6 +108,9 @@ impl PySetterDef { .expect("Method name must not contain NULL byte") .into_raw(); } + if dst.doc.is_null() { + dst.doc = self.doc.as_ptr() as *mut libc::c_char; + } dst.set = Some(self.meth); } } diff --git a/tests/test_class_basics.rs b/tests/test_class_basics.rs index c026bfd17ad..809210a1482 100644 --- a/tests/test_class_basics.rs +++ b/tests/test_class_basics.rs @@ -23,7 +23,19 @@ fn empty_class() { /// Line3 // this is not doc string #[pyclass] -struct ClassWithDocs {} +struct ClassWithDocs { + /// Property field + #[pyo3(get, set)] + value: i32, + + /// Read-only property field + #[pyo3(get)] + readonly: i32, + + /// Write-only property field + #[pyo3(set)] + writeonly: i32, +} #[test] fn class_with_docstr() { @@ -36,6 +48,21 @@ fn class_with_docstr() { typeobj, "assert typeobj.__doc__ == 'Line1\\nLine2\\n Line3'" ); + py_run!( + py, + typeobj, + "assert typeobj.value.__doc__ == 'Property field'" + ); + py_run!( + py, + typeobj, + "assert typeobj.readonly.__doc__ == 'Read-only property field'" + ); + py_run!( + py, + typeobj, + "assert typeobj.writeonly.__doc__ == 'Write-only property field'" + ); } }