From 911f74f4671ed9de46d3074b7b2482dfda8675c1 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 15 Feb 2018 22:56:30 +0900 Subject: [PATCH 01/14] Fix #4623: sphinx.build_main no longer exists in 1.7.0 --- CHANGES | 4 ++++ sphinx/__init__.py | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/CHANGES b/CHANGES index df83d9b24..1b7aa967d 100644 --- a/CHANGES +++ b/CHANGES @@ -10,6 +10,9 @@ Incompatible changes Deprecated ---------- +* #4623: ``sphinx.build_main()`` is deprecated. Use + ``sphinx.cmd.build.build_main()`` instead. + Features added -------------- @@ -19,6 +22,7 @@ Bugs fixed * #4608: epub: Invalid meta tag is generated * #4260: autodoc: keyword only argument separator is not disappeared if it is appeared at top of the argument list +* #4623: sphinx.build_main no longer exists in 1.7.0 Testing -------- diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 49dd0ab8b..27a5dc63a 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -15,6 +15,7 @@ from __future__ import absolute_import import os +import sys import warnings from os import path @@ -69,6 +70,26 @@ def main(*args, **kwargs): return build.main(*args, **kwargs) +def build_main(argv=sys.argv[1:]): + """Sphinx build "main" command-line entry.""" + warnings.warn( + '`sphinx.build_main()` has moved to `sphinx.cmd.build.build_main()`.', + RemovedInSphinx20Warning, + stacklevel=2, + ) + return build.build_main(argv) + + +def make_main(argv=sys.argv[1:]): + """Sphinx build "make mode" entry.""" + warnings.warn( + '`sphinx.build_main()` has moved to `sphinx.cmd.build.make_main()`.', + RemovedInSphinx20Warning, + stacklevel=2, + ) + return build.make_main(argv) + + if __name__ == '__main__': warnings.warn( '`sphinx` has moved to `sphinx.build`.', From b5bae235f2033ca0c732e3401d463193f29d6866 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 16 Feb 2018 01:52:51 +0900 Subject: [PATCH 02/14] Fix #4615: The argument of ``sphinx.build`` has been changed in 1.7.0 --- CHANGES | 1 + sphinx/__init__.py | 9 +++++---- sphinx/apidoc.py | 1 + sphinx/quickstart.py | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 1b7aa967d..538a44a4f 100644 --- a/CHANGES +++ b/CHANGES @@ -23,6 +23,7 @@ Bugs fixed * #4260: autodoc: keyword only argument separator is not disappeared if it is appeared at top of the argument list * #4623: sphinx.build_main no longer exists in 1.7.0 +* #4615: The argument of ``sphinx.build`` has been changed in 1.7.0 Testing -------- diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 27a5dc63a..ace7c02e1 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -67,27 +67,28 @@ def main(*args, **kwargs): RemovedInSphinx20Warning, stacklevel=2, ) + args = args[1:] # skip first argument to adjust arguments (refs: #4615) return build.main(*args, **kwargs) -def build_main(argv=sys.argv[1:]): +def build_main(argv=sys.argv): """Sphinx build "main" command-line entry.""" warnings.warn( '`sphinx.build_main()` has moved to `sphinx.cmd.build.build_main()`.', RemovedInSphinx20Warning, stacklevel=2, ) - return build.build_main(argv) + return build.build_main(argv[1:]) # skip first argument to adjust arguments (refs: #4615) -def make_main(argv=sys.argv[1:]): +def make_main(argv=sys.argv): """Sphinx build "make mode" entry.""" warnings.warn( '`sphinx.build_main()` has moved to `sphinx.cmd.build.make_main()`.', RemovedInSphinx20Warning, stacklevel=2, ) - return build.make_main(argv) + return build.make_main(argv[1:]) # skip first argument to adjust arguments (refs: #4615) if __name__ == '__main__': diff --git a/sphinx/apidoc.py b/sphinx/apidoc.py index 4e20fb7e4..be5e5c5ab 100644 --- a/sphinx/apidoc.py +++ b/sphinx/apidoc.py @@ -21,6 +21,7 @@ def main(*args, **kwargs): RemovedInSphinx20Warning, stacklevel=2, ) + args = args[1:] # skip first argument to adjust arguments (refs: #4615) _main(*args, **kwargs) diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 81676e8b1..ab4cf92da 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -22,6 +22,7 @@ def main(*args, **kwargs): RemovedInSphinx20Warning, stacklevel=2, ) + args = args[1:] # skip first argument to adjust arguments (refs: #4615) _main(*args, **kwargs) From a9ecb190a7d9f778509ef3185fb1c938f6e65cbd Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 17 Feb 2018 23:26:28 +0900 Subject: [PATCH 03/14] autosummary: Fix compatibility of get_documenter() API (refs: #4366) --- CHANGES | 5 ++++ sphinx/application.py | 1 + sphinx/ext/autosummary/__init__.py | 20 ++++++++++++-- sphinx/util/compat.py | 43 ++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 sphinx/util/compat.py diff --git a/CHANGES b/CHANGES index bcfe2b92d..905fb5cfa 100644 --- a/CHANGES +++ b/CHANGES @@ -10,6 +10,9 @@ Incompatible changes Deprecated ---------- +* autosummary: The interface of ``sphinx.ext.autosummary.get_documenter()`` has + been changed (Since 1.7.0) + Features added -------------- @@ -22,6 +25,8 @@ Bugs fixed * #4622: epub: :confval:`epub_scheme` does not effect to content.opf * #4627: graphviz: Fit graphviz images to page * #4617: quickstart: PROJECT_DIR argument is required +* autosummary: The interface of ``sphinx.ext.autosummary.get_documenter()`` has + been changed Testing -------- diff --git a/sphinx/application.py b/sphinx/application.py index d118c2afe..d943de81d 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -88,6 +88,7 @@ builtin_extensions = ( 'sphinx.roles', 'sphinx.transforms.post_transforms', 'sphinx.transforms.post_transforms.images', + 'sphinx.util.compat', # collectors should be loaded by specific order 'sphinx.environment.collectors.dependencies', 'sphinx.environment.collectors.asset', diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 4728281c3..85dd8d15b 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -58,6 +58,7 @@ import os import posixpath import re import sys +import warnings from types import ModuleType from typing import TYPE_CHECKING @@ -69,6 +70,7 @@ from six import text_type import sphinx from sphinx import addnodes +from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.environment.adapters.toctree import TocTree from sphinx.ext.autodoc import get_documenters from sphinx.ext.autodoc.directive import DocumenterBridge, Options @@ -153,13 +155,17 @@ def autosummary_table_visit_html(self, node): # -- autodoc integration ------------------------------------------------------- +# current application object (used in `get_documenter()`). +_app = None # type: Sphinx + + class FakeDirective(DocumenterBridge): def __init__(self): super(FakeDirective, self).__init__({}, None, Options(), 0) # type: ignore -def get_documenter(app, obj, parent): - # type: (Sphinx, Any, Any) -> Type[Documenter] +def get_documenter(*args): + # type: (Any) -> Type[Documenter] """Get an autodoc.Documenter class suitable for documenting the given object. @@ -168,6 +174,16 @@ def get_documenter(app, obj, parent): belongs to. """ from sphinx.ext.autodoc import DataDocumenter, ModuleDocumenter + if len(args) == 3: + # new style arguments: (app, obj, parent) + app, obj, parent = args + else: + # old style arguments: (obj, parent) + app = _app + obj, parent = args + warnings.warn('the interface of get_documenter() has been changed. ' + 'Please give application object as first argument.', + RemovedInSphinx20Warning) if inspect.ismodule(obj): # ModuleDocumenter.can_document_member always returns False diff --git a/sphinx/util/compat.py b/sphinx/util/compat.py new file mode 100644 index 000000000..f25e43315 --- /dev/null +++ b/sphinx/util/compat.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +""" + sphinx.util.compat + ~~~~~~~~~~~~~~~~~~ + + modules for backward compatibility + + :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import sys + + +if False: + # For type annotation + from typing import Any, Dict # NOQA + from sphinx.application import Sphinx # NOQA + + +def register_application_for_autosummary(app): + # type: (Sphinx) -> None + """Register application object to autosummary module. + + Since Sphinx-1.7, documenters and attrgetters are registered into + applicaiton object. As a result, the arguments of + ``get_documenter()`` has been changed. To keep compatibility, + this handler registers application object to the module. + """ + if 'sphinx.ext.autosummary' in sys.modules: + from sphinx.ext import autosummary + autosummary._app = app + + +def setup(app): + # type: (Sphinx) -> Dict[unicode, Any] + app.connect('builder-inited', register_application_for_autosummary) + + return { + 'version': 'builtin', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } From 070c1b294ae6dd49c51bfa0099e2a2cb45c2c782 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 18 Feb 2018 17:34:21 +0900 Subject: [PATCH 04/14] Adjust .codecov.yml --- .codecov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.codecov.yml b/.codecov.yml index aa7a96c27..0e6fdf0c3 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,6 +1,6 @@ coverage: status: - patch: + project: default: # allowed to drop X% and still result in a "success" commit status threshold: 0.05 From f51b644f39ff53f14332eb8cd5019bbe8dc0e0e1 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 18 Feb 2018 18:08:31 +0900 Subject: [PATCH 05/14] Use typing.TYPE_CHECKING --- sphinx/util/compat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/util/compat.py b/sphinx/util/compat.py index f25e43315..b3f78ca7d 100644 --- a/sphinx/util/compat.py +++ b/sphinx/util/compat.py @@ -10,10 +10,10 @@ """ import sys +from typing import TYPE_CHECKING -if False: - # For type annotation +if TYPE_CHECKING: from typing import Any, Dict # NOQA from sphinx.application import Sphinx # NOQA From 8e73cbca5206e11b62838a90e9f935e7906e8b85 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 18 Feb 2018 21:15:03 +0900 Subject: [PATCH 06/14] Fix ImportError --- sphinx/util/compat.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinx/util/compat.py b/sphinx/util/compat.py index b3f78ca7d..32ac11341 100644 --- a/sphinx/util/compat.py +++ b/sphinx/util/compat.py @@ -8,6 +8,7 @@ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ +from __future__ import absolute_import import sys from typing import TYPE_CHECKING From 582c829fa86482075206cf735bf1bdb241dce8b2 Mon Sep 17 00:00:00 2001 From: cocoatomo Date: Sat, 17 Feb 2018 14:15:27 +0900 Subject: [PATCH 07/14] Have order on msgids in sphinx.pot deterministic --- sphinx/builders/gettext.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index a5cca6a47..0a678975b 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -12,7 +12,7 @@ from __future__ import unicode_literals from codecs import open -from collections import defaultdict +from collections import defaultdict, OrderedDict from datetime import datetime, tzinfo, timedelta from os import path, walk, getenv from time import time @@ -68,8 +68,8 @@ class Catalog(object): # type: () -> None self.messages = [] # type: List[unicode] # retain insertion order, a la OrderedDict - self.metadata = {} # type: Dict[unicode, List[Tuple[unicode, int, unicode]]] - # msgid -> file, line, uid + self.metadata = OrderedDict() # type: Dict[unicode, List[Tuple[unicode, int, unicode]]] + # msgid -> file, line, uid def add(self, msg, origin): # type: (unicode, MsgOrigin) -> None @@ -236,7 +236,8 @@ class MessageCatalogBuilder(I18nBuilder): def _extract_from_template(self): # type: () -> None - files = self._collect_templates() + files = list(self._collect_templates()) + files.sort() logger.info(bold('building [%s]: ' % self.name), nonl=1) logger.info('targets for %d template files', len(files)) From 07d2cdd47b9bb5319a2de11ab514b956379068ab Mon Sep 17 00:00:00 2001 From: cocoatomo Date: Sun, 18 Feb 2018 09:05:32 +0900 Subject: [PATCH 08/14] Add a testcase for deterministic msgid order --- .../test-intl-template/_templates/template1.html | 5 +++++ .../test-intl-template/_templates/template2.html | 5 +++++ tests/roots/test-intl-template/conf.py | 3 +++ tests/roots/test-intl-template/contents.rst | 0 tests/test_build_gettext.py | 15 +++++++++++++++ 5 files changed, 28 insertions(+) create mode 100644 tests/roots/test-intl-template/_templates/template1.html create mode 100644 tests/roots/test-intl-template/_templates/template2.html create mode 100644 tests/roots/test-intl-template/conf.py create mode 100644 tests/roots/test-intl-template/contents.rst diff --git a/tests/roots/test-intl-template/_templates/template1.html b/tests/roots/test-intl-template/_templates/template1.html new file mode 100644 index 000000000..f4b49f122 --- /dev/null +++ b/tests/roots/test-intl-template/_templates/template1.html @@ -0,0 +1,5 @@ +{% extends "layout.html" %} +{% block body %} +

{{ _('Template 1') }}

+

{%trans%}This is Template 1.{%endtrans%}

+{% endblock %} diff --git a/tests/roots/test-intl-template/_templates/template2.html b/tests/roots/test-intl-template/_templates/template2.html new file mode 100644 index 000000000..2a21069a6 --- /dev/null +++ b/tests/roots/test-intl-template/_templates/template2.html @@ -0,0 +1,5 @@ +{% extends "layout.html" %} +{% block body %} +

{{ _('Template 2') }}

+

{%trans%}This is Template 2.{%endtrans%}

+{% endblock %} diff --git a/tests/roots/test-intl-template/conf.py b/tests/roots/test-intl-template/conf.py new file mode 100644 index 000000000..1f4d84d07 --- /dev/null +++ b/tests/roots/test-intl-template/conf.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +templates_path = ['_templates'] diff --git a/tests/roots/test-intl-template/contents.rst b/tests/roots/test-intl-template/contents.rst new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index c14013f9a..179c90288 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -165,3 +165,18 @@ def test_gettext_template(app): result = (app.outdir / 'sphinx.pot').text(encoding='utf-8') assert "Welcome" in result assert "Sphinx %(version)s" in result + + +@pytest.mark.sphinx('gettext', testroot='intl-template') +def test_gettext_template_msgid_order_in_sphinxpot(app): + app.builder.build_all() + assert (app.outdir / 'sphinx.pot').isfile() + + result = (app.outdir / 'sphinx.pot').text(encoding='utf-8') + assert re.search( + ('msgid "Template 1".*' + 'msgid "This is Template 1\.".*' + 'msgid "Template 2".*' + 'msgid "This is Template 2\.".*'), + result, + flags=re.S) From 536fee9e8314bb3ef9765d8ee68a45b7b17a4f0a Mon Sep 17 00:00:00 2001 From: cocoatomo Date: Sun, 18 Feb 2018 22:28:37 +0900 Subject: [PATCH 09/14] Fix indentation violation (flake8 E114) --- sphinx/builders/gettext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index 0a678975b..27ec3f391 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -69,7 +69,7 @@ class Catalog(object): self.messages = [] # type: List[unicode] # retain insertion order, a la OrderedDict self.metadata = OrderedDict() # type: Dict[unicode, List[Tuple[unicode, int, unicode]]] - # msgid -> file, line, uid + # msgid -> file, line, uid def add(self, msg, origin): # type: (unicode, MsgOrigin) -> None From 97463fd07e6c5d068c5cc7c1946647356bc12d32 Mon Sep 17 00:00:00 2001 From: cocoatomo Date: Mon, 19 Feb 2018 09:20:04 +0900 Subject: [PATCH 10/14] Suppress a flake8 warning (E501) --- sphinx/builders/gettext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index 27ec3f391..c33a0af40 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -68,7 +68,7 @@ class Catalog(object): # type: () -> None self.messages = [] # type: List[unicode] # retain insertion order, a la OrderedDict - self.metadata = OrderedDict() # type: Dict[unicode, List[Tuple[unicode, int, unicode]]] + self.metadata = OrderedDict() # type: Dict[unicode, List[Tuple[unicode, int, unicode]]] # NOQA # msgid -> file, line, uid def add(self, msg, origin): From f3d13d9ce70b19f1ddc14dc084a5c214fe1102e0 Mon Sep 17 00:00:00 2001 From: cocoatomo Date: Mon, 19 Feb 2018 09:51:30 +0900 Subject: [PATCH 11/14] Fix the dirname for testing files --- .../_templates/template1.html | 0 .../_templates/template2.html | 0 .../roots/{test-intl-template => test-gettext-template}/conf.py | 0 .../{test-intl-template => test-gettext-template}/contents.rst | 0 tests/test_build_gettext.py | 2 +- 5 files changed, 1 insertion(+), 1 deletion(-) rename tests/roots/{test-intl-template => test-gettext-template}/_templates/template1.html (100%) rename tests/roots/{test-intl-template => test-gettext-template}/_templates/template2.html (100%) rename tests/roots/{test-intl-template => test-gettext-template}/conf.py (100%) rename tests/roots/{test-intl-template => test-gettext-template}/contents.rst (100%) diff --git a/tests/roots/test-intl-template/_templates/template1.html b/tests/roots/test-gettext-template/_templates/template1.html similarity index 100% rename from tests/roots/test-intl-template/_templates/template1.html rename to tests/roots/test-gettext-template/_templates/template1.html diff --git a/tests/roots/test-intl-template/_templates/template2.html b/tests/roots/test-gettext-template/_templates/template2.html similarity index 100% rename from tests/roots/test-intl-template/_templates/template2.html rename to tests/roots/test-gettext-template/_templates/template2.html diff --git a/tests/roots/test-intl-template/conf.py b/tests/roots/test-gettext-template/conf.py similarity index 100% rename from tests/roots/test-intl-template/conf.py rename to tests/roots/test-gettext-template/conf.py diff --git a/tests/roots/test-intl-template/contents.rst b/tests/roots/test-gettext-template/contents.rst similarity index 100% rename from tests/roots/test-intl-template/contents.rst rename to tests/roots/test-gettext-template/contents.rst diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index 179c90288..58ac7409c 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -167,7 +167,7 @@ def test_gettext_template(app): assert "Sphinx %(version)s" in result -@pytest.mark.sphinx('gettext', testroot='intl-template') +@pytest.mark.sphinx('gettext', testroot='gettext-template') def test_gettext_template_msgid_order_in_sphinxpot(app): app.builder.build_all() assert (app.outdir / 'sphinx.pot').isfile() From 9b0bf85c93c6c4d1446ec3cbb457783911404b80 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 19 Feb 2018 10:49:54 +0900 Subject: [PATCH 12/14] Update CHANGES for PR #4634 --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index c5ebfce61..4b1f4b17a 100644 --- a/CHANGES +++ b/CHANGES @@ -31,6 +31,7 @@ Bugs fixed * #4615: The argument of ``sphinx.build`` has been changed in 1.7.0 * autosummary: The interface of ``sphinx.ext.autosummary.get_documenter()`` has been changed +* #4630: Have order on msgids in sphinx.pot deterministic Testing -------- From 20613edc033a3c193f4a1fda5f93b7b28ad8bb12 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 18 Feb 2018 01:51:57 +0900 Subject: [PATCH 13/14] refactor: Add extract_summary() for easily testing --- sphinx/ext/autosummary/__init__.py | 50 +++++++++++++++++------------- tests/test_ext_autosummary.py | 14 ++++++--- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 85dd8d15b..63ca10483 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -340,27 +340,7 @@ class Autosummary(Directive): # -- Grab the summary documenter.add_content(None) - doc = self.result.data - - while doc and not doc[0].strip(): - doc.pop(0) - - # If there's a blank line, then we can assume the first sentence / - # paragraph has ended, so anything after shouldn't be part of the - # summary - for i, piece in enumerate(doc): - if not piece.strip(): - doc = doc[:i] - 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() - else: - summary = '' + summary = extract_summary(self.result.data[:]) items.append((display_name, sig, summary, real_name)) @@ -467,6 +447,34 @@ def mangle_signature(sig, max_chars=30): return u"(%s)" % sig +def extract_summary(doc): + # type: (List[unicode]) -> unicode + """Extract summary from docstring.""" + + # Skip a blank lines at the top + while doc and not doc[0].strip(): + doc.pop(0) + + # If there's a blank line, then we can assume the first sentence / + # paragraph has ended, so anything after shouldn't be part of the + # summary + for i, piece in enumerate(doc): + if not piece.strip(): + doc = doc[:i] + 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() + else: + summary = '' + + return summary + + def limited_join(sep, items, max_chars=30, overflow_marker="..."): # type: (unicode, List[unicode], int, unicode) -> unicode """Join a number of strings to one, limiting the length to *max_chars*. diff --git a/tests/test_ext_autosummary.py b/tests/test_ext_autosummary.py index a77785954..11ee442d2 100644 --- a/tests/test_ext_autosummary.py +++ b/tests/test_ext_autosummary.py @@ -9,14 +9,12 @@ :license: BSD, see LICENSE for details. """ +import pytest from six import iteritems, StringIO -from sphinx.ext.autosummary import mangle_signature, import_by_name - +from sphinx.ext.autosummary import mangle_signature, import_by_name, extract_summary from sphinx.testing.util import etree_parse -import pytest - html_warnfile = StringIO() @@ -57,6 +55,14 @@ def test_mangle_signature(): assert res == outp, (u"'%s' -> '%s' != '%s'" % (inp, res, outp)) +def test_extract_summary(): + doc = ['', + 'This is a first sentence. And second one.', + '', + 'Second block is here'] + assert extract_summary(doc) == 'This is a first sentence.' + + @pytest.mark.sphinx('dummy', **default_kw) def test_get_items_summary(make_app, app_params): import sphinx.ext.autosummary From d40db5efa260efbecdbd7cf3d8b86d3de7280b10 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 18 Feb 2018 12:09:24 +0900 Subject: [PATCH 14/14] Fix #4563: autosummary: Incorrect end of line punctuation detection --- CHANGES | 1 + sphinx/ext/autosummary/__init__.py | 24 ++++++++++++++++-------- tests/test_ext_autosummary.py | 19 +++++++++++++++++-- 3 files changed, 34 insertions(+), 10 deletions(-) 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',