From aa345ca0475dbe8f4da7f4c7c56832c8d8e6884a Mon Sep 17 00:00:00 2001 From: Simon Heybrock Date: Fri, 11 Feb 2022 13:10:53 +0100 Subject: [PATCH] Add `typehints_use_rtype` option (#218) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Bernát Gábor --- README.md | 5 ++ src/sphinx_autodoc_typehints/__init__.py | 10 ++- .../dummy_module_simple_no_use_rtype.py | 37 +++++++++++ .../roots/test-dummy/simple_no_use_rtype.rst | 7 ++ tests/test_sphinx_autodoc_typehints.py | 65 +++++++++++++++++++ tox.ini | 2 +- 6 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 tests/roots/test-dummy/dummy_module_simple_no_use_rtype.py create mode 100644 tests/roots/test-dummy/simple_no_use_rtype.rst diff --git a/README.md b/README.md index 8780eec5..c2362368 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,11 @@ The following configuration options are accepted: `True`, add stub documentation for undocumented parameters to be able to add type info. - `typehints_document_rtype` (default: `True`): If `False`, never add an `:rtype:` directive. If `True`, add the `:rtype:` directive if no existing `:rtype:` is found. +- `typehints_use_rtype` (default: `True`): + Controls behavior when `typehints_document_rtype` is set to `True`. + If `True`, document return type in the `:rtype:` directive. + If `False`, document return type as part of the `:return:` directive, if present, otherwise fall back to using `:rtype:`. + Use in conjunction with [napoleon_use_rtype](https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html#confval-napoleon_use_rtype) to avoid generation of duplicate or redundant return type information. - `typehints_defaults` (default: `None`): If `None`, defaults are not added. Otherwise adds a default annotation: - `'comma'` adds it after the type, changing Sphinx’ default look to “**param** (_int_, default: `1`) -- text”. diff --git a/src/sphinx_autodoc_typehints/__init__.py b/src/sphinx_autodoc_typehints/__init__.py index b1f55f72..bfa95909 100644 --- a/src/sphinx_autodoc_typehints/__init__.py +++ b/src/sphinx_autodoc_typehints/__init__.py @@ -556,13 +556,20 @@ def _inject_types_to_docstring( insert_index = None break elif line.startswith(":return:") or line.startswith(":returns:"): + if " -- " in line and not app.config.typehints_use_rtype: + insert_index = None + break insert_index = at if insert_index is not None and app.config.typehints_document_rtype: if insert_index == len(lines): # ensure that :rtype: doesn't get joined with a paragraph of text lines.append("") insert_index += 1 - lines.insert(insert_index, f":rtype: {formatted_annotation}") + if app.config.typehints_use_rtype or insert_index == len(lines): + lines.insert(insert_index, f":rtype: {formatted_annotation}") + else: + line = lines[insert_index] + lines[insert_index] = f":return: {formatted_annotation} --{line[line.find(' '):]}" def validate_config(app: Sphinx, env: BuildEnvironment, docnames: list[str]) -> None: # noqa: U100 @@ -579,6 +586,7 @@ def setup(app: Sphinx) -> dict[str, bool]: app.add_config_value("always_document_param_types", False, "html") app.add_config_value("typehints_fully_qualified", False, "env") app.add_config_value("typehints_document_rtype", True, "env") + app.add_config_value("typehints_use_rtype", True, "env") app.add_config_value("typehints_defaults", None, "env") app.add_config_value("simplify_optional_unions", True, "env") app.add_config_value("typehints_formatter", None, "env") diff --git a/tests/roots/test-dummy/dummy_module_simple_no_use_rtype.py b/tests/roots/test-dummy/dummy_module_simple_no_use_rtype.py new file mode 100644 index 00000000..3f10b4dc --- /dev/null +++ b/tests/roots/test-dummy/dummy_module_simple_no_use_rtype.py @@ -0,0 +1,37 @@ +def function_no_returns(x: bool, y: int = 1) -> str: # noqa: U100 + """ + Function docstring. + + :param x: foo + :param y: bar + """ + + +def function_returns_with_type(x: bool, y: int = 1) -> str: # noqa: U100 + """ + Function docstring. + + :param x: foo + :param y: bar + :returns: *CustomType* -- A string + """ + + +def function_returns_with_compound_type(x: bool, y: int = 1) -> str: # noqa: U100 + """ + Function docstring. + + :param x: foo + :param y: bar + :returns: Union[str, int] -- A string or int + """ + + +def function_returns_without_type(x: bool, y: int = 1) -> str: # noqa: U100 + """ + Function docstring. + + :param x: foo + :param y: bar + :returns: A string + """ diff --git a/tests/roots/test-dummy/simple_no_use_rtype.rst b/tests/roots/test-dummy/simple_no_use_rtype.rst new file mode 100644 index 00000000..00b2d61e --- /dev/null +++ b/tests/roots/test-dummy/simple_no_use_rtype.rst @@ -0,0 +1,7 @@ +Simple Module +============= + +.. autofunction:: dummy_module_simple_no_use_rtype.function_no_returns +.. autofunction:: dummy_module_simple_no_use_rtype.function_returns_with_type +.. autofunction:: dummy_module_simple_no_use_rtype.function_returns_with_compound_type +.. autofunction:: dummy_module_simple_no_use_rtype.function_returns_without_type diff --git a/tests/test_sphinx_autodoc_typehints.py b/tests/test_sphinx_autodoc_typehints.py index becefd89..f38a9ff9 100644 --- a/tests/test_sphinx_autodoc_typehints.py +++ b/tests/test_sphinx_autodoc_typehints.py @@ -916,3 +916,68 @@ def test_no_source_code_type_guard() -> None: from csv import Error _resolve_type_guarded_imports(Error) + + +@pytest.mark.sphinx("text", testroot="dummy") +@patch("sphinx.writers.text.MAXWIDTH", 2000) +def test_sphinx_output_formatter_no_use_rtype(app: SphinxTestApp, status: StringIO) -> None: + set_python_path() + app.config.master_doc = "simple_no_use_rtype" # type: ignore # create flag + app.config.typehints_use_rtype = False # type: ignore + app.build() + assert "build succeeded" in status.getvalue() + text_path = pathlib.Path(app.srcdir) / "_build" / "text" / "simple_no_use_rtype.txt" + text_contents = text_path.read_text().replace("–", "--") + expected_contents = """\ + Simple Module + ************* + + dummy_module_simple_no_use_rtype.function_no_returns(x, y=1) + + Function docstring. + + Parameters: + * **x** ("bool") -- foo + + * **y** ("int") -- bar + + Return type: + "str" + + dummy_module_simple_no_use_rtype.function_returns_with_type(x, y=1) + + Function docstring. + + Parameters: + * **x** ("bool") -- foo + + * **y** ("int") -- bar + + Returns: + *CustomType* -- A string + + dummy_module_simple_no_use_rtype.function_returns_with_compound_type(x, y=1) + + Function docstring. + + Parameters: + * **x** ("bool") -- foo + + * **y** ("int") -- bar + + Returns: + Union[str, int] -- A string or int + + dummy_module_simple_no_use_rtype.function_returns_without_type(x, y=1) + + Function docstring. + + Parameters: + * **x** ("bool") -- foo + + * **y** ("int") -- bar + + Returns: + "str" -- A string + """ + assert text_contents == dedent(expected_contents) diff --git a/tox.ini b/tox.ini index dff0c9ce..fde4e7da 100644 --- a/tox.ini +++ b/tox.ini @@ -45,7 +45,7 @@ description = run type check on code base setenv = {tty:MYPY_FORCE_COLOR = 1} deps = - mypy==0.930 + mypy==0.931 types-docutils commands = mypy --python-version 3.10 src