4

Sphinx HTML documentation for generic Python classes (i.e. parametric types) does not show the initialization arguments.

Desired output: class GenericClass(value: T)

Actual output: class GenericClass(*args, **kwargs)

Python source code (class_test.py)

from typing import Generic, TypeVar

#:TypeVar("T"): Type variable serving as a parameter for generic types.
T = TypeVar("T")


class GenericClass(Generic[T]):
    """A class that accepts a generic type parameter.

    Attributes:
        value: A value of generic type ``T``.
    """
    def __init__(self, value: T):
        self.value: T = value


class RegularClass:
    """A regular class that is explicitly typed.

    Attributes:
        value: A string value.
    """
    def __init__(self, value: str):
        self.value: str = value

Sphinx Output

enter image description here

conf.py

import os
import sys
sys.path.insert(0, os.path.abspath('../..'))

# -- Project information -----------------------------------------------------
project = 'sphinx-autodoc-generic-class-bug'
copyright = '2020, Christopher Peisert'
author = 'Christopher Peisert'

# The short X.Y.Z version.
version = "0.0.1"

# -- General configuration ---------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.napoleon',
]

# autodoc options
autodoc_typehints = 'signature'

autodoc_default_options = {
    'autoclass_content': 'class',
    'member-order': 'bysource',
    'members': True,
    'show-inheritance': True,
}

# Napoleon settings
napoleon_google_docstring = True
napoleon_use_admonition_for_examples = False
napoleon_use_admonition_for_notes = False
napoleon_use_admonition_for_references = False
napoleon_use_param = False  # Show the Parameter Types in separate line.
napoleon_use_rtype = True   # Show the Return Type in separate line.


# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
source_suffix = ['.rst']

# The master toctree document.
master_doc = 'index'


# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = 'classic'


# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

class_test.rst

=========================
Generic vs. Regular Class
=========================

.. automodule:: src.class_test
    :exclude-members: T

    .. autodata:: T
        :annotation: = TypeVar("T")
Christopher Peisert
  • 21,862
  • 3
  • 86
  • 117

2 Answers2

1

The exact Python code in the question works for me using the sphinx.ext.autodoc extension together with the configuration autodoc_typehints = 'signature'.

enter image description here

conf.py

extensions = [
    'sphinx.ext.autodoc',
]

autodoc_typehints = 'signature'

autodoc_default_options = {
    'members':           True,
    'undoc-members':     True,
    'member-order':      'bysource',
}

type_var_test.rst

=============
type_var_test
=============

.. automodule:: type_var_test
    :show-inheritance:

It also works using sphinx.ext.napoleon with the bellow configurations. Notice typing.TypeVar is a class so you can cross-reference it as such, altough in the .rst it is best declared as a module level variable using .. autodata::. This leaves several choices of style, should you go for Google style type annotations in docstrings, it would look like this:

enter image description here

conf.py

extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.napoleon',
]

# Napoleon settings
napoleon_google_docstring = True
napoleon_numpy_docstring = True
napoleon_include_init_with_doc = False
napoleon_include_private_with_doc = False
napoleon_include_special_with_doc = False
napoleon_use_admonition_for_examples = False
napoleon_use_admonition_for_notes = False
napoleon_use_admonition_for_references = False
napoleon_use_ivar = False
napoleon_use_param = False
napoleon_use_rtype = False

type_var_test.py

from typing import Generic, TypeVar

#:TypeVar("T"): Type variables exist primarily for the benefit of static type checkers.
T = TypeVar("T")



class GenericClass(Generic[T]):
    """A class that accepts a generic type parameter.

    Attributes:
        value (:class:`T`): Google style with types in docstrings.
    """
    def __init__(self, value: T):
        self.value: T = value

type_var_test.rst

=============
type_var_test
=============

.. automodule:: type_var_test
    :show-inheritance:
    :exclude-members: T

    .. autodata:: T
        :annotation: = TypeVar("T")
bad_coder
  • 11,289
  • 20
  • 44
  • 72
  • Using Sphinx version 3.2.1 with **autodoc** and **napoleon**, I tried the exact settings shown above, and the class signature still appears as `class GenericClass(*args, **kwargs)`. – Christopher Peisert Sep 19 '20 at 13:23
  • 1
    @ChristopherPeisert I just invalidated caches and I'm getting the same result as you. So it's a bug and something that broke recently. I tried running my code and yours carefully comparing both but it doesn't work anymore. It's noteworthy the caches survived system reboots, venv upgrades, persisted across venvs, and running on terminal Python and Sphinx version numbers were shown up-to-date...Honest mistake, as the screenshots show. (It was the possible contribution towards identifying a bug.) – bad_coder Sep 20 '20 at 00:55
1

Sphinx issue #8219 autodoc doesn't handle TypeVars or document user-defined generic classes accurately was officially fixed in release 3.4.0.

From sphinx/CHANGES:

Release 3.4.0

...

Features added

  • #8219: autodoc: Parameters for generic class are not shown when super class is a generic class and show-inheritance option is given (in Python 3.7 or above)
Christopher Peisert
  • 21,862
  • 3
  • 86
  • 117