sphinx-doc / sphinx

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

Multiple return types aren't hyperlinked #9394

Open mhostetter opened 3 years ago

mhostetter commented 3 years ago

Using multiple return values, sphinx/napoleon doesn't hyperlink the return types as it does with parameters. I tried with and without explicitly naming the return values.

    r"""
    Parameters
    ----------
    n : int
        A positive integer.
    B : int, optional
        The max divisor in the trial division. The default is `None` which corresponds to :math:`B = \sqrt{n}`.
        If :math:`B > \sqrt{n}`, the algorithm will only search up to :math:`\sqrt{n}`, since a factor of :math:`n`
        cannot be larger than :math:`\sqrt{n}`.

    Returns
    -------
    list
        The discovered prime factors :math:`\{p_1, \dots, p_k\}`.
    list
        The corresponding prime exponents :math:`\{e_1, \dots, e_k\}`.
    int
        The residual factor :math:`n_r`.
    """

renders like this

image

And when naming the return values

    r"""
    Parameters
    ----------
    n : int
        A positive integer.
    B : int, optional
        The max divisor in the trial division. The default is `None` which corresponds to :math:`B = \sqrt{n}`.
        If :math:`B > \sqrt{n}`, the algorithm will only search up to :math:`\sqrt{n}`, since a factor of :math:`n`
        cannot be larger than :math:`\sqrt{n}`.

    Returns
    -------
    p : list
        The discovered prime factors :math:`\{p_1, \dots, p_k\}`.
    e : list
        The corresponding prime exponents :math:`\{e_1, \dots, e_k\}`.
    n_r : int
        The residual factor :math:`n_r`.
    """

it renders like this

image

However, when using a single return value, the hyperlinks work as expected.

image

FYI, I am using intershpinx_mapping to link to the python docs, which correctly works for the Parameters section.

# File: conf.py
intersphinx_mapping = {
    'python': ('https://docs.python.org/3', None),
    'numpy': ('https://numpy.org/doc/stable/', None),
    'numba': ('https://numba.pydata.org/numba-doc/latest/', None)
}
astrojuanlu commented 3 years ago

On one hand, I have been a victim of this myself and your markup looks correct to me, but on the other hand I just checked that for example the Astropy folks got it working https://docs.astropy.org/en/latest/api/astropy.coordinates.Attribute.html#astropy.coordinates.Attribute.convert_input so I'm not sure what is happening here 🤔

mhostetter commented 3 years ago

FWIW, from my example, this is the project, conf.py, example function, and the rendered docs.

I'm using sphinx_rtd_theme and astropy isn't. I don't know if that makes a difference.

tk0miya commented 3 years ago

Could you try napoleon_preprocess_types = True, please?

mhostetter commented 3 years ago

Thanks for the suggestion. It does now hyperlink, however the hyperlink styling has changed. I'm using sphinx_rtd_theme.

Before: image

After adding napoleon_preprocess_types = True: image

astrojuanlu commented 3 years ago

@mhostetter Please open an issue about this on https://github.com/readthedocs/sphinx_rtd_theme/, and if you have a Read the Docs build URL, attach it too so we can easily have a look. I guess napoleon_preprocess_types = True generates different HTML (which is then styled differently).

mhostetter commented 3 years ago

I can open a new issue there. I would note, though, that the default theme in sphinx manifests the same error. So I wonder if the issue is in sphinx itself and not necessarily sphinx_rtd_theme.

Without setting napoleon_preprocess_types: image

After setting napoleon_preprocess_types = True: image

When you asked for a "build URL" do you mean a link to the public docs generated from sphinx or a link to the build output from readthedocs website? Let me know how else I can help in the debugging.

astrojuanlu commented 3 years ago

Interesting. If the issue is not specific to the theme, then definitely let's continue the conversation here.

I guess this is the function you're talking about: https://galois.readthedocs.io/en/latest/api/galois.trial_division.html#galois.trial_division

The generated HTML is <a class="reference external" href="https://docs.python.org/3/library/functions.html#int" title="(in Python v3.9)"><em>int</em></a>. What happens if napoleon_preprocess_types = True then?

mhostetter commented 3 years ago

Without setting napoleon_preprocess_types, the html is

<dt class="field-odd">Parameters</dt>
<dd class="field-odd"><ul class="simple">
<li><p><strong>n</strong> (<a class="reference external" href="https://docs.python.org/3/library/functions.html#int" title="(in Python v3.9)"><em>int</em></a>) – A positive integer.</p></li>
<li><p><strong>B</strong> (<a class="reference external" href="https://docs.python.org/3/library/functions.html#int" title="(in Python v3.9)"><em>int</em></a><em>, </em><em>optional</em>) – The max divisor in the trial division. The default is <code class="samp docutils literal notranslate"><span class="pre">None</span></code> which corresponds to <span class="math notranslate nohighlight">\(B = \sqrt{n}\)</span>.
If <span class="math notranslate nohighlight">\(B &gt; \sqrt{n}\)</span>, the algorithm will only search up to <span class="math notranslate nohighlight">\(\sqrt{n}\)</span>, since a factor of <span class="math notranslate nohighlight">\(n\)</span>
cannot be larger than <span class="math notranslate nohighlight">\(\sqrt{n}\)</span>.</p></li>
</ul>
</dd>
<dt class="field-even">Returns</dt>
<dd class="field-even"><p><ul class="simple">
<li><p><em>list</em> – The discovered prime factors <span class="math notranslate nohighlight">\(\{p_1, \dots, p_k\}\)</span>.</p></li>
<li><p><em>list</em> – The corresponding prime exponents <span class="math notranslate nohighlight">\(\{e_1, \dots, e_k\}\)</span>.</p></li>
<li><p><em>int</em> – The residual factor <span class="math notranslate nohighlight">\(n_r\)</span>.</p></li>
</ul>

and renders like

image

After setting napoleon_preprocess_types = True, the html is

<dt class="field-odd">Parameters</dt>
<dd class="field-odd"><ul class="simple">
<li><p><strong>n</strong> (<a class="reference external" href="https://docs.python.org/3/library/functions.html#int" title="(in Python v3.9)"><code class="xref py py-class docutils literal notranslate"><span class="pre">int</span></code></a>) – A positive integer.</p></li>
<li><p><strong>B</strong> (<a class="reference external" href="https://docs.python.org/3/library/functions.html#int" title="(in Python v3.9)"><code class="xref py py-class docutils literal notranslate"><span class="pre">int</span></code></a>, <em>optional</em>) – The max divisor in the trial division. The default is <code class="samp docutils literal notranslate"><span class="pre">None</span></code> which corresponds to <span class="math notranslate nohighlight">\(B = \sqrt{n}\)</span>.
If <span class="math notranslate nohighlight">\(B &gt; \sqrt{n}\)</span>, the algorithm will only search up to <span class="math notranslate nohighlight">\(\sqrt{n}\)</span>, since a factor of <span class="math notranslate nohighlight">\(n\)</span>
cannot be larger than <span class="math notranslate nohighlight">\(\sqrt{n}\)</span>.</p></li>
</ul>
</dd>
<dt class="field-even">Returns</dt>
<dd class="field-even"><p><ul class="simple">
<li><p><a class="reference external" href="https://docs.python.org/3/library/stdtypes.html#list" title="(in Python v3.9)"><code class="xref py py-class docutils literal notranslate"><span class="pre">list</span></code></a> – The discovered prime factors <span class="math notranslate nohighlight">\(\{p_1, \dots, p_k\}\)</span>.</p></li>
<li><p><a class="reference external" href="https://docs.python.org/3/library/stdtypes.html#list" title="(in Python v3.9)"><code class="xref py py-class docutils literal notranslate"><span class="pre">list</span></code></a> – The corresponding prime exponents <span class="math notranslate nohighlight">\(\{e_1, \dots, e_k\}\)</span>.</p></li>
<li><p><a class="reference external" href="https://docs.python.org/3/library/functions.html#int" title="(in Python v3.9)"><code class="xref py py-class docutils literal notranslate"><span class="pre">int</span></code></a> – The residual factor <span class="math notranslate nohighlight">\(n_r\)</span>.</p></li>
</ul>

and renders like

image

mhostetter commented 3 years ago

For completeness, I verified the HTML is the same when using the default theme.

astrojuanlu commented 3 years ago

Thanks @mhostetter. I think there are two things here:

  1. With the default value of napoleon_preprocess_types, which is False, multiple return types aren't hyperlinked. Given that "when using a single return value, the hyperlinks work as expected", this looks like a bug rather than a feature, but I'm not sure.
  2. With napoleon_preprocess_types = True, extra HTML is being generated. I have no idea whether this makes sense or not.
    • With that extra HTML, I think it's correct that the different themes style it differently.
mhostetter commented 3 years ago

FWIW, I agree with your assessment.

ain-soph commented 2 years ago

Have you guys checked the nested type (e.g., dict[float])?

It seems the hyperlinks for dict and float work correctly in Parameters when napoleon_preprocess_types = False, but will not work at True case. This is for both Parameters and Returns.

It seems impossible to get everything hyperlinked very well. I wish everything could be linked as the case of Parameters with napoleon_preprocess_types = False.

From my understanding, when napoleon_preprocess_types = False, parameter types are translated into :type:. According to the docs, the links are created if possible. But multiple return type won't be translated into :rtype: and there's no link for them.
When napoleon_preprocess_types = True, all types won't get translated into :type: nor :rtype:, but using something like :any: to get the link, which makes the nested types not work.

False

image

True

image

Alternatives

From #9119, @fzalkow proposes an alternative, seems promising:

napoleon_custom_sections = [('Returns', 'params_style')]

But currently, it seems not generating any hyperlink (the same output as False image above).
Is this method not generating :param: and :type:, even though using params_style? That might be a question to the author @SolidifiedRay of PR #8658 .

It seems _parse_custom_params_style_section is calling _format_fields, while _parse_parameters_section is calling _format_docutils_params. Is that the difference? Or it might because of multiple=True or False in _consume_fields?

https://github.com/sphinx-doc/sphinx/blob/4c91c038b220d07bbdfe0c1680af42fe897f342c/sphinx/ext/napoleon/docstring.py#L668-L669

https://github.com/sphinx-doc/sphinx/blob/4c91c038b220d07bbdfe0c1680af42fe897f342c/sphinx/ext/napoleon/docstring.py#L729-L736