sphinx-doc / sphinx

The Sphinx documentation generator
https://www.sphinx-doc.org/
Other
6.61k stars 2.13k forks source link

Napoleon Raises mutli-line description parsing #13126

Open BalzaniEdoardo opened 1 week ago

BalzaniEdoardo commented 1 week ago

Describe the bug

Multi-line description of Raises messages are concatenared into a single string interleaved by \n, this doesn't ultimately render in html (i.e. it renders as a single string). I have napoleon

How to Reproduce

>>> from sphinx.ext.napoleon.docstring import NumpyDocstring
>>> import numpy as np
>>> out = NumpyDocstring(np.matmul.__doc__, obj=np.matmul)
>>> out._parsed_lines
["matmul(x1, x2, /, out=None, *, casting='same_kind', order='K', dtype=None, subok=True[, signature, extobj, axes, axis])",
 '',
 'Matrix product of two arrays.',
 '',
 ':param x1: Input arrays, scalars not allowed.',
 ':type x1: array_like',
 ':param x2: Input arrays, scalars not allowed.',
 ':type x2: array_like',
 ':param out: A location into which the result is stored. If provided, it must have',
 '            a shape that matches the signature `(n,k),(k,m)->(n,m)`. If not',
 '            provided or None, a freshly-allocated array is returned.',
 ':type out: ndarray, optional',
 ':param \\*\\*kwargs: For other keyword-only arguments, see the',
 '                   :ref:`ufunc docs <ufuncs.kwargs>`.',
 '',
 '                   .. versionadded:: 1.16',
 '                      Now handles ufunc kwargs',
 '',
 ':returns: **y** -- The matrix product of the inputs.',
 '          This is a scalar only when both x1, x2 are 1-d vectors.',
 ':rtype: ndarray',
 '',
 ':raises ValueError: If the last dimension of `x1` is not the same size as\n    the second-to-last dimension of `x2`.\n    \n    If a scalar value is passed in.',
 '',
 '.. seealso::',
 '',
 '   :obj:`vdot`',
 '       Complex-conjugating dot product.',
 '   ',
 '   :obj:`tensordot`',
 '       Sum products over arbitrary axes.',
 '   ',
 '   :obj:`einsum`',
 '       Einstein summation convention.',
 '   ',
 '   :obj:`dot`',
 '       alternative matrix product with different broadcasting rules.',
 '',
 '.. rubric:: Notes',
 '',
 'The behavior depends on the arguments in the following way.',
 '',
 '- If both arguments are 2-D they are multiplied like conventional',
 '  matrices.',
 '- If either argument is N-D, N > 2, it is treated as a stack of',
 '  matrices residing in the last two indexes and broadcast accordingly.',
 '- If the first argument is 1-D, it is promoted to a matrix by',
 '  prepending a 1 to its dimensions. After matrix multiplication',
 '  the prepended 1 is removed.',
 '- If the second argument is 1-D, it is promoted to a matrix by',
 '  appending a 1 to its dimensions. After matrix multiplication',
 '  the appended 1 is removed.',
 '',
 '``matmul`` differs from ``dot`` in two important ways:',
 '',
 '- Multiplication by scalars is not allowed, use ``*`` instead.',
 '- Stacks of matrices are broadcast together as if the matrices',
 '  were elements, respecting the signature ``(n,k),(k,m)->(n,m)``:',
 '',
 '  >>> a = np.ones([9, 5, 7, 4])',
 '  >>> c = np.ones([9, 5, 4, 3])',
 '  >>> np.dot(a, c).shape',
 '  (9, 5, 7, 9, 5, 3)',
 '  >>> np.matmul(a, c).shape',
 '  (9, 5, 7, 3)',
 '  >>> # n is 7, k is 4, m is 3',
 '',
 'The matmul function implements the semantics of the ``@`` operator',
 'introduced in Python 3.5 following :pep:`465`.',
 '',
 'It uses an optimized BLAS library when possible (see `numpy.linalg`).',
 '',
 '.. rubric:: Examples',
 '',
 'For 2-D arrays it is the matrix product:',
 '',
 '>>> a = np.array([[1, 0],',
 '...               [0, 1]])',
 '>>> b = np.array([[4, 1],',
 '...               [2, 2]])',
 '>>> np.matmul(a, b)',
 'array([[4, 1],',
 '       [2, 2]])',
 '',
 'For 2-D mixed with 1-D, the result is the usual.',
 '',
 '>>> a = np.array([[1, 0],',
 '...               [0, 1]])',
 '>>> b = np.array([1, 2])',
 '>>> np.matmul(a, b)',
 'array([1, 2])',
 '>>> np.matmul(b, a)',
 'array([1, 2])',
 '',
 'Broadcasting is conventional for stacks of arrays',
 '',
 '>>> a = np.arange(2 * 2 * 4).reshape((2, 2, 4))',
 '>>> b = np.arange(2 * 2 * 4).reshape((2, 4, 2))',
 '>>> np.matmul(a,b).shape',
 '(2, 2, 2)',
 '>>> np.matmul(a, b)[0, 1, 1]',
 '98',
 '>>> sum(a[0, 1, :] * b[0 , :, 1])',
 '98',
 '',
 'Vector, vector returns the scalar inner product, but neither argument',
 'is complex-conjugated:',
 '',
 '>>> np.matmul([2j, 3j], [2j, 3j])',
 '(-13+0j)',
 '',
 'Scalar multiplication raises an error.',
 '',
 '>>> np.matmul([1,2], 3)',
 'Traceback (most recent call last):',
 '...',
 'ValueError: matmul: Input operand 1 does not have enough dimensions ...',
 '',
 'The ``@`` operator can be used as a shorthand for ``np.matmul`` on',
 'ndarrays.',
 '',
 '>>> x1 = np.array([2j, 3j])',
 '>>> x2 = np.array([2j, 3j])',
 '>>> x1 @ x2',
 '(-13+0j)',
 '',
 '.. versionadded:: 1.10.0']

Here you can see the ValueError description differs form the out parameter description, the former is a single string, the latter has multiple list entry with proper indent.

Environment Information

Platform:              darwin; (macOS-14.7-arm64-arm-64bit)
Python version:        3.10.11 (main, Feb 21 2024, 15:26:02) [Clang 15.0.0 (clang-1500.1.0.2.5)])
Python implementation: CPython
Sphinx version:        8.1.3
Docutils version:      0.21.2
Jinja2 version:        3.1.4
Pygments version:      2.18.0

Sphinx extensions

>>> from sphinx.ext.napoleon.docstring import NumpyDocstring
>>> import numpy as np
>>> out = NumpyDocstring(np.matmul.__doc__, obj=np.matmul)
>>> out._parsed_lines

As shown before, the output of the ValueError description is a single string which ultimately won't render as multi-line in HTML

Additional context

No response

BalzaniEdoardo commented 1 week ago

The processing happens in sphinx/ext/napoleon/doscring.py, in the _parse_raises_section function, line 800.