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

Fixed default options not displaying for parameters without type hints. #279

Merged
merged 15 commits into from
Jan 6, 2023
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
82 changes: 47 additions & 35 deletions src/sphinx_autodoc_typehints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,19 +475,27 @@ def add(val: str) -> None:
return result


def format_default(app: Sphinx, default: Any) -> str | None:
def format_default(app: Sphinx, default: Any, is_annotated: bool) -> str | None:
if default is inspect.Parameter.empty:
return None
formatted = repr(default).replace("\\", "\\\\")
if app.config.typehints_defaults.startswith("braces"):
return f" (default: ``{formatted}``)"

if is_annotated:
if app.config.typehints_defaults.startswith("braces"):
return f" (default: ``{formatted}``)"
else: # other option is comma
return f", default: ``{formatted}``"
else:
return f", default: ``{formatted}``"
if app.config.typehints_defaults == "braces-after":
return f" (default: ``{formatted}``)"
else:
return f"default: ``{formatted}``"


def process_docstring(
app: Sphinx, what: str, name: str, obj: Any, options: Options | None, lines: list[str] # noqa: U100
) -> None:

original_obj = obj
obj = obj.fget if isinstance(obj, property) else obj
if not callable(obj):
Expand Down Expand Up @@ -561,41 +569,45 @@ def _inject_types_to_docstring(
name: str,
lines: list[str],
) -> None:
for arg_name, annotation in type_hints.items():
if arg_name == "return":
continue # this is handled separately later
if signature is None or arg_name not in signature.parameters:
default = inspect.Parameter.empty
else:

if signature is not None:
for arg_name in signature.parameters:
annotation = type_hints.get(arg_name, None)

default = signature.parameters[arg_name].default
if arg_name.endswith("_"):
arg_name = f"{arg_name[:-1]}\\_"

formatted_annotation = format_annotation(annotation, app.config)
if arg_name.endswith("_"):
arg_name = f"{arg_name[:-1]}\\_"

insert_index = None
for at, line in enumerate(lines):
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
insert_index = None
for at, line in enumerate(lines):
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

if annotation is not None and insert_index is None and app.config.always_document_param_types:
lines.append(f":param {arg_name}:")
insert_index = len(lines)

if insert_index is None and app.config.always_document_param_types:
lines.append(f":param {arg_name}:")
insert_index = len(lines)

if insert_index is not None:
type_annotation = f":type {arg_name}: {formatted_annotation}"
if app.config.typehints_defaults:
formatted_default = format_default(app, default)
if formatted_default:
if app.config.typehints_defaults.endswith("after"):
lines[insert_index] += formatted_default
else: # add to last param doc line
type_annotation += formatted_default
lines.insert(insert_index, type_annotation)
if insert_index is not None:
if annotation is None:
type_annotation = f":type {arg_name}: "
else:
formatted_annotation = format_annotation(annotation, app.config)
type_annotation = f":type {arg_name}: {formatted_annotation}"

if app.config.typehints_defaults:
formatted_default = format_default(app, default, annotation is not None)
if formatted_default:
if app.config.typehints_defaults.endswith("after"):
lines[insert_index] += formatted_default
else: # add to last param doc line
type_annotation += formatted_default

lines.insert(insert_index, type_annotation)

if "return" in type_hints and not inspect.isclass(original_obj):
if what == "method" and name.endswith(".__init__"): # avoid adding a return type for data class __init__
Expand Down
34 changes: 34 additions & 0 deletions tests/roots/test-dummy/dummy_module_without_complete_typehints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
def function_with_some_defaults_and_without_typehints(x, y=None): # noqa: U100
"""
Function docstring.

:param x: foo
:param y: bar
"""


def function_with_some_defaults_and_some_typehints(x: int, y=None): # noqa: U100
"""
Function docstring.

:param x: foo
:param y: bar
"""


def function_with_some_defaults_and_more_typehints(x: int, y=None) -> str: # noqa: U100
"""
Function docstring.

:param x: foo
:param y: bar
"""


def function_with_defaults_and_some_typehints(x: int = 0, y=None) -> str: # noqa: U100
"""
Function docstring.

:param x: foo
:param y: bar
"""
7 changes: 7 additions & 0 deletions tests/roots/test-dummy/without_complete_typehints.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Simple Module
=============

.. autofunction:: dummy_module_without_complete_typehints.function_with_some_defaults_and_without_typehints
.. autofunction:: dummy_module_without_complete_typehints.function_with_some_defaults_and_some_typehints
.. autofunction:: dummy_module_without_complete_typehints.function_with_some_defaults_and_more_typehints
.. autofunction:: dummy_module_without_complete_typehints.function_with_defaults_and_some_typehints
59 changes: 59 additions & 0 deletions tests/test_sphinx_autodoc_typehints.py
Original file line number Diff line number Diff line change
Expand Up @@ -1121,3 +1121,62 @@ def test_sphinx_output_with_use_signature_and_return(app: SphinxTestApp, status:
"str"
"""
assert text_contents == dedent(expected_contents)


@pytest.mark.sphinx("text", testroot="dummy")
@patch("sphinx.writers.text.MAXWIDTH", 2000)
def test_default_annotation_without_typehints(app: SphinxTestApp, status: StringIO) -> None:
set_python_path()
app.config.master_doc = "without_complete_typehints" # type: ignore # create flag
app.config.typehints_defaults = "comma" # type: ignore
app.build()
assert "build succeeded" in status.getvalue()
text_path = pathlib.Path(app.srcdir) / "_build" / "text" / "without_complete_typehints.txt"
text_contents = text_path.read_text().replace("–", "--")
expected_contents = """\
Simple Module
*************

dummy_module_without_complete_typehints.function_with_some_defaults_and_without_typehints(x, y=None)

Function docstring.

Parameters:
* **x** -- foo

* **y** (default: "None") -- bar

dummy_module_without_complete_typehints.function_with_some_defaults_and_some_typehints(x, y=None)

Function docstring.

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

* **y** (default: "None") -- bar

dummy_module_without_complete_typehints.function_with_some_defaults_and_more_typehints(x, y=None)

Function docstring.

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

* **y** (default: "None") -- bar

Return type:
"str"

dummy_module_without_complete_typehints.function_with_defaults_and_some_typehints(x=0, y=None)

Function docstring.

Parameters:
* **x** ("int", default: "0") -- foo

* **y** (default: "None") -- bar

Return type:
"str"
"""
assert text_contents == dedent(expected_contents)