diff --git a/CHANGES b/CHANGES index 4b1f4b17a..39606e301 100644 --- a/CHANGES +++ b/CHANGES @@ -32,6 +32,7 @@ Bugs fixed * autosummary: The interface of ``sphinx.ext.autosummary.get_documenter()`` has been changed * #4630: Have order on msgids in sphinx.pot deterministic +* #4563: autosummary: Incorrect end of line punctuation detection Testing -------- diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 63ca10483..1334084e2 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -64,6 +64,7 @@ from typing import TYPE_CHECKING from docutils import nodes from docutils.parsers.rst import Directive, directives +from docutils.parsers.rst.states import RSTStateMachine, state_classes from docutils.statemachine import ViewList from six import string_types from six import text_type @@ -77,6 +78,7 @@ from sphinx.ext.autodoc.directive import DocumenterBridge, Options from sphinx.ext.autodoc.importer import import_module from sphinx.pycode import ModuleAnalyzer, PycodeError from sphinx.util import import_object, rst, logging +from sphinx.util.docutils import new_document if TYPE_CHECKING: from typing import Any, Dict, List, Tuple, Type, Union # NOQA @@ -340,7 +342,7 @@ class Autosummary(Directive): # -- Grab the summary documenter.add_content(None) - summary = extract_summary(self.result.data[:]) + summary = extract_summary(self.result.data[:], self.state.document) items.append((display_name, sig, summary, real_name)) @@ -447,8 +449,8 @@ def mangle_signature(sig, max_chars=30): return u"(%s)" % sig -def extract_summary(doc): - # type: (List[unicode]) -> unicode +def extract_summary(doc, document): + # type: (List[unicode], Any) -> unicode """Extract summary from docstring.""" # Skip a blank lines at the top @@ -464,13 +466,19 @@ def extract_summary(doc): break # Try to find the "first sentence", which may span multiple lines - m = re.search(r"^([A-Z].*?\.)(?:\s|$)", " ".join(doc).strip()) - if m: - summary = m.group(1).strip() - elif doc: - summary = doc[0].strip() + sentences = " ".join(doc).split('.') + if len(sentences) == 1: + summary = sentences[0].strip() else: summary = '' + state_machine = RSTStateMachine(state_classes, 'Body') + while sentences: + summary += sentences.pop(0) + '.' + node = new_document('', document.settings) + state_machine.run([summary], node) + if not node.traverse(nodes.system_message): + # considered as that splitting by period does not break inline markups + break return summary diff --git a/tests/test_ext_autosummary.py b/tests/test_ext_autosummary.py index 11ee442d2..f9e217801 100644 --- a/tests/test_ext_autosummary.py +++ b/tests/test_ext_autosummary.py @@ -56,11 +56,26 @@ def test_mangle_signature(): def test_extract_summary(): + from sphinx.util.docutils import new_document + from mock import Mock + settings = Mock(language_code='', + id_prefix='', + auto_id_prefix='', + pep_reference=False, + rfc_reference=False) + document = new_document('', settings) + + # normal case doc = ['', 'This is a first sentence. And second one.', '', 'Second block is here'] - assert extract_summary(doc) == 'This is a first sentence.' + assert extract_summary(doc, document) == 'This is a first sentence.' + + # inliner case + doc = ['This sentence contains *emphasis text having dots.*,', + 'it does not break sentence.'] + assert extract_summary(doc, document) == ' '.join(doc) @pytest.mark.sphinx('dummy', **default_kw) @@ -101,7 +116,7 @@ def test_get_items_summary(make_app, app_params): expected_values = { 'withSentence': 'I have a sentence which spans multiple lines.', - 'noSentence': "this doesn't start with a", + 'noSentence': "this doesn't start with a capital.", 'emptyLine': "This is the real summary", 'module_attr': 'This is a module attribute', 'C.class_attr': 'This is a class attribute',