sphinx icon indicating copy to clipboard operation
sphinx copied to clipboard

autodoc: Allow overloaded functions to have different docstrings

Open eric-wieser opened this issue 5 years ago • 11 comments

If would be great if sphinx could emit all of the docstrings for something like:

from typing import overload

@overload
def my_bytes(n: int):
    """ construct an empty array of n bytes """

@overload
def my_bytes(s: bytes):
    """ copy an existing bytes-like object """

def my_bytes(b):
    """ Some common docstring """
    return bytes(b)

As of the fix to #3610, autodoc emits:

.. function:: my_bytes(n: int)
              my_bytes(s: bytes)
    Some common docstring

but does not extract the first two docstrings.

Perhaps one reasonable option would be to emit

.. function:: my_bytes(n: int)
    construct an empty array of n bytes

    Some common docstring
.. function:: my_bytes(s: bytes)
    copy an existing bytes-like object

    Some common docstring

although this doesn't scale to .. class particularly well.

Another choice would be to render numbers next to the overloads (in the style of cppreference), and then refer to them in the text. In its most minimal form, that would be:

.. function:: my_bytes(n: int)
              my_bytes(s: bytes)
    :number-overloads:

    1. construct an empty array of n bytes
    2. copy an existing bytes-like object

    Some common docstring

but perhaps new directives or roles could be introduced to render that better:

.. function:: my_bytes(n: int)
              my_bytes(s: bytes)

    .. overloadref::1  # presence of these makes the numbers appear as needed.
        construct an empty array of n bytes
    .. overloadref::2
        copy an existing bytes-like object

    Some common docstring

eric-wieser avatar Jun 04 '20 17:06 eric-wieser

It sounds good. But it is very difficult to support inheritance, f-string and other features. And I think this is not commonly used.

tk0miya avatar Jun 04 '20 18:06 tk0miya

I don't see how f-string or inheritance would come into play here: f-strings cannot be used as docstrings anyway, and inheritance seems already handled by the existing patch?

eric-wieser avatar Jun 04 '20 18:06 eric-wieser

It is great to get overload support! Perhaps I missed it in one of the other issues/PRs but is it correctly understood that only

.. function:: my_bytes(n: int)
              my_bytes(s: bytes)

is supposed to work right now, while

.. function:: my_bytes(n: int)
.. function:: my_bytes(s: bytes)

yields a duplication error? It would be great if both styles are supported :-).

jakobandersen avatar Jun 04 '20 18:06 jakobandersen

@jakobandersen: I think that

.. function:: my_bytes(n: int)
              my_bytes(s: bytes)

has worked for quite a long time. What changed recently is that @typing.overload causes it to be emitted automatically.

I think your problem can be solved with:

.. function:: my_bytes(n: int)
    :no-index:
.. function:: my_bytes(s: bytes)

eric-wieser avatar Jun 04 '20 18:06 eric-wieser

Ah, I see. I suggest to add a note in the Python domain documentation (not sure what the best way to write it is currently).

Indeed the :no-index: makes the error disappear, but I wouldn't say it solves the problem. The grouped version indicates to me that the functions are sort of closely related, while the non-grouped indicates a more distant relationship. In your example, without too much thinking, I would have them separate: one is a simple copy-constructor the other is (slightly) more interesting. (I may be somewhat biased by usually documenting Python bindings for C++ :-))

jakobandersen avatar Jun 04 '20 18:06 jakobandersen

Ah, I see. I suggest to add a note in the Python domain documentation

Agreed, I didn't even know no-index was allowed outside autofunction, it doesn't seem to be documented.

In your example, without too much thinking,

If you're willing to put in a bit more thinking, how do you think the constructors for my class here should be documented? Right now, I'm just using .. class with :no-index: inside the docstring for __init__, which feels rather hacky.

eric-wieser avatar Jun 04 '20 18:06 eric-wieser

Oh, I don't know that. It means we can get docstrings via AST, great!

f-strings cannot be used as docstrings anyway

tk0miya avatar Jun 07 '20 17:06 tk0miya

@eric-wieser, I'm not too familiar with the domain so it's tricky to judge, but to me it looks like 2 or possibly 3 different groups: default and copy constructor, and then the rest, or the rest partitioned in some way. But it should be possible to change the hax classes to simply __init__ functions (with the same :no-index: annotation).

jakobandersen avatar Jun 10 '20 07:06 jakobandersen

Having a better way to handle overloaded functions would be great. Currently trying to make something work.

.. class:: StrokeAttribute

   Class to define a set of attributes associated with a :class:`StrokeVertex`.
   The attribute set stores the color, alpha and thickness values for a Stroke
   Vertex.

   .. method:: __init__()
               __init__(brother)
               __init__(red, green, blue, alpha, thickness_right, thickness_left)
               __init__(attribute1, attribute2, t)

      __init__()
         Default constructor.
      __init__(brother)
         Copy constructor.
      __init__(red, green, blue, alpha, thickness_right, thickness_left)
         Build a stroke vertex attribute from a set of parameters.
      __init__(attribute1, attribute2, t)
            Interpolation constructor. Build a StrokeAttribute from two
            StrokeAttribute objects and an interpolation parameter.

      :arg brother: A StrokeAttribute object to be used as a copy constructor.
      :type brother: :class:`StrokeAttribute`
      :arg red: Red component of a stroke color.
      :type red: float
      :arg green: Green component of a stroke color.
      :type green: float
      :arg blue: Blue component of a stroke color.
      :type blue: float
      :arg alpha: Alpha component of a stroke color.
      :type alpha: float
      :arg thickness_right: Stroke thickness on the right.
      :type thickness_right: float
      :arg thickness_left: Stroke thickness on the left.
      :type thickness_left: float
      :arg attribute1: The first StrokeAttribute object.
      :type attribute1: :class:`StrokeAttribute`
      :arg attribute2: The second StrokeAttribute object.
      :type attribute2: :class:`StrokeAttribute`
      :arg t: The interpolation parameter (0 <= t <= 1).
      :type t: float

It would be nice if we could have a description for each overloaded function without duplicating text.

Blendify avatar Oct 10 '20 01:10 Blendify

@eric-wieser , is it here?

https://pytorch.org/docs/stable/generated/torch.optim.Optimizer.step.html#torch.optim.Optimizer.step

image

@overload
def step(self, closure: None = ...) -> None:
    ...

@overload
def step(self, closure: Callable[[], float]) -> float:
    ...

def step(self, closure: Optional[Callable[[], float]] = None) -> Optional[float]:
    r"""Performs a single optimization step (parameter update).

kuraga avatar Mar 04 '24 13:03 kuraga

No, I don't think the original request works yet. https://github.com/sizmailov/pybind11-stubgen/issues/221

The last example posted has a single doc string per overload, instead of a unique doc string for each overload.

ax3l avatar Apr 03 '24 22:04 ax3l

yes please

devesnosy avatar Nov 21 '24 19:11 devesnosy