Skip to content

Commit fd32eb5

Browse files
authored
Extract _prepare_docstrings() (#13977)
1 parent 9203f2f commit fd32eb5

File tree

2 files changed

+79
-73
lines changed

2 files changed

+79
-73
lines changed

sphinx/ext/autodoc/_docstrings.py

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
from sphinx.util.inspect import getdoc
1717

1818
if TYPE_CHECKING:
19-
from collections.abc import Sequence
19+
from collections.abc import Iterator, Mapping, Sequence
2020
from typing import Any, Literal
2121

22+
from sphinx.events import EventManager
23+
from sphinx.ext.autodoc._directive_options import _AutoDocumenterOptions
2224
from sphinx.ext.autodoc._property_types import _ItemProperties
2325
from sphinx.ext.autodoc.importer import _AttrGetter
2426

@@ -28,6 +30,76 @@
2830
_OBJECT_NEW_DOCSTRING = (tuple(prepare_docstring(object.__new__.__doc__ or '')),)
2931

3032

33+
def _prepare_docstrings(
34+
*,
35+
props: _ItemProperties,
36+
attr_docs: Mapping[tuple[str, str], list[str]],
37+
) -> list[list[str]] | None:
38+
"""Add content from docstrings, attribute documentation and user."""
39+
if props._docstrings is not None:
40+
if props._docstrings:
41+
docstrings = [list(doc) for doc in props._docstrings]
42+
else:
43+
# append at least a dummy docstring, so that the event
44+
# autodoc-process-docstring is fired and can add some
45+
# content if desired
46+
docstrings = [[]]
47+
else:
48+
docstrings = None
49+
50+
if props.obj_type in {'data', 'attribute'}:
51+
return docstrings
52+
53+
if props.obj_type in {'class', 'exception'}:
54+
real_module = props._obj___module__ or props.module_name
55+
if props.module_name != real_module:
56+
try:
57+
# override analyzer to obtain doc-comment around its definition.
58+
ma = ModuleAnalyzer.for_module(props.module_name)
59+
ma.analyze()
60+
attr_docs = ma.attr_docs
61+
except PycodeError:
62+
pass
63+
64+
# add content from attribute documentation
65+
if attr_docs and props.parts:
66+
key = ('.'.join(props.parent_names), props.name)
67+
if key in attr_docs:
68+
# make a copy of docstring for attributes to avoid cache
69+
# the change of autodoc-process-docstring event.
70+
return [list(attr_docs[key])]
71+
72+
return docstrings
73+
74+
75+
def _process_docstrings(
76+
docstrings: list[list[str]] | None,
77+
*,
78+
events: EventManager,
79+
props: _ItemProperties,
80+
obj: Any,
81+
options: _AutoDocumenterOptions,
82+
) -> Iterator[str]:
83+
"""Let the user process the docstrings before adding them."""
84+
if docstrings is None:
85+
return
86+
for docstring_lines in docstrings:
87+
# let extensions pre-process docstrings
88+
events.emit(
89+
'autodoc-process-docstring',
90+
props.obj_type,
91+
props.full_name,
92+
obj,
93+
options,
94+
docstring_lines,
95+
)
96+
97+
yield from docstring_lines
98+
if docstring_lines and docstring_lines[-1]:
99+
# ensure the docstring ends with a blank line
100+
yield ''
101+
102+
31103
def _get_docstring_lines(
32104
props: _ItemProperties,
33105
*,

sphinx/ext/autodoc/_documenters.py

Lines changed: 6 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
member_order_option,
1717
members_option,
1818
)
19+
from sphinx.ext.autodoc._docstrings import _prepare_docstrings, _process_docstrings
1920
from sphinx.ext.autodoc._member_finder import _document_members
2021
from sphinx.ext.autodoc._renderer import _add_content, _directive_header_lines
2122
from sphinx.ext.autodoc.importer import _load_object_by_name
@@ -27,7 +28,7 @@
2728
from sphinx.util.typing import restify, stringify_annotation
2829

2930
if TYPE_CHECKING:
30-
from collections.abc import Callable, Iterator, Sequence
31+
from collections.abc import Callable, Sequence
3132
from typing import Any, ClassVar, Final, Literal, NoReturn
3233

3334
from sphinx.config import Config
@@ -181,12 +182,13 @@ def add_directive_header(self, *, indent: str) -> None:
181182

182183
def add_content(self, more_content: StringList | None, *, indent: str) -> None:
183184
"""Add content from docstrings, attribute documentation and user."""
184-
analyzer_source = '' if self.analyzer is None else self.analyzer.srcname
185185
# add content from docstrings
186+
attr_docs = {} if self.analyzer is None else self.analyzer.attr_docs
187+
analyzer_source = '' if self.analyzer is None else self.analyzer.srcname
186188
processed_doc = StringList(
187189
list(
188-
self._process_docstrings(
189-
self._get_docstrings(),
190+
_process_docstrings(
191+
_prepare_docstrings(props=self.props, attr_docs=attr_docs),
190192
events=self._events,
191193
props=self.props,
192194
obj=self.props._obj,
@@ -214,74 +216,6 @@ def add_content(self, more_content: StringList | None, *, indent: str) -> None:
214216
indent=indent + ' ' * (self.props.obj_type != 'module'),
215217
)
216218

217-
def _get_docstrings(self) -> list[list[str]] | None:
218-
"""Add content from docstrings, attribute documentation and user."""
219-
if self.props._docstrings is not None:
220-
docstrings = [list(doc) for doc in self.props._docstrings]
221-
else:
222-
docstrings = None
223-
props = self.props
224-
225-
if docstrings is not None and len(docstrings) == 0:
226-
# append at least a dummy docstring, so that the event
227-
# autodoc-process-docstring is fired and can add some
228-
# content if desired
229-
docstrings.append([])
230-
231-
if props.obj_type in {'data', 'attribute'}:
232-
return docstrings
233-
234-
attr_docs = None if self.analyzer is None else self.analyzer.find_attr_docs()
235-
if props.obj_type in {'class', 'exception'}:
236-
real_module = props._obj___module__ or props.module_name
237-
if props.module_name != real_module:
238-
try:
239-
# override analyzer to obtain doc-comment around its definition.
240-
ma = ModuleAnalyzer.for_module(props.module_name)
241-
ma.analyze()
242-
attr_docs = ma.attr_docs
243-
except PycodeError:
244-
pass
245-
246-
# add content from attribute documentation
247-
if attr_docs is not None and props.parts:
248-
key = ('.'.join(props.parent_names), props.name)
249-
if key in attr_docs:
250-
# make a copy of docstring for attributes to avoid cache
251-
# the change of autodoc-process-docstring event.
252-
return [list(attr_docs[key])]
253-
254-
return docstrings
255-
256-
@staticmethod
257-
def _process_docstrings(
258-
docstrings: list[list[str]] | None,
259-
*,
260-
events: EventManager,
261-
props: _ItemProperties,
262-
obj: Any,
263-
options: _AutoDocumenterOptions,
264-
) -> Iterator[str]:
265-
"""Let the user process the docstrings before adding them."""
266-
if docstrings is None:
267-
return
268-
for docstring_lines in docstrings:
269-
# let extensions preprocess docstrings
270-
events.emit(
271-
'autodoc-process-docstring',
272-
props.obj_type,
273-
props.full_name,
274-
obj,
275-
options,
276-
docstring_lines,
277-
)
278-
279-
if docstring_lines and docstring_lines[-1]:
280-
# append a blank line to the end of the docstring
281-
docstring_lines.append('')
282-
283-
yield from docstring_lines
284-
285219
@staticmethod
286220
def _assemble_more_content(
287221
more_content: StringList,

0 commit comments

Comments
 (0)