From 41cf6e49d9daddaa373606e58be38741ec9d5df4 Mon Sep 17 00:00:00 2001 From: Numerlor Date: Thu, 22 Sep 2022 18:51:24 +0200 Subject: [PATCH] allow starred args and kwargs in docstring params (#254) --- CHANGELOG.md | 1 + src/sphinx_autodoc_typehints/__init__.py | 49 +++++++++++++++++++++++- tests/roots/test-dummy/dummy_module.py | 9 +++++ tests/roots/test-dummy/index.rst | 2 + tests/test_sphinx_autodoc_typehints.py | 9 +++++ 5 files changed, 68 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f6ea3aa..2188a3b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/sphinx_autodoc_typehints/__init__.py b/src/sphinx_autodoc_typehints/__init__.py index 32e81240..cbfa5267 100644 --- a/src/sphinx_autodoc_typehints/__init__.py +++ b/src/sphinx_autodoc_typehints/__init__.py @@ -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, @@ -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 diff --git a/tests/roots/test-dummy/dummy_module.py b/tests/roots/test-dummy/dummy_module.py index 0f6dc3c4..d94c7dda 100644 --- a/tests/roots/test-dummy/dummy_module.py +++ b/tests/roots/test-dummy/dummy_module.py @@ -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. diff --git a/tests/roots/test-dummy/index.rst b/tests/roots/test-dummy/index.rst index b8229774..fac01863 100644 --- a/tests/roots/test-dummy/index.rst +++ b/tests/roots/test-dummy/index.rst @@ -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: diff --git a/tests/test_sphinx_autodoc_typehints.py b/tests/test_sphinx_autodoc_typehints.py index 48013eea..2b6fde03 100644 --- a/tests/test_sphinx_autodoc_typehints.py +++ b/tests/test_sphinx_autodoc_typehints.py @@ -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.