Skip to content

Commit

Permalink
feat: Add option to make parentheses around the type of returned valu…
Browse files Browse the repository at this point in the history
…es optional (Google-style)

Issue #137: #137
  • Loading branch information
pawamoy committed Sep 1, 2023
1 parent e8a9fdc commit b0620f8
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 33 deletions.
91 changes: 65 additions & 26 deletions docs/docstrings.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,6 @@ the text as if it is Markdown, or AsciiDoc, or reStructuredText, etc..

### Google-style

The parser accepts a few options:

- `ignore_init_summary`: Ignore the first line in `__init__` methods' docstrings.
Useful when merging `__init__` docstring into class' docstrings
with mkdocstrings-python's [`merge_init_into_class`][merge_init] option. Default: false.
- `trim_doctest_flags`: Remove the [doctest flags][] written as comments in `pycon` snippets within a docstring.
These flags are used to alter the behavior of [doctest][] when testing docstrings,
and should not be visible in your docs. Default: true.
- `warn_unknown_params`: Warn about parameters documented in docstrings that do not appear in the signature. Default: true.
- `returns_multiple_items`: Parse Returns sections as if they contain multiple items.
It means that continuation lines must be indented. Default: true.

Sections are written like this:

```
Expand Down Expand Up @@ -129,6 +117,24 @@ def foo(a, b):
"""
```

#### Parser options

The parser accepts a few options:

