Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow starred args and kwargs in docstring params #254

Merged
merged 1 commit into from Sep 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,7 @@

- Use hatchling instead of setuptools
- Add support for typing.ParamSpec
- Allow star prefixes for parameter names in docstring

## 1.19.2

Expand Down
49 changes: 47 additions & 2 deletions src/sphinx_autodoc_typehints/__init__.py
Expand Up @@ -500,6 +500,49 @@ def process_docstring(
delattr(app.config, "_annotation_globals")


def _get_sphinx_line_keyword_and_argument(line: str) -> tuple[str, str | None] | None:
"""
Extract a keyword, and its optional argument out of a sphinx field option line.

For example
>>> _get_sphinx_line_keyword_and_argument(":param parameter:")
("param", "parameter")
>>> _get_sphinx_line_keyword_and_argument(":return:")
("return", None)
>>> _get_sphinx_line_keyword_and_argument("some invalid line")
None
"""

param_line_without_description = line.split(":", maxsplit=2) # noqa: SC200
if len(param_line_without_description) != 3:
return None

split_directive_and_name = param_line_without_description[1].split(maxsplit=1) # noqa: SC200
if len(split_directive_and_name) != 2:
return (split_directive_and_name[0], None)

return tuple(split_directive_and_name) # type: ignore


def _line_is_param_line_for_arg(line: str, arg_name: str) -> bool:
"""Return True if `line` is a valid parameter line for `arg_name`, false otherwise."""
keyword_and_name = _get_sphinx_line_keyword_and_argument(line)
if keyword_and_name is None:
return False

keyword, doc_name = keyword_and_name
if doc_name is None:
return False

if keyword not in {"param", "parameter", "arg", "argument"}:
return False

for prefix in ("", r"\*", r"\**", r"\*\*"):
if doc_name == prefix + arg_name:
return True
return False


def _inject_types_to_docstring(
type_hints: dict[str, Any],
signature: inspect.Signature | None,
Expand All @@ -521,10 +564,12 @@ def _inject_types_to_docstring(

formatted_annotation = format_annotation(annotation, app.config)

search_for = {f":{field} {arg_name}:" for field in ("param", "parameter", "arg", "argument")}
insert_index = None
for at, line in enumerate(lines):
if any(line.startswith(search_string) for search_string in search_for):
if _line_is_param_line_for_arg(line, arg_name):
# Get the arg_name from the doc to match up for type in case it has a star prefix.
# Line is in the correct format so this is guaranteed to return tuple[str, str].
_, arg_name = _get_sphinx_line_keyword_and_argument(line) # type: ignore[assignment, misc]
insert_index = at
break

Expand Down
9 changes: 9 additions & 0 deletions tests/roots/test-dummy/dummy_module.py
Expand Up @@ -126,6 +126,15 @@ def function(x: bool, y: int, z_: typing.Optional[str] = None) -> str: # noqa:
"""


def function_with_starred_documentation_param_names(*args: int, **kwargs: str): # noqa: U100
r"""
Function docstring.

:param \*args: foo
:param \**kwargs: bar
"""


def function_with_escaped_default(x: str = "\b"): # noqa: U100
"""
Function docstring.
Expand Down
2 changes: 2 additions & 0 deletions tests/roots/test-dummy/index.rst
Expand Up @@ -19,6 +19,8 @@ Dummy Module

.. autofunction:: dummy_module.function_with_typehint_comment

.. autofunction:: dummy_module.function_with_starred_documentation_param_names

.. autoclass:: dummy_module.ClassWithTypehints
:members:

Expand Down
9 changes: 9 additions & 0 deletions tests/test_sphinx_autodoc_typehints.py
Expand Up @@ -619,6 +619,15 @@ class InnerClass
Return type:
"None"

dummy_module.function_with_starred_documentation_param_names(*args, **kwargs)

Function docstring.

Parameters:
* ***args** ("int") -- foo

* ****kwargs** ("str") -- bar

class dummy_module.ClassWithTypehints(x)

Class docstring.
Expand Down