pythondocumentationpython-sphinxautodocslots

How to prevent Sphinx from displaying duplicate attributes in class documentation when using slots?


I'm using Sphinx to generate documentation for a Python project, and I have noticed that some attributes are being displayed twice in the class documentation.

This is my current set up simplified:

job_shop_lib/example_class.py:

"""An example class to demonstrate the documentation issue."""


class ExampleClass:
    """An example class to demonstrate the documentation issue.

    Attributes:
        attribute1:
            Description of attribute1 from the class docstring.
        attribute2:
            Description of attribute2 from the class docstring.
    """

    __slots__ = ("attribute1", "attribute2")

    def __init__(self):
        self.attribute1 = None
        self.attribute2 = None

And this is my conf.py file:

import os
import sys

sys.path.insert(0, os.path.abspath(".."))

# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

project = "JobShopLib"
copyright = "MIT License"
author = "Pablo Ariño"

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

extensions = [
    "sphinx.ext.autodoc",
    "sphinx.ext.napoleon",
    "sphinx.ext.viewcode",
    "sphinx.ext.autosummary",
    "nbsphinx",
    "nbsphinx_link",
    "sphinx.ext.doctest",
    "myst_parser",
    "sphinx.ext.autodoc.typehints",
]

templates_path = ["_templates"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "_*.py"]
add_module_names = False
python_use_unqualified_type_names = True
autosummary_generate = True
autosummary_generate_overwrite = True

autodoc_mock_imports = ["ortools"]
autodoc_default_options = {
    "members": True,
    "undoc-members": True,
    "show-inheritance": True,
    "imported-members": False,
    "special-members": "__call__",
    # I tried with no excluded members too
    "exclude-members": "__weakref__, __dict__, __module__, __slots__",
}
add_function_parentheses = True
modindex_common_prefix = ["job_shop_lib."]

autodoc_typehints = "description"
autodoc_typehints_format = "short"

napoleon_include_init_with_doc = True
napoleon_preprocess_types = True

# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output

html_theme = "furo"  # pylint: disable=invalid-name
html_static_path = ["_static"]
templates_path = ["_templates"]  # I am not using templates

html_logo = "images/logo_no_bg_resized_fixed.png"

And my .rst files are:

.. JobShopLib documentation master file, created by
   sphinx-quickstart on Sun Jul  7 16:51:14 2024.
   You can adapt this file completely to your liking, but it should at least
   contain the root `toctree` directive.

Welcome to JobShopLib's documentation!
======================================

**Version:** 1.0.0-alpha.1

A brief description.

Contents
--------

.. toctree::
    :maxdepth: 3

    api

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
API
===

.. autosummary::
   :toctree: api
   :recursive:

   job_shop_lib

The generated documentation shows the attributes duplicated:

Image showing attributes 1 and 2 showing twice, first time with the description, and the second one without it.

If I remove the attributes from the __slots__ definition (i.e. removing the line __slots__ = ("attribute1", "attribute2")) they appear correctly: with the description and without duplication.

Ideally, I want to solve this problem while keeping the current code unchanged.


Solution

  • As a workaround, documenting each attribute inside the __slots__ definition and removing the class docstring "Attributes" section solved the issue:

    class ExampleClass:
        """An example class to demonstrate the documentation issue."""
    
        __slots__ = {
            "attribute1": "Description of attribute1 from the class docstring.",
            "attribute2": "Description of attribute2 from the class docstring.",
        }
    
        def __init__(self):
            self.attribute1 = None
            self.attribute2 = None