- `ignore_init_summary`: Ignore the first line in `__init__` methods' docstrings.
Useful when merging `__init__` docstring into class' docstrings
with mkdocstrings-python's [`merge_init_into_class`][merge_init] option. Default: false.
- `trim_doctest_flags`: Remove the [doctest flags][] written as comments in `pycon` snippets within a docstring.
These flags are used to alter the behavior of [doctest][] when testing docstrings,
and should not be visible in your docs. Default: true.
- `warn_unknown_params`: Warn about parameters documented in docstrings that do not appear in the signature. Default: true.
- `returns_multiple_items`: Parse [Returns sections](#returns) as if they contain multiple items.
It means that continuation lines must be indented. Default: true.
- `returns_named_value`: Whether to parse `thing: Description` in [Returns sections](#returns) as a name and description,
rather than a type and description. When true, type must be wrapped in parentheses: `(int): Description.`.
When false, parentheses are optional but the items cannot be named: `int: Description`.


#### Attributes

- Multiple items allowed
Expand Down Expand Up @@ -688,6 +694,22 @@ def foo() -> tuple[bool, float]:
...
```

You have to indent each continuation line when documenting returned values,
even if there's only one value returned:

```python
"""Foo.
Returns:
success: Whether it succeeded.
A longer description of what is considered success,
and what is considered failure.
"""
```

If you don't want to indent continuation lines for the only returned value,
use the [`returns_multiple_items=False`](#parser-options) parser option.

Type annotations can as usual be overridden using types in parentheses
in the docstring itself:

Expand All @@ -700,6 +722,21 @@ Returns:
"""
```

If you want to specify the type without a name, you still have to wrap the type in parentheses:

```python
"""Foo.
Returns:
(int): Whether it succeeded.
(Decimal): Final precision.
"""
```

If you don't want to wrap the type in parentheses,
use the [`returns_named_value=False`](#parser-options) parser option.
Setting it to false will disallow specifying a name.

TIP: **Types in docstrings are resolved using the docstrings' function scope.**
See previous tips for types in docstrings.

Expand All @@ -711,20 +748,6 @@ multiple items.

### Numpydoc-style

The parser accepts a few options:

- `ignore_init_summary`: Ignore the first line in `__init__` methods' docstrings.
Useful when merging `__init__` docstring into class' docstrings
with mkdocstrings-python's [`merge_init_into_class`][merge_init] option. Default: false.
- `trim_doctest_flags`: Remove the [doctest flags][] written as comments in `pycon` snippets within a docstring.
These flags are used to alter the behavior of [doctest][] when testing docstrings,
and should not be visible in your docs. Default: true.
- `warn_unknown_params`: Warn about parameters documented in docstrings that do not appear in the signature. Default: true.
- `allow_section_blank_line`: Allow blank lines in sections' content.
When false, a blank line finishes the current section.
When true, single blank lines are kept as part of the section.
You can terminate sections with double blank lines. Default: false.

Sections are written like this:

```
Expand Down Expand Up @@ -805,6 +828,22 @@ several syntaxes are supported:
"""
```

#### Parser options

The parser accepts a few options:

- `ignore_init_summary`: Ignore the first line in `__init__` methods' docstrings.
Useful when merging `__init__` docstring into class' docstrings
with mkdocstrings-python's [`merge_init_into_class`][merge_init] option. Default: false.
- `trim_doctest_flags`: Remove the [doctest flags][] written as comments in `pycon` snippets within a docstring.
These flags are used to alter the behavior of [doctest][] when testing docstrings,
and should not be visible in your docs. Default: true.
- `warn_unknown_params`: Warn about parameters documented in docstrings that do not appear in the signature. Default: true.
- `allow_section_blank_line`: Allow blank lines in sections' content.
When false, a blank line finishes the current section.
When true, single blank lines are kept as part of the section.
You can terminate sections with double blank lines. Default: false.

#### Attributes

- Multiple items allowed
Expand Down
28 changes: 21 additions & 7 deletions src/griffe/docstrings/google.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,8 @@ def _read_returns_section(
docstring: Docstring,
*,
offset: int,
returns_multiple_items: bool,
returns_multiple_items: bool = True,
returns_named_value: bool = True,
**options: Any,
) -> tuple[DocstringSectionReturns | None, int]:
returns = []
Expand All @@ -440,12 +441,20 @@ def _read_returns_section(
block = [(new_offset, one_block.splitlines())]

for index, (line_number, return_lines) in enumerate(block):
match = _RE_NAME_ANNOTATION_DESCRIPTION.match(return_lines[0])
if not match:
_warn(docstring, line_number, f"Failed to get name, annotation or description from '{return_lines[0]}'")
continue

name, annotation, description = match.groups()
if returns_named_value:
match = _RE_NAME_ANNOTATION_DESCRIPTION.match(return_lines[0])
if not match:
_warn(docstring, line_number, f"Failed to get name, annotation or description from '{return_lines[0]}'")
continue
name, annotation, description = match.groups()
else:
name = None
if ":" in return_lines[0]:
annotation, description = return_lines[0].split(":", 1)
annotation = annotation.lstrip("(").rstrip(")")
else:
annotation = None
description = return_lines[0]
description = "\n".join([description.lstrip(), *return_lines[1:]]).rstrip("\n")

if annotation:
Expand Down Expand Up @@ -689,6 +698,7 @@ def parse(
trim_doctest_flags: bool = True,
returns_multiple_items: bool = True,
warn_unknown_params: bool = True,
returns_named_value: bool = True,
**options: Any,
) -> list[DocstringSection]:
"""Parse a docstring.
Expand All @@ -702,6 +712,9 @@ def parse(
trim_doctest_flags: Whether to remove doctest flags from Python example blocks.
returns_multiple_items: Whether the `Returns` section has multiple items.
warn_unknown_params: Warn about documented parameters not appearing in the signature.
returns_named_value: Whether to parse `thing: Description` in returns sections as a name and description,
rather than a type and description. When true, type must be wrapped in parentheses: `(int): Description.`.
When false, parentheses are optional but the items cannot be named: `int: Description`.
**options: Additional parsing options.
Returns:
Expand All @@ -718,6 +731,7 @@ def parse(
"trim_doctest_flags": trim_doctest_flags,
"returns_multiple_items": returns_multiple_items,
"warn_unknown_params": warn_unknown_params,
"returns_named_value": returns_named_value,
**options,
}

Expand Down
37 changes: 37 additions & 0 deletions tests/test_docstrings/test_google.py
Original file line number Diff line number Diff line change
Expand Up @@ -1440,3 +1440,40 @@ def test_avoid_false_positive_sections(parse_google: ParserType) -> None:
"Possible section skipped, reasons: Missing blank line above section; Extraneous blank line below section title",
"Possible admonition skipped, reasons: Missing blank line above admonition; Extraneous blank line below admonition title",
]


def test_type_in_returns_without_parentheses(parse_google: ParserType) -> None:
"""Assert we can parse the return type without parentheses.
Parameters:
parse_google: Fixture parser.
"""
docstring = """
Summary.
Returns:
int: Description
on several lines.
"""
sections, warnings = parse_google(docstring, returns_named_value=False)
assert len(sections) == 2
assert not warnings
retval = sections[1].value[0]
assert retval.name == ""
assert retval.annotation == "int"
assert retval.description == "Description\non several lines."

docstring = """
Summary.
Returns:
Description
on several lines.
"""
sections, warnings = parse_google(docstring, returns_named_value=False)
assert len(sections) == 2
assert len(warnings) == 1
retval = sections[1].value[0]
assert retval.name == ""
assert retval.annotation is None
assert retval.description == "Description\non several lines."

0 comments on commit b0620f8

Please sign in to comment.