From d9c4a06b654f4415d61e5ce23f46c8356a6ab92a Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 23 Jan 2023 13:44:05 -0800 Subject: [PATCH] Patch docutils rst parser to add line numbers to more nodes (#316) * Patch docutils rst parser to add line numbers to more nodes This resolves issue 312. The missing line numbers make it impossible for us to correctly calculate where to place the rtype. See upstream issue: https://sourceforge.net/p/docutils/bugs/465/ I submitted a patch to docutils to fix these things but it is currently waiting to get through a spam filter. The docutils test suite has no coverage checking whether this field is filled correctly. * Remove noqa for spelling error --- src/sphinx_autodoc_typehints/__init__.py | 2 +- src/sphinx_autodoc_typehints/patches.py | 34 +++++ tests/test_integration.py | 168 +++++++++++++++++++++++ whitelist.txt | 2 + 4 files changed, 205 insertions(+), 1 deletion(-) diff --git a/src/sphinx_autodoc_typehints/__init__.py b/src/sphinx_autodoc_typehints/__init__.py index 94cd45e8..3176651a 100644 --- a/src/sphinx_autodoc_typehints/__init__.py +++ b/src/sphinx_autodoc_typehints/__init__.py @@ -678,7 +678,7 @@ def node_line_no(node: Node) -> int | None: def tag_name(node: Node) -> str: - return node.tagname # type:ignore[attr-defined,no-any-return] # noqa: SC200 + return node.tagname # type:ignore[attr-defined,no-any-return] def get_insert_index(app: Sphinx, lines: list[str]) -> InsertIndexInfo | None: diff --git a/src/sphinx_autodoc_typehints/patches.py b/src/sphinx_autodoc_typehints/patches.py index b91f839e..4cc6e96c 100644 --- a/src/sphinx_autodoc_typehints/patches.py +++ b/src/sphinx_autodoc_typehints/patches.py @@ -3,6 +3,8 @@ from functools import lru_cache from typing import Any +from docutils.parsers.rst.directives.admonitions import BaseAdmonition +from docutils.parsers.rst.states import Text from sphinx.application import Sphinx from sphinx.ext.autodoc import Options from sphinx.ext.napoleon.docstring import GoogleDocstring @@ -86,11 +88,43 @@ def patch_google_docstring_lookup_annotation() -> None: GoogleDocstring._lookup_annotation = patched_lookup_annotation # type: ignore[assignment] +orig_base_admonition_run = BaseAdmonition.run + + +def patched_base_admonition_run(self: BaseAdmonition) -> Any: + result = orig_base_admonition_run(self) + result[0].line = self.lineno + return result + + +orig_text_indent = Text.indent + + +def patched_text_indent(self: Text, *args: Any) -> Any: + _, line = self.state_machine.get_source_and_line() + result = orig_text_indent(self, *args) + node = self.parent[-1] + if node.tagname == "system_message": + node = self.parent[-2] + node.line = line + return result + + +def patch_line_numbers() -> None: + """Make the rst parser put line numbers on more nodes. + + When the line numbers are missing, we have a hard time placing the :rtype:. + """ + Text.indent = patched_text_indent + BaseAdmonition.run = patched_base_admonition_run + + def install_patches(app: Sphinx) -> None: fix_autodoc_typehints_for_overloaded_methods() patch_attribute_handling(app) patch_google_docstring_lookup_annotation() fix_napoleon_numpy_docstring_return_type(app) + patch_line_numbers() ___all__ = ["install_patches"] diff --git a/tests/test_integration.py b/tests/test_integration.py index 04130224..ace46091 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -999,6 +999,174 @@ def google_docstrings(arg1: CodeType, arg2: ModuleType) -> CodeType: # noqa: U1 """ +@expected( + """ + mod.docstring_with_multiline_note_after_params(param) + + Do something. + + Parameters: + **param** ("int") -- A parameter. + + Return type: + "None" + + Note: + + Some notes. More notes + + """ +) +def docstring_with_multiline_note_after_params(param: int) -> None: # noqa: U100 + """Do something. + + Args: + param: A parameter. + + Note: + + Some notes. + More notes + """ + + +@expected( + """ + mod.docstring_with_bullet_list_after_params(param) + + Do something. + + Parameters: + **param** ("int") -- A parameter. + + Return type: + "None" + + * A: B + + * C: D + + """ +) +def docstring_with_bullet_list_after_params(param: int) -> None: # noqa: U100 + """Do something. + + Args: + param: A parameter. + + * A: B + * C: D + """ + + +@expected( + """ + mod.docstring_with_definition_list_after_params(param) + + Do something. + + Parameters: + **param** ("int") -- A parameter. + + Return type: + "None" + + Term + A description + + maybe multiple lines + + Next Term + Somethign about it + + """ +) +def docstring_with_definition_list_after_params(param: int) -> None: # noqa: U100 + """Do something. + + Args: + param: A parameter. + + Term + A description + + maybe multiple lines + + Next Term + Somethign about it + """ + + +@expected( + """ + mod.docstring_with_enum_list_after_params(param) + + Do something. + + Parameters: + **param** ("int") -- A parameter. + + Return type: + "None" + + 1. A: B + + 2. C: D + + """ +) +def docstring_with_enum_list_after_params(param: int) -> None: # noqa: U100 + """Do something. + + Args: + param: A parameter. + + 1. A: B + 2. C: D + """ + + +@warns("Definition list ends without a blank line") +@expected( + """ + mod.docstring_with_definition_list_after_params_no_blank_line(param) + + Do something. + + Parameters: + **param** ("int") -- A parameter. + + Return type: + "None" + + Term + A description + + maybe multiple lines + + Next Term + Somethign about it + + -[ Example ]- + """ +) +def docstring_with_definition_list_after_params_no_blank_line(param: int) -> None: # noqa: U100 + """Do something. + + Args: + param: A parameter. + + Term + A description + + maybe multiple lines + + Next Term + Somethign about it + .. rubric:: Example + """ + + AUTO_FUNCTION = ".. autofunction:: mod.{}" AUTO_CLASS = """\ .. autoclass:: mod.{} diff --git a/whitelist.txt b/whitelist.txt index 7a2e21de..0e748bdc 100644 --- a/whitelist.txt +++ b/whitelist.txt @@ -39,6 +39,7 @@ isfunction iterdir kwonlyargs libs +lineno lru metaclass ModuleType @@ -73,6 +74,7 @@ stmt stringify subclasses supertype +tagname tempdir testroot textwrap