twisted / pydoctor

This is pydoctor, an API documentation generator that works by static analysis.
https://pydoctor.readthedocs.io
Other
181 stars 49 forks source link

Minimize the docstring line numbers offset introduced by napoleon #807

Open tristanlatr opened 1 month ago

tristanlatr commented 1 month ago

The napoleon docstring preprocessor has been ported from sphinx. It's a great and complete piece of code. It has only one drawback, and that's the fact that it messes up with the line numbers of docstring fields. It is a completely normal side effect since the code operates as a string transformer before parsing it with docutils.

Even for the simplest example, some offset will be of 1, and this is probably best to accept otherwise we have to look at re-implementing the whole google and numpy parsing. And this is not a viable option for the moment. So let's say an offset of 1 is acceptable. But still I wonder if we could ensure at any time len(original_docsstring.splitlines()) == len(transformed_docstring.splitlines()).

Anyway here goes a few examples of cases that could be improved:

Example Function

Raises:
    InvalidDimensionsError: With description

Is transformed into :

Example Function

:raises InvalidDimensionsError: With description

But it would best if that was

Example Function

:raises InvalidDimensionsError: With description

And

Example Function

Raises:
    InvalidDimensionsError: 
        With description

Is transformed into the same :

Example Function

:raises InvalidDimensionsError: With description

But it would best if that was

Example Function

:raises InvalidDimensionsError: 
    With description

So now look at example where the line number diffs are greater

Example Function

Raises:
    RuntimeError:
        A setting wasn't specified, or was invalid.
    ValueError:
        Something something value error.
    :py:class:`AttributeError`
        errors for missing attributes.
    ~InvalidDimensionsError
        If the dimensions couldn't be parsed.
    `InvalidArgumentsError`
        If the arguments are invalid.
    :exc:`~ValueError`
        If the arguments are wrong.

Is transformed into :

Example Function

:raises RuntimeError: A setting wasn't specified, or was invalid.
:raises ValueError: Something something value error.
:raises AttributeError: errors for missing attributes.
:raises ~InvalidDimensionsError: If the dimensions couldn't be parsed.
:raises InvalidArgumentsError: If the arguments are invalid.
:raises ~ValueError: If the arguments are wrong.

But it would best if that was

Example Function

:raises RuntimeError: 
    A setting wasn't specified, or was invalid.
:raises ValueError: 
    Something something value error.
:raises AttributeError: 
    errors for missing attributes.
:raises ~InvalidDimensionsError: 
    If the dimensions couldn't be parsed.
:raises InvalidArgumentsError: 
    If the arguments are invalid.
:raises ~ValueError: 
    If the arguments are wrong.

Another interesting example is the return clause that is translated to two fields.

Example Function

Returns:
    :class:`numpy.ndarray`: A :math:`n \\times 2` array containing
    a bunch of math items

Is transformed into :

Example Function

:returns: A :math:`n \\times 2` array containing
          a bunch of math items
:returntype: :class:`numpy.ndarray`

But it would best if that was

Example Function

:returntype: :class:`numpy.ndarray`
:returns: A :math:`n \\times 2` array containing
          a bunch of math items