Skip to content

Commit

Permalink
Fix sphinx-doc#9648: autodoc: *args and **kwargs entries are duplicated
Browse files Browse the repository at this point in the history
In basic usage of autodoc (docstring), `args` and `kwargs` arguments
are marked up without stars.  But numpydoc style recommends to mark
them up with stars.

This adds support for starred arguments in docstrings to
`autodoc_typehints` feature.
  • Loading branch information
tk0miya committed May 15, 2022
1 parent 92fcac3 commit 294617a
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 3 deletions.
3 changes: 3 additions & 0 deletions CHANGES
Expand Up @@ -16,6 +16,9 @@ Features added
Bugs fixed
----------

* #9648: autodoc: ``*args`` and ``**kwargs`` entries are duplicated when
``autodoc_typehints="description"``

Testing
--------

Expand Down
20 changes: 17 additions & 3 deletions sphinx/ext/autodoc/typehints.py
Expand Up @@ -111,7 +111,15 @@ def modify_field_list(node: nodes.field_list, annotations: Dict[str, str]) -> No
if name == 'return':
continue

arg = arguments.get(name, {})
if '*' + name in arguments:
name = '*' + name
arguments.get(name)
elif '**' + name in arguments:
name = '**' + name
arguments.get(name)
else:
arg = arguments.get(name, {})

if not arg.get('type'):
field = nodes.field()
field += nodes.field_name('', 'type ' + name)
Expand Down Expand Up @@ -159,13 +167,19 @@ def augment_descriptions_with_types(
has_type.add('return')

# Add 'type' for parameters with a description but no declared type.
for name in annotations:
for name, annotation in annotations.items():
if name in ('return', 'returns'):
continue

if '*' + name in has_description:
name = '*' + name
elif '**' + name in has_description:
name = '**' + name

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]))
field += nodes.field_body('', nodes.paragraph('', annotation))
node += field

# Add 'rtype' if 'return' is present and 'rtype' isn't.
Expand Down
5 changes: 5 additions & 0 deletions tests/roots/test-ext-napoleon/conf.py
@@ -0,0 +1,5 @@
import os
import sys

sys.path.insert(0, os.path.abspath('.'))
extensions = ['sphinx.ext.napoleon']
6 changes: 6 additions & 0 deletions tests/roots/test-ext-napoleon/index.rst
@@ -0,0 +1,6 @@
test-ext-napoleon
=================

.. toctree::

typehints
Empty file.
11 changes: 11 additions & 0 deletions tests/roots/test-ext-napoleon/target/typehints.py
@@ -0,0 +1,11 @@
def hello(x: int, *args: int, **kwargs: int) -> None:
"""
Parameters
----------
x
X
*args
Additional arguments.
**kwargs
Extra arguments.
"""
5 changes: 5 additions & 0 deletions tests/roots/test-ext-napoleon/typehints.rst
@@ -0,0 +1,5 @@
typehints
=========

.. automodule:: target.typehints
:members:
45 changes: 45 additions & 0 deletions tests/test_ext_napoleon_docstring.py
Expand Up @@ -2593,3 +2593,48 @@ def test_pep526_annotations(self):
"""
print(actual)
assert expected == actual


@pytest.mark.sphinx('text', testroot='ext-napoleon',
confoverrides={'autodoc_typehints': 'description',
'autodoc_typehints_description_target': 'all'})
def test_napoleon_and_autodoc_typehints_description_all(app, status, warning):
app.build()
content = (app.outdir / 'typehints.txt').read_text(encoding='utf-8')
assert content == (
'typehints\n'
'*********\n'
'\n'
'target.typehints.hello(x, *args, **kwargs)\n'
'\n'
' Parameters:\n'
' * **x** (*int*) -- X\n'
'\n'
' * ***args** (*int*) -- Additional arguments.\n'
'\n'
' * ****kwargs** (*int*) -- Extra arguments.\n'
'\n'
' Return type:\n'
' None\n'
)


@pytest.mark.sphinx('text', testroot='ext-napoleon',
confoverrides={'autodoc_typehints': 'description',
'autodoc_typehints_description_target': 'documented_params'})
def test_napoleon_and_autodoc_typehints_description_documented_params(app, status, warning):
app.build()
content = (app.outdir / 'typehints.txt').read_text(encoding='utf-8')
assert content == (
'typehints\n'
'*********\n'
'\n'
'target.typehints.hello(x, *args, **kwargs)\n'
'\n'
' Parameters:\n'
' * **x** (*int*) -- X\n'
'\n'
' * ***args** (*int*) -- Additional arguments.\n'
'\n'
' * ****kwargs** (*int*) -- Extra arguments.\n'
)

0 comments on commit 294617a

Please sign in to comment.