Skip to content

Commit 1fde0d2

Browse files
authored
fix: Keep newlines in parameter (and other multiline directives) descriptions in Sphinx docstrings
Issue-mkdocstrings-808: mkdocstrings/mkdocstrings#808 PR-438: #438
1 parent 5325786 commit 1fde0d2

File tree

2 files changed

+53
-4
lines changed

2 files changed

+53
-4
lines changed

src/griffe/_internal/docstrings/sphinx.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from contextlib import suppress
1010
from dataclasses import dataclass, field
11+
from inspect import cleandoc
1112
from typing import TYPE_CHECKING, Any, Callable, TypedDict
1213

1314
from griffe._internal.docstrings.models import (
@@ -458,10 +459,10 @@ def _consolidate_continuation_lines(lines: list[str], offset: int) -> tuple[str,
458459
# start processing after first item
459460
curr_line_index += 1
460461
while curr_line_index < len(lines) and not lines[curr_line_index].startswith(":"):
461-
block.append(lines[curr_line_index].lstrip())
462+
block.append(lines[curr_line_index])
462463
curr_line_index += 1
463464

464-
return " ".join(block).rstrip("\n"), curr_line_index - 1
465+
return cleandoc("\n".join(block)).rstrip("\n"), curr_line_index - 1
465466

466467

467468
def _consolidate_descriptive_type(descriptive_type: str) -> str:

tests/test_docstrings/test_sphinx.py

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ def test_parse__param_field_multi_line__param_section(parse_sphinx: ParserType,
190190
assert len(sections) == 2
191191
assert sections[1].kind is DocstringSectionKind.parameters
192192
actual = sections[1].value[0]
193-
expected = DocstringParameter(SOME_NAME, description=f"{SOME_TEXT} {SOME_EXTRA_TEXT}")
193+
expected = DocstringParameter(SOME_NAME, description=f"{SOME_TEXT}\n{SOME_EXTRA_TEXT}")
194194
assert isinstance(actual, type(expected))
195195
assert actual.as_dict() == expected.as_dict()
196196

@@ -747,6 +747,54 @@ def test_parse__param_type_no_name__error_message(parse_sphinx: ParserType) -> N
747747
assert "Failed to get parameter name from" in warnings[0]
748748

749749

750+
def test_parse__param_multiline(parse_sphinx: ParserType) -> None:
751+
"""Parse multiline parameter descriptions.
752+
753+
Parameters:
754+
parse_sphinx: Fixture parser.
755+
"""
756+
docstring = """Do something.
757+
758+
:param foo: This is a docstring that is long enough to run onto a second line,
759+
because it is quite long.
760+
761+
A second paragraph is also required.
762+
:param bar: This is an example that is quite long, and also requires bullet
763+
points to be clear about intent:
764+
765+
* First thing
766+
767+
```
768+
a code block
769+
```
770+
771+
* Second thing
772+
* Third thing
773+
"""
774+
775+
sections, _ = parse_sphinx(docstring)
776+
param_section = sections[1]
777+
778+
param_foo = param_section.value[0]
779+
assert param_foo.description == (
780+
"This is a docstring that is long enough to run onto a second line,\n"
781+
"because it is quite long.\n\n"
782+
"A second paragraph is also required."
783+
)
784+
785+
param_bar = param_section.value[1]
786+
assert param_bar.description == (
787+
"This is an example that is quite long, and also requires bullet\n"
788+
"points to be clear about intent:\n\n"
789+
"* First thing\n\n"
790+
" ```\n"
791+
" a code block\n"
792+
" ```\n\n"
793+
"* Second thing\n"
794+
"* Third thing"
795+
)
796+
797+
750798
@pytest.mark.parametrize(
751799
"docstring",
752800
[
@@ -775,7 +823,7 @@ def test_parse__attribute_field_multi_line__param_section(parse_sphinx: ParserTy
775823
assert len(sections) == 2
776824
assert sections[1].kind is DocstringSectionKind.attributes
777825
actual = sections[1].value[0]
778-
expected = DocstringAttribute(SOME_NAME, description=f"{SOME_TEXT} {SOME_EXTRA_TEXT}")
826+
expected = DocstringAttribute(SOME_NAME, description=f"{SOME_TEXT}\n{SOME_EXTRA_TEXT}")
779827
assert isinstance(actual, type(expected))
780828
assert actual.as_dict() == expected.as_dict()
781829
assert not warnings

0 commit comments

Comments
 (0)