Skip to content

Commit bfbc005

Browse files
Preserve type information from docstrings if no type annotation is present and parameter has default value. (#576)
1 parent 62274c9 commit bfbc005

File tree

4 files changed

+79
-35
lines changed

4 files changed

+79
-35
lines changed

src/sphinx_autodoc_typehints/__init__.py

Lines changed: 55 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -808,43 +808,63 @@ def _inject_signature(
808808
lines: list[str],
809809
) -> None:
810810
for arg_name in signature.parameters:
811-
annotation = type_hints.get(arg_name)
812-
813-
default = signature.parameters[arg_name].default
814-
815-
if arg_name.endswith("_"):
816-
arg_name = f"{arg_name[:-1]}\\_" # noqa: PLW2901
817-
818-
insert_index = None
819-
for at, line in enumerate(lines):
820-
if _line_is_param_line_for_arg(line, arg_name):
821-
# Get the arg_name from the doc to match up for type in case it has a star prefix.
822-
# Line is in the correct format so this is guaranteed to return tuple[str, str].
823-
func = _get_sphinx_line_keyword_and_argument
824-
_, arg_name = func(line) # type: ignore[assignment, misc] # noqa: PLW2901
825-
insert_index = at
826-
break
827-
828-
if annotation is not None and insert_index is None and app.config.always_document_param_types:
829-
lines.append(f":param {arg_name}:")
830-
insert_index = len(lines)
831-
832-
if insert_index is not None:
833-
if annotation is None:
834-
type_annotation = f":type {arg_name}: "
835-
else:
836-
short_literals = app.config.python_display_short_literal_types
837-
formatted_annotation = add_type_css_class(
838-
format_annotation(annotation, app.config, short_literals=short_literals)
839-
)
840-
type_annotation = f":type {arg_name}: {formatted_annotation}"
811+
_inject_arg_signature(type_hints, signature, app, lines, arg_name)
812+
813+
814+
def _inject_arg_signature(
815+
type_hints: dict[str, Any],
816+
signature: inspect.Signature,
817+
app: Sphinx,
818+
lines: list[str],
819+
arg_name: str,
820+
) -> None:
821+
annotation = type_hints.get(arg_name)
822+
823+
default = signature.parameters[arg_name].default
824+
825+
if arg_name.endswith("_"):
826+
arg_name = f"{arg_name[:-1]}\\_"
827+
828+
insert_index = None
829+
for at, line in enumerate(lines):
830+
if _line_is_param_line_for_arg(line, arg_name):
831+
# Get the arg_name from the doc to match up for type in case it has a star prefix.
832+
# Line is in the correct format so this is guaranteed to return tuple[str, str].
833+
_, arg_name = _get_sphinx_line_keyword_and_argument(line) # type: ignore[assignment, misc]
834+
insert_index = at
835+
break
836+
837+
if annotation is not None and insert_index is None and app.config.always_document_param_types:
838+
lines.append(f":param {arg_name}:")
839+
insert_index = len(lines)
840+
841+
if insert_index is not None:
842+
has_preexisting_annotation = False
843+
844+
if annotation is None:
845+
type_annotation, has_preexisting_annotation = _find_preexisting_type_annotation(lines, arg_name)
846+
else:
847+
short_literals = app.config.python_display_short_literal_types
848+
formatted_annotation = add_type_css_class(
849+
format_annotation(annotation, app.config, short_literals=short_literals)
850+
)
851+
type_annotation = f":type {arg_name}: {formatted_annotation}"
852+
853+
if app.config.typehints_defaults:
854+
formatted_default = format_default(app, default, annotation is not None or has_preexisting_annotation)
855+
if formatted_default:
856+
type_annotation = _append_default(app, lines, insert_index, type_annotation, formatted_default)
857+
858+
lines.insert(insert_index, type_annotation)
841859

842-
if app.config.typehints_defaults:
843-
formatted_default = format_default(app, default, annotation is not None)
844-
if formatted_default:
845-
type_annotation = _append_default(app, lines, insert_index, type_annotation, formatted_default)
846860

847-
lines.insert(insert_index, type_annotation)
861+
def _find_preexisting_type_annotation(lines: list[str], arg_name: str) -> tuple[str, bool]:
862+
"""Find a type entry in the input docstring that matches the given arg name."""
863+
type_annotation = f":type {arg_name}: "
864+
for line in lines:
865+
if line.startswith(type_annotation):
866+
return line, True
867+
return type_annotation, False
848868

849869

850870
def _append_default(

tests/roots/test-dummy/dummy_module_without_complete_typehints.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,14 @@ def function_with_defaults_and_some_typehints(x: int = 0, y=None) -> str: # noq
3535
:param x: foo
3636
:param y: bar
3737
"""
38+
39+
40+
def function_with_defaults_and_type_information_in_docstring(x, y=0) -> str: # noqa: ANN001
41+
"""
42+
Function docstring.
43+
44+
:type x: int
45+
:type y: int
46+
:param x: foo
47+
:param y: bar
48+
"""

tests/roots/test-dummy/without_complete_typehints.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ Simple Module
77
.. autofunction:: dummy_module_without_complete_typehints.function_with_some_defaults_and_some_typehints
88
.. autofunction:: dummy_module_without_complete_typehints.function_with_some_defaults_and_more_typehints
99
.. autofunction:: dummy_module_without_complete_typehints.function_with_defaults_and_some_typehints
10+
.. autofunction:: dummy_module_without_complete_typehints.function_with_defaults_and_type_information_in_docstring

tests/test_sphinx_autodoc_typehints.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,18 @@ def test_default_annotation_without_typehints(app: SphinxTestApp, status: String
10741074
10751075
Return type:
10761076
"str"
1077+
1078+
dummy_module_without_complete_typehints.function_with_defaults_and_type_information_in_docstring(x, y=0)
1079+
1080+
Function docstring.
1081+
1082+
Parameters:
1083+
* **x** (*int*) -- foo
1084+
1085+
* **y** (int, default: "0") -- bar
1086+
1087+
Return type:
1088+
"str"
10771089
"""
10781090
assert text_contents == dedent(expected_contents)
10791091

0 commit comments

Comments
 (0)