How napoleon and autodoc interact documenting members
Asked Answered
D

1

3

I noticed a change in behavior in how Sphinx renders class descriptions. Given this code

# my example happens to be a dataclass, but the behavior for 
# regular classes is the same
@dataclass
class TestClass:
    """This is a test class for dataclasses.

    This is the body of the docstring description.
    """
    var_int: int
    var_str: str

plus some generic Sphinx settings, I used to get this about two years ago

Documentation shows just the docstring

And am now getting this

Documentation shows class variables as if they were in the docstring

Is there a way to tell Sphinx not to add the class variables to the bottom of the class definition? It's particularly annoying that it assumes their values to be None, just because they don't have defaults.


This issue came up during discussions on this post, which also contains a bit more context in comments regarding Sphinx configuration etc.

Darondarooge answered 29/10, 2020 at 10:1 Comment(4)
I cannot reproduce this with Sphinx 3.2.1 and Python 3.8.5.Kindergarten
When you say: "Is there a way to tell Sphinx not to add the class variables to the bottom of the class definition?" do you mean "not add them" at all, or include them yes but just not at the bottom? Also, what would be the desired behavior regarding the None value? Omit it together with the equal sign?Urumchi
@Urumchi I mean not to add them at all, and the None behavior is just weird to me. They don't have the value None, if anything they'd be undefined. But, strictly speaking, it is a different issue.Darondarooge
@Kindergarten Got one working here: gist. I tried what you suggested @Urumchi regarding :undoc-members:, and it solves my issue. To be honest, I never bothered to find out what the directives in autodoc-generated rst mean. Maybe the default value for autodoc_default_options at undoc-members used to be false and is now true, but I'm too lazy to hunt that info down. Feel free to post a conf.py that changes it back to false as an answer I can accept =)Darondarooge
U
5

Members of an object are included by an autodoc directive depending if:

  • the :members: option is used (for members with docstrings).
  • the :undoc-members: option is used (for members without docstrings).

For example:

dc module
=========

.. autoclass:: dc.Foo

In the above .rst file the autodoc directive does not have explicit options set, what Sphinx will do is implicitly apply the option flags taken from the autodoc_default_flags in conf.py.

Setting the following in conf.py would cause all members of an object (with or without docstrings) to be included by Sphinx in all directives that do not explicitly specify options.

# autodoc settings
autodoc_default_options = {
    'members':          True,
    'undoc-members':    True,
}

The result:

enter image description here

However, this raises a question: What do autodoc and sphinx-napoleon extensions do if members are explicitly specified in the Attributes docstring section but also included by the autodoc extension?

Docstrings

Napoleon interprets every docstring that autodoc can find (...) Inside each docstring, specially formatted Sections are parsed and converted to reStructuredText.

For example, using the following docstring together with the options specified above in autodoc_default_options.

import dataclasses

@dataclasses.dataclass
class Foo:
    """Docstring for Foo

    Attributes:
        var_a (str): An integer.
        var_b (int): A string.
    """
    var_a: str
    var_b: int

In this case members will be declared twice, once by each extension, with the corresponding reST declaration being generated as a duplicate. Having a duplicate reST declaration will lead to the usual warning:

C:\dc.py:docstring of dc.Foo.var_a:1: WARNING: duplicate object description of dc.Foo.var_a, other instance in dc, use :noindex: for one of them

C:\dc.py:docstring of dc.Foo.var_b:1: WARNING: duplicate object description of dc.Foo.var_b, other instance in dc, use :noindex: for one of them

Here one difference can be noted: sphinx-napoleon will declare the member in its own docstring section whereas autodoc will render it normally as other members. The visual difference will depend on theme, for example using classic theme:

enter image description here

Finally, if you want to document members using sphinx-napoleon docstring sections and avoid a duplicate reST declaration from autodoc while keeping autodoc_default_options as shown, you can explicitly use :exclude-members: option in that specific directive, for example:

dc module
=========

.. autoclass:: dc.Foo
    :exclude-members: var_a, var_b

Would document the members using only sphinx-napoleon generated reST directives:

enter image description here

Urumchi answered 30/10, 2020 at 2:29 Comment(2)
Can I somehow configure the exclude-members in conf.py, or do I need to update the .rst every time I run sphinx-apidoc?Darondarooge
@Darondarooge I wrote a somewhat extensive post on the difficulties of setting the default autodoc options when using sphinx-apidoc. The catch is sphinx-apidoc takes the default options from an environment variable, not as a command line argument...As for the rest, :exclude-members: can be set in autodoc_default_flags. The right choice is setting as default the most frequently used option, whenever you want an exception to the rule set that option directly in the .rst directive.Urumchi

© 2022 - 2024 — McMap. All rights reserved.