Skip to content

Commit

Permalink
Add autodoc_typehint_undoc option
Browse files Browse the repository at this point in the history
Previously, if autodoc_typehints="description", a :type: field would be
added for every parameter and return type appearing in the annotation,
including **kwargs and underscore-prefixed parameters that are meant to
be private, as well as None return types.

This commit introduces a new option, "autodoc_typehint_undoc".  By
default this option is True, requesting the old behavior. By setting
this option to False, :type: and :rtype: fields will only be added for
annotated parameters or return types if there is already a corresponding
:param: or :return: field, to put users in control over whether a given
parameter is documented or not.
  • Loading branch information
godlygeek committed Mar 27, 2021
1 parent d0785e5 commit b9970b9
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 2 deletions.
13 changes: 13 additions & 0 deletions doc/usage/extensions/autodoc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,19 @@ There are also config values that you can set:

New option ``'description'`` is added.

.. confval:: autodoc_typehints_description_target

This value controls whether the types of undocumented parameters and return
values are documented when ``autodoc_typehints`` is set to ``description``.

The default value is ``"all"``, meaning that types are documented for all
parameters and return values, whether they are documented or not.

When set to ``"documented"``, types will only be documented for a parameter
or a return value that is already documented by the docstring.

.. versionadded:: 4.0

.. confval:: autodoc_type_aliases

A dictionary for users defined `type aliases`__ that maps a type name to the
Expand Down
2 changes: 2 additions & 0 deletions sphinx/ext/autodoc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2643,6 +2643,8 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('autodoc_mock_imports', [], True)
app.add_config_value('autodoc_typehints', "signature", True,
ENUM("signature", "description", "none"))
app.add_config_value('autodoc_typehints_description_target', 'all', True,
ENUM('all', 'documented'))
app.add_config_value('autodoc_type_aliases', {}, True)
app.add_config_value('autodoc_warningiserror', True, True)
app.add_config_value('autodoc_inherit_docstrings', True, True)
Expand Down
53 changes: 51 additions & 2 deletions sphinx/ext/autodoc/typehints.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import re
from collections import OrderedDict
from typing import Any, Dict, Iterable, cast
from typing import Any, Dict, Iterable, Set, cast

from docutils import nodes
from docutils.nodes import Element
Expand Down Expand Up @@ -63,7 +63,10 @@ def merge_typehints(app: Sphinx, domain: str, objtype: str, contentnode: Element
field_lists.append(field_list)

for field_list in field_lists:
modify_field_list(field_list, annotations[fullname])
if app.config.autodoc_typehints_description_target == "all":
modify_field_list(field_list, annotations[fullname])
else:
augment_descriptions_with_types(field_list, annotations[fullname])


def insert_field_list(node: Element) -> nodes.field_list:
Expand Down Expand Up @@ -126,6 +129,52 @@ def modify_field_list(node: nodes.field_list, annotations: Dict[str, str]) -> No
node += field


def augment_descriptions_with_types(
node: nodes.field_list,
annotations: Dict[str, str],
) -> None:
fields = cast(Iterable[nodes.field], node)
has_description = set() # type: Set[str]
has_type = set() # type: Set[str]
for field in fields:
field_name = field[0].astext()
parts = re.split(' +', field_name)
if parts[0] == 'param':
if len(parts) == 2:
# :param xxx:
has_description.add(parts[1])
elif len(parts) > 2:
# :param xxx yyy:
name = ' '.join(parts[2:])
has_description.add(name)
has_type.add(name)
elif parts[0] == 'type':
name = ' '.join(parts[1:])
has_type.add(name)
elif parts[0] == 'return':
has_description.add('return')
elif parts[0] == 'rtype':
has_type.add('return')

# Add 'type' for parameters with a description but no declared type.
for name in annotations:
if name == 'return':
continue
if name in has_description and name not in has_type:
field = nodes.field()
field += nodes.field_name('', 'type ' + name)
field += nodes.field_body('', nodes.paragraph('', annotations[name]))
node += field

# Add 'rtype' if 'return' is present and 'rtype' isn't.
if 'return' in annotations:
if 'return' in has_description and 'return' not in has_type:
field = nodes.field()
field += nodes.field_name('', 'rtype')
field += nodes.field_body('', nodes.paragraph('', annotations['return']))
node += field


def setup(app: Sphinx) -> Dict[str, Any]:
app.connect('autodoc-process-signature', record_typehints)
app.connect('object-description-transform', merge_typehints)
Expand Down

0 comments on commit b9970b9

Please sign in to comment.