Skip to content

Commit

Permalink
Fixed default options not displaying for parameters without type hint…
Browse files Browse the repository at this point in the history
…s. (#279)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Resolves #234
  • Loading branch information
BhavyeMathur committed Jan 6, 2023
1 parent 32dc422 commit 4e6bc55
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 35 deletions.
82 changes: 47 additions & 35 deletions src/sphinx_autodoc_typehints/__init__.py
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
@@ -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
@@ -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
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)

0 comments on commit 4e6bc55

Please sign in to comment.