Skip to content

Commit cd0668b

Browse files
committed
feat: Support Google-style Yields sections
Issue #89: #89 PR #116: #116
1 parent 75b4024 commit cd0668b

File tree

3 files changed

+80
-0
lines changed

3 files changed

+80
-0
lines changed

Diff for: src/pytkdocs/parsers/docstrings/base.py

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class Type:
105105
PARAMETERS = "parameters"
106106
EXCEPTIONS = "exceptions"
107107
RETURN = "return"
108+
YIELD = "yield"
108109
EXAMPLES = "examples"
109110
ATTRIBUTES = "attributes"
110111
KEYWORD_ARGS = "keyword_args"

Diff for: src/pytkdocs/parsers/docstrings/google.py

+40
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
"exceptions:": Section.Type.EXCEPTIONS,
1818
"return:": Section.Type.RETURN,
1919
"returns:": Section.Type.RETURN,
20+
"yield:": Section.Type.YIELD,
21+
"yields:": Section.Type.YIELD,
2022
"example:": Section.Type.EXAMPLES,
2123
"examples:": Section.Type.EXAMPLES,
2224
"attribute:": Section.Type.ATTRIBUTES,
@@ -46,6 +48,7 @@ def __init__(self, replace_admonitions: bool = True) -> None:
4648
Section.Type.EXAMPLES: self.read_examples_section,
4749
Section.Type.ATTRIBUTES: self.read_attributes_section,
4850
Section.Type.RETURN: self.read_return_section,
51+
Section.Type.YIELD: self.read_yield_section,
4952
}
5053

5154
def parse_sections(self, docstring: str) -> List[Section]: # noqa: D102
@@ -418,6 +421,43 @@ def read_return_section(self, lines: List[str], start_index: int) -> Tuple[Optio
418421

419422
return Section(Section.Type.RETURN, AnnotatedObject(annotation, description)), i
420423

424+
def read_yield_section(self, lines: List[str], start_index: int) -> Tuple[Optional[Section], int]:
425+
"""
426+
Parse a "yields" section.
427+
428+
Arguments:
429+
lines: The return block lines.
430+
start_index: The line number to start at.
431+
432+
Returns:
433+
A tuple containing a `Section` (or `None`) and the index at which to continue parsing.
434+
"""
435+
text, i = self.read_block(lines, start_index)
436+
437+
# Early exit if there is no text in the yield section
438+
if not text:
439+
self.error(f"Empty yield section at line {start_index}")
440+
return None, i
441+
442+
# First try to get the annotation and description from the docstring
443+
try:
444+
type_, text = text.split(":", 1)
445+
except ValueError:
446+
description = text
447+
annotation = self.context["annotation"]
448+
# If there was no annotation in the docstring then move to signature
449+
if annotation is empty and self.context["signature"]:
450+
annotation = self.context["signature"].return_annotation
451+
else:
452+
annotation = type_.lstrip()
453+
description = text.lstrip()
454+
455+
# There was no type in the docstring and no annotation
456+
if annotation is empty:
457+
self.error("No yield type/annotation in docstring/signature")
458+
459+
return Section(Section.Type.YIELD, AnnotatedObject(annotation, description)), i
460+
421461
def read_examples_section(self, lines: List[str], start_index: int) -> Tuple[Optional[Section], int]:
422462
"""
423463
Parse an "examples" section.

Diff for: tests/test_parsers/test_docstrings/test_google.py

+39
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import inspect
44
from textwrap import dedent
5+
from typing import Iterator
56

67
from pytkdocs.loader import Loader
78
from pytkdocs.parsers.docstrings.base import Section
@@ -659,3 +660,41 @@ def test_parse_module_attributes_section():
659660
{"name": "E", "annotation": "float", "description": "Epsilon."},
660661
]
661662
assert [serialize_attribute(attr) for attr in attr_section.value] == expected
663+
664+
665+
def test_docstring_with_yield_section():
666+
"""Parse Yields section."""
667+
668+
def f():
669+
"""A useless range wrapper.
670+
671+
Yields:
672+
int: Integers.
673+
"""
674+
yield from range(10)
675+
676+
sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
677+
assert len(sections) == 2
678+
annotated = sections[1].value
679+
assert annotated.annotation is int
680+
assert annotated.description == "Integers."
681+
assert not errors
682+
683+
684+
def test_docstring_with_yield_section():
685+
"""Parse Yields section."""
686+
687+
def f() -> Iterator[int]:
688+
"""A useless range wrapper.
689+
690+
Yields:
691+
Integers.
692+
"""
693+
yield from range(10)
694+
695+
sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
696+
assert len(sections) == 2
697+
annotated = sections[1].value
698+
assert annotated.annotation is Iterator[int]
699+
assert annotated.description == "Integers."
700+
assert not errors

0 commit comments

Comments
 (0)