Skip to content

Commit 48f6739

Browse files
authored
Accept a sequence of strings in extract_summary() (#14014)
1 parent de745a9 commit 48f6739

File tree

2 files changed

+44
-40
lines changed

2 files changed

+44
-40
lines changed

sphinx/ext/autosummary/__init__.py

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ def get_items(self, names: list[str]) -> list[tuple[str, str | None, str, str]]:
301301
)
302302
raise ValueError(msg)
303303

304+
document_settings = self.state.document.settings
304305
env = self.env
305306
config = env.config
306307
current_document = env.current_document
@@ -395,7 +396,7 @@ def get_items(self, names: list[str]) -> list[tuple[str, str | None, str, str]]:
395396
props=props,
396397
options=opts,
397398
)
398-
summary = extract_summary(list(docstring_lines), self.state.document)
399+
summary = extract_summary(list(docstring_lines), document_settings)
399400

400401
items.append((display_name, sig, summary, real_name))
401402

@@ -537,51 +538,47 @@ def mangle_signature(sig: str, max_chars: int = 30) -> str:
537538
return '(%s)' % sig
538539

539540

540-
def extract_summary(doc: list[str], document: Any) -> str:
541+
def extract_summary(doc: Sequence[str], settings: Any) -> str:
541542
"""Extract summary from docstring."""
543+
# Find the first stanza (heading, sentence, paragraph, etc.).
544+
# If there's a blank line, then we can assume that the stanza has ended,
545+
# so anything after shouldn't be part of the summary.
546+
first_stanza = []
547+
content_started = False
548+
for line in doc:
549+
is_blank_line = not line or line.isspace()
550+
if not content_started:
551+
# Skip any blank lines at the start
552+
if is_blank_line:
553+
continue
554+
content_started = True
555+
if content_started:
556+
if is_blank_line:
557+
break
558+
first_stanza.append(line)
542559

543-
def parse(doc: list[str], settings: Any) -> nodes.document:
544-
state_machine = RSTStateMachine(state_classes, 'Body')
545-
node = new_document('', settings)
546-
node.reporter = NullReporter()
547-
state_machine.run(doc, node)
548-
549-
return node
550-
551-
# Skip a blank lines at the top
552-
while doc and not doc[0].strip():
553-
doc.pop(0)
554-
555-
# If there's a blank line, then we can assume the first sentence /
556-
# paragraph has ended, so anything after shouldn't be part of the
557-
# summary
558-
for i, piece in enumerate(doc):
559-
if not piece.strip():
560-
doc = doc[:i]
561-
break
562-
563-
if doc == []:
560+
if not first_stanza:
564561
return ''
565562

566563
# parse the docstring
567-
node = parse(doc, document.settings)
564+
node = _parse_summary(first_stanza, settings)
568565
if isinstance(node[0], nodes.section):
569566
# document starts with a section heading, so use that.
570567
summary = node[0].astext().strip()
571568
elif not isinstance(node[0], nodes.paragraph):
572569
# document starts with non-paragraph: pick up the first line
573-
summary = doc[0].strip()
570+
summary = first_stanza[0].strip()
574571
else:
575572
# Try to find the "first sentence", which may span multiple lines
576-
sentences = periods_re.split(' '.join(doc))
573+
sentences = periods_re.split(' '.join(first_stanza))
577574
if len(sentences) == 1:
578575
summary = sentences[0].strip()
579576
else:
580577
summary = ''
581578
for i in range(len(sentences)):
582579
summary = '. '.join(sentences[: i + 1]).rstrip('.') + '.'
583580
node[:] = []
584-
node = parse(doc, document.settings)
581+
node = _parse_summary(first_stanza, settings)
585582
if summary.endswith(WELL_KNOWN_ABBREVIATIONS):
586583
pass
587584
elif not any(node.findall(nodes.system_message)):
@@ -594,6 +591,15 @@ def parse(doc: list[str], settings: Any) -> nodes.document:
594591
return summary
595592

596593

594+
def _parse_summary(doc: Sequence[str], settings: Any) -> nodes.document:
595+
state_machine = RSTStateMachine(state_classes, 'Body')
596+
node = new_document('', settings)
597+
node.reporter = NullReporter()
598+
state_machine.run(doc, node)
599+
600+
return node
601+
602+
597603
def limited_join(
598604
sep: str, items: list[str], max_chars: int = 30, overflow_marker: str = '...'
599605
) -> str:

tests/test_ext_autosummary/test_ext_autosummary.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
)
2727
from sphinx.ext.autosummary.generate import main as autogen_main
2828
from sphinx.testing.util import assert_node, etree_parse
29-
from sphinx.util.docutils import new_document
3029

3130
if TYPE_CHECKING:
3231
from xml.etree.ElementTree import Element
@@ -86,7 +85,6 @@ def test_extract_summary(capsys):
8685
pep_reference=False,
8786
rfc_reference=False,
8887
)
89-
document = new_document('', settings)
9088

9189
# normal case
9290
doc = [
@@ -95,52 +93,52 @@ def test_extract_summary(capsys):
9593
'',
9694
'Second block is here',
9795
]
98-
assert extract_summary(doc, document) == 'This is a first sentence.'
96+
assert extract_summary(doc, settings) == 'This is a first sentence.'
9997

10098
# inliner case
10199
doc = [
102100
'This sentence contains *emphasis text having dots.*,',
103101
'it does not break sentence.',
104102
]
105-
assert extract_summary(doc, document) == ' '.join(doc)
103+
assert extract_summary(doc, settings) == ' '.join(doc)
106104

107105
# abbreviations
108106
doc = ['Blabla, i.e. bla.']
109-
assert extract_summary(doc, document) == ' '.join(doc)
107+
assert extract_summary(doc, settings) == ' '.join(doc)
110108

111109
doc = ['Blabla, (i.e. bla).']
112-
assert extract_summary(doc, document) == ' '.join(doc)
110+
assert extract_summary(doc, settings) == ' '.join(doc)
113111

114112
doc = ['Blabla, e.g. bla.']
115-
assert extract_summary(doc, document) == ' '.join(doc)
113+
assert extract_summary(doc, settings) == ' '.join(doc)
116114

117115
doc = ['Blabla, (e.g. bla).']
118-
assert extract_summary(doc, document) == ' '.join(doc)
116+
assert extract_summary(doc, settings) == ' '.join(doc)
119117

120118
doc = ['Blabla, et al. bla.']
121-
assert extract_summary(doc, document) == ' '.join(doc)
119+
assert extract_summary(doc, settings) == ' '.join(doc)
122120

123121
# literal
124122
doc = ['blah blah::']
125-
assert extract_summary(doc, document) == 'blah blah.'
123+
assert extract_summary(doc, settings) == 'blah blah.'
126124

127125
# heading
128126
doc = [
129127
'blah blah',
130128
'=========',
131129
]
132-
assert extract_summary(doc, document) == 'blah blah'
130+
assert extract_summary(doc, settings) == 'blah blah'
133131

134132
doc = [
135133
'=========',
136134
'blah blah',
137135
'=========',
138136
]
139-
assert extract_summary(doc, document) == 'blah blah'
137+
assert extract_summary(doc, settings) == 'blah blah'
140138

141139
# hyperlink target
142140
doc = ['Do `this <https://www.sphinx-doc.org/>`_ and that. blah blah blah.']
143-
extracted = extract_summary(doc, document)
141+
extracted = extract_summary(doc, settings)
144142
assert extracted == 'Do `this <https://www.sphinx-doc.org/>`_ and that.'
145143

146144
_, err = capsys.readouterr()

0 commit comments

Comments
 (0)