Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] Generate docstrings that are compatible with Sphinx and stubgen #2619

Open
AWhetter opened this issue Oct 26, 2020 · 1 comment · May be fixed by #2621
Open

[FEAT] Generate docstrings that are compatible with Sphinx and stubgen #2619

AWhetter opened this issue Oct 26, 2020 · 1 comment · May be fixed by #2621
Labels
docs Docs or GitHub info enhancement signatures Issue about static signatures

Comments

@AWhetter
Copy link
Contributor

AWhetter commented Oct 26, 2020

This issue follows on from a discussion at python/mypy#9070

Current Behaviour

Currently, when a function is overloaded in pybind, the docstring generated includes a single, generic type signature at the top (name(*args, **kwargs) -> Any).
Sphinx autodoc will document the signature of the function using this generic type signature (https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc_docstring_signature). However it recently added support for reading multiple type signatures from the docstring (sphinx-doc/sphinx#2106). This is apparently an already established convention and it's used in CPython source code (https://github.com/python/cpython/blob/c0f22fb8b3006936757cebb959cee94e285bc503/Objects/rangeobject.c#L156).
Furthermore, stubgen (https://mypy.readthedocs.io/en/stable/stubgen.html) uses this generic function signature, which makes the type stubs that it generates useless (this was the original issue discussed in python/mypy#9070).

New Behaviour

This issue is a feature request to prepend docstrings with all of the overload signatures of the function (unless options.disable_function_signatures() has been called).

To use an example from the pybind11 documentation:

py::class_<Pet>(m, "Pet")
   .def(py::init<const std::string &, int>())
   .def("set", (void (Pet::*)(int)) &Pet::set, "Set the pet's age")
   .def("set", (void (Pet::*)(const std::string &)) &Pet::set, "Set the pet's name");

This produces docstrings that looks like the following:

>>> help(example.Pet)

class Pet(__builtin__.object)
 |  Methods defined here:
 |
 |  __init__(...)
 |      __init__(self: Pet, arg0: str, arg1: int) -> None
 |
 |  set(...)
 |      set(*args, **kwargs) -> Any
 |      Overloaded function.
 |
 |      1. set(self: Pet, arg0: int) -> None
 |
 |      Set the pet's age
 |
 |      2. set(self: Pet, arg0: str) -> None
 |
 |      Set the pet's name

This request is to have the docstrings instead look like the following:

>>> help(example.Pet)

class Pet(__builtin__.object)
 |  Methods defined here:
 |
 |  __init__(...)
 |      __init__(self: Pet, arg0: str, arg1: int) -> None
 |
 |  set(...)
 |      set(self: Pet, arg0: int) -> None
 |      set(self: Pet, arg0: str) -> None
 |      Overloaded function.
 |
 |      1. set(self: Pet, arg0: int) -> None
 |
 |      Set the pet's age
 |
 |      2. set(self: Pet, arg0: str) -> None
 |
 |      Set the pet's name

Furthermore, I think it needs to be possible to have all of the function signatures at the start of the docstring generated but without the section headings. This would allow a user to write the whole docstring as one, but still have the function signatures generated for Sphinx and/or stubgen.

So it should be possible to generate a docstring like this:

>>> help(example.Pet.set)

set(...)
 |      set(self: Pet, arg0: int) -> None
 |      set(self: Pet, arg0: str) -> None
 |      Set attributes on the pet.
 |
 |      When arg0 is an integer, the pet's age is set.
 |      When arg0 is a string, the pet's name is set.

Implementation Details

I think the second change, to generate the signatures without signature headings, would require the addition of a new option. To maintain current behaviour, the options.disable_function_signatures() function could still turn all signatures off. A new option disable_signature_headings() would turn only the section headings off.

So the following code would produce the docstring seen above:

py::options options;
options.disable_signature_headings();

py::class_<Pet>(m, "Pet")
   .def(py::init<const std::string &, int>())
   .def("set", (void (Pet::*)(int)) &Pet::set)
   .def("set", (void (Pet::*)(const std::string &)) &Pet::set, "Set attributes on the pet.\n\nWhen arg0 is an integer, the pet's age is set.\nWhen arg0 is a string, the pet's name is set.");
@ka-bo
Copy link
Contributor

ka-bo commented Jun 16, 2021

Maybe a rework of docstrings for overloaded functions could also include a fix for #3037?

wjakob added a commit to wjakob/nanobind that referenced this issue Mar 29, 2022
Sphinx can nowadays nicely parse overload chains if all function
signatures are printed at the beginning of the docstring. This change
therefore moves signatures to the beginning to improve Sphinx output.

When an overload chain specifies multiple overload-specific docstrings,
another copy of the signature (in ``code quotes``) is inserted later on
for context.

This change resembles a PR in the pybind11 repository
(pybind/pybind11#2619).
@ax3l ax3l added enhancement docs Docs or GitHub info signatures Issue about static signatures labels Aug 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Docs or GitHub info enhancement signatures Issue about static signatures
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants