pybind11 icon indicating copy to clipboard operation
pybind11 copied to clipboard

feat: add type signatures to docstrings of properties.

Open frosch123 opened this issue 2 years ago • 3 comments

Description

Currently signatures with type information are generated into docstrings for functions.

This PR adds type information in docstrings of properties, like

value_property: int

Property documentation.

Generation of this type information is enabled by default, but can be disabled/enabled with the same setting py::options::disable_function_signatures as for functions. I did not consider it necessary to have separate settings for function and property signatures.

Suggested changelog entry:

* Add type signatures in docstrings of properties.

frosch123 avatar Jul 31 '22 16:07 frosch123

@frosch123 See also https://github.com/pybind/pybind11/issues/3815 which may also cause issues here with static properties.

Skylion007 avatar Aug 01 '22 18:08 Skylion007

I initiated our (Google's) global testing for this PR, to see if there is anything that trips over this change.

Quick update: Unfortunately I'm seeing breakages with at least two root causes. The total number of breakages is a few thousands (out of millions). I will try to drill down as soon as I get a chance.

rwgk avatar Aug 02 '22 17:08 rwgk

After a couple hours drilling into a deep pile of tooling, and a guess ... the diff below fixes all breakages reported in my previous comment.

I don't have a full explanation. I know that MyPy's stubgen is in the mix, but not much more. There is also a script (currently not open source AFAIK) that post-processes the stubgen output to generate a .pyi file for pytype. I don't know if it is stubgen or the post-processing that stumbles over the name of the property.

What I'm seeing is something like this:

This part is manually edited and untested but hopefully close enough:

    .def(py::init([](int left, int bottom, int width, int height) {
              return Rect{left, bottom, width, height};
            }),
            py::arg("left"), py::arg("bottom"), py::arg("width"),
            py::arg("height"));
    .def_readwrite("left", &Rect::left)
    .def_readwrite("bottom", &Rect::bottom)
    .def_readwrite("width", &Rect::width)
    .def_readwrite("height", &Rect::height);

.pyi file before this PR:

class Rect:
    bottom: int
    height: int
    left: int
    width: int

.pyi file with this PR:

class Rect:
    bottom: bottom
    height: height
    left: left
    width: width

I won't have time to drill into this much more. Does someone familiar with stubgen have a clue?

diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h
--- a/include/pybind11/pybind11.h
+++ b/include/pybind11/pybind11.h
@@ -1384,18 +1384,18 @@ protected:
         std::string doc;
         if (rec_func != nullptr) {
             if (pybind11::options::show_function_signatures()) {
-                doc += name;
                 if (rec_func->signature != nullptr) {
                     std::string sig = rec_func->signature;
                     size_t ret = sig.rfind(" -> ");
                     if (ret != std::string::npos) {
-                        doc += ": ";
                         doc += sig.substr(ret + 4);
                     }
                 }
-                doc += "\n\n";
             }
             if (rec_func->doc != nullptr && pybind11::options::show_user_defined_docstrings()) {
+                if (!doc.empty()) {
+                    doc += "\n\n";
+                }
                 doc += rec_func->doc;
             }
         }
diff --git a/tests/test_class.py b/tests/test_class.py
--- a/tests/test_class.py
+++ b/tests/test_class.py
@@ -91,7 +91,7 @@ def test_docstrings(doc):
     assert (
         doc(UserType.value)
         == """
-        value: int
+        int

         Get/set value using a property
     """
diff --git a/tests/test_docstring_options.py b/tests/test_docstring_options.py
--- a/tests/test_docstring_options.py
+++ b/tests/test_docstring_options.py
@@ -39,13 +39,13 @@ def test_docstring_options():
     # Suppression of user-defined docstrings for non-function objects
     assert not m.DocstringTestFoo.__doc__

-    assert m.DocstringTestFoo.value_prop1.__doc__ == "value_prop1: int\n\n"
+    assert m.DocstringTestFoo.value_prop1.__doc__ == "int"

     assert (
         m.DocstringTestFoo.value_prop2.__doc__
-        == "value_prop2: int\n\nThis is a property docstring"
+        == "int\n\nThis is a property docstring"
     )

-    assert m.DocstringTestFoo.value_prop3.__doc__ == "value_prop3: int\n\n"
+    assert m.DocstringTestFoo.value_prop3.__doc__ == "int"

     assert not m.DocstringTestFoo.value_prop4.__doc__

rwgk avatar Aug 20 '22 10:08 rwgk