Skip to content

Commit cbce5a2

Browse files
authored
feat: Enhance Sphinx-style parameter parsing to handle invalid type info
PR-396: #396
1 parent 2d77bf1 commit cbce5a2

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-0
lines changed

src/_griffe/docstrings/sphinx.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ def _read_parameter(
150150
docstring,
151151
)
152152
name = parsed_directive.directive_parts[2]
153+
elif len(parsed_directive.directive_parts) > 3: # noqa: PLR2004
154+
# Ignoring type info, only a type with a single word is valid
155+
# https://www.sphinx-doc.org/en/master/usage/domains/python.html#info-field-lists
156+
name = parsed_directive.directive_parts[-1]
157+
if warnings:
158+
docstring_warning(docstring, 0, f"Failed to parse field directive from '{parsed_directive.line}'")
153159
else:
154160
if warnings:
155161
docstring_warning(docstring, 0, f"Failed to parse field directive from '{parsed_directive.line}'")

tests/test_docstrings/test_sphinx.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616
DocstringReturn,
1717
DocstringSectionKind,
1818
Expr,
19+
ExprAttribute,
1920
ExprBinOp,
2021
ExprName,
22+
ExprSubscript,
23+
ExprTuple,
2124
Function,
2225
Module,
2326
Parameter,
@@ -526,6 +529,80 @@ def test_parse__invalid_param_field_wrong_part_count__error_message(parse_sphinx
526529
assert "Failed to parse field directive" in warnings[0]
527530

528531

532+
def test_parse__invalid_param_field_wrong_part_count_spaces_4__error_message(parse_sphinx: ParserType) -> None:
533+
"""Parse a simple docstring.
534+
535+
Parameters:
536+
parse_sphinx: Fixture parser.
537+
"""
538+
docstring = f"""
539+
Docstring with line continuation.
540+
541+
:param typing.Union[str, int] {SOME_NAME}: {SOME_TEXT}
542+
"""
543+
544+
sections, warnings = parse_sphinx(docstring)
545+
546+
# Assert that the warning is shown
547+
assert "Failed to parse field directive" in warnings[0]
548+
549+
# Assert that the parameter is still collected, but ignores the invalid type
550+
assert len(sections) == 2
551+
assert sections[1].kind is DocstringSectionKind.parameters
552+
actual = sections[1].value[0]
553+
expected = DocstringParameter(SOME_NAME, annotation=None, description=SOME_TEXT)
554+
assert isinstance(actual, type(expected))
555+
assert actual.as_dict() == expected.as_dict()
556+
557+
558+
def test_parse__valid_param_field_part_count_3(parse_sphinx: ParserType) -> None:
559+
"""Parse a simple docstring.
560+
561+
Parameters:
562+
parse_sphinx: Fixture parser.
563+
"""
564+
docstring = f"""
565+
Docstring with line continuation.
566+
567+
:param typing.Union[str,int] {SOME_NAME}: {SOME_TEXT}
568+
"""
569+
570+
sections, _ = parse_sphinx(docstring)
571+
assert len(sections) == 2
572+
assert sections[1].kind is DocstringSectionKind.parameters
573+
actual = sections[1].value[0]
574+
expected = DocstringParameter(SOME_NAME, annotation="typing.Union[str,int]", description=SOME_TEXT)
575+
assert isinstance(actual, type(expected))
576+
assert actual.as_dict() == expected.as_dict()
577+
578+
579+
def test_parse__valid_param_field_part_count_3_with_parent(parse_sphinx: ParserType) -> None:
580+
"""Parse a simple docstring.
581+
582+
Parameters:
583+
parse_sphinx: Fixture parser.
584+
"""
585+
docstring = f"""
586+
Docstring with line continuation.
587+
588+
:param typing.Union[str,int] {SOME_NAME}: {SOME_TEXT}
589+
"""
590+
591+
parent_fn = Function("func3", parameters=Parameters(Parameter(name=SOME_NAME)))
592+
sections, _ = parse_sphinx(docstring, parent=parent_fn)
593+
assert len(sections) == 2
594+
assert sections[1].kind is DocstringSectionKind.parameters
595+
actual = sections[1].value[0]
596+
typing_expr = ExprName("typing", parent=parent_fn)
597+
expected_annotation = ExprSubscript(
598+
left=ExprAttribute(values=[typing_expr, ExprName("Union", parent=typing_expr)]),
599+
slice=ExprTuple([ExprName("str", parent=parent_fn), ExprName("int", parent=parent_fn)], implicit=True),
600+
)
601+
expected = DocstringParameter(SOME_NAME, annotation=expected_annotation, description=SOME_TEXT)
602+
assert isinstance(actual, type(expected))
603+
assert actual.as_dict() == expected.as_dict()
604+
605+
529606
def test_parse__param_twice__error_message(parse_sphinx: ParserType) -> None:
530607
"""Parse a simple docstring.
531608

0 commit comments

Comments
 (0)