From 6bbfa9ac453c5ced69178480680aab454178af3c Mon Sep 17 00:00:00 2001 From: Roman Bogorodskiy Date: Sun, 26 Nov 2017 18:02:36 +0400 Subject: [PATCH 01/86] Add autosectionlabel_max_depth config option This option defines maximum section depth that labels will be generated for by the autosectionlabel extension. This is useful when there are a lot of sections of the similar structure, for example: Releases ======== Release 1 --------- Changes ^^^^^^^ Date ^^^^ ... Release N --------- Changes ^^^^^^^ Date ^^^^ This way there'll be warnings about duplicate labels. Setting autosectionlabel_max_depth allows to skip sections deeper than 'Releases' in the example above. By default it's None, so things will not change and labels will be generated for all sections unless configured otherwise. --- sphinx/ext/autosectionlabel.py | 13 ++++++++ .../roots/test-ext-autosectionlabel/index.rst | 7 ++++ tests/test_ext_autosectionlabel.py | 33 +++++++++++++++++-- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/sphinx/ext/autosectionlabel.py b/sphinx/ext/autosectionlabel.py index 69b8c6873..9ce8922fa 100644 --- a/sphinx/ext/autosectionlabel.py +++ b/sphinx/ext/autosectionlabel.py @@ -16,10 +16,22 @@ from sphinx.util.nodes import clean_astext logger = logging.getLogger(__name__) +def node_get_depth(node): + i = 0 + cur_node = node + while cur_node.parent != node.document: + cur_node = cur_node.parent + i += 1 + return i + + def register_sections_as_label(app, document): labels = app.env.domaindata['std']['labels'] anonlabels = app.env.domaindata['std']['anonlabels'] for node in document.traverse(nodes.section): + if (app.config.autosectionlabel_max_depth and + node_get_depth(node) > app.config.autosectionlabel_max_depth): + continue labelid = node['ids'][0] docname = app.env.docname if app.config.autosectionlabel_prefix_document: @@ -39,4 +51,5 @@ def register_sections_as_label(app, document): def setup(app): app.add_config_value('autosectionlabel_prefix_document', False, 'env') + app.add_config_value('autosectionlabel_max_depth', None, 'env') app.connect('doctree-read', register_sections_as_label) diff --git a/tests/roots/test-ext-autosectionlabel/index.rst b/tests/roots/test-ext-autosectionlabel/index.rst index ac96e5cf5..408e8c07d 100644 --- a/tests/roots/test-ext-autosectionlabel/index.rst +++ b/tests/roots/test-ext-autosectionlabel/index.rst @@ -15,6 +15,11 @@ For Windows users For UNIX users -------------- +Linux +^^^^^ + +FreeBSD +^^^^^^^ References ========== @@ -23,3 +28,5 @@ References * :ref:`Installation` * :ref:`For Windows users` * :ref:`For UNIX users` +* :ref:`Linux` +* :ref:`FreeBSD` diff --git a/tests/test_ext_autosectionlabel.py b/tests/test_ext_autosectionlabel.py index 4726a2378..8e00a5467 100644 --- a/tests/test_ext_autosectionlabel.py +++ b/tests/test_ext_autosectionlabel.py @@ -15,7 +15,7 @@ import pytest @pytest.mark.sphinx('html', testroot='ext-autosectionlabel') -def test_autosectionlabel_html(app, status, warning): +def test_autosectionlabel_html(app, status, warning, skipped_labels=False): app.builder.build_all() content = (app.outdir / 'index.html').text() @@ -35,8 +35,37 @@ def test_autosectionlabel_html(app, status, warning): 'For UNIX users') assert re.search(html, content, re.S) + if skipped_labels is None: + return + + if not skipped_labels: + html = ('
  • ' + 'Linux
  • ') + assert re.search(html, content, re.S) + + html = ('
  • ' + 'FreeBSD
  • ') + assert re.search(html, content, re.S) + else: + html = '
  • Linux
  • ' + assert re.search(html, content, re.S) + + html = '
  • FreeBSD
  • ' + assert re.search(html, content, re.S) + + assert 'WARNING: undefined label: linux' in warning.getvalue() + assert 'WARNING: undefined label: freebsd' in warning.getvalue() + # Re-use test definition from above, just change the test root directory @pytest.mark.sphinx('html', testroot='ext-autosectionlabel-prefix-document') def test_autosectionlabel_prefix_document_html(app, status, warning): - return test_autosectionlabel_html(app, status, warning) + return test_autosectionlabel_html(app, status, warning, + skipped_labels=None) + + +@pytest.mark.sphinx('html', testroot='ext-autosectionlabel', + confoverrides={'autosectionlabel_max_depth': 2}) +def test_autosectionlabel_max_depth(app, status, warning): + return test_autosectionlabel_html(app, status, warning, + skipped_labels=True) From d5fd873d24c363fdd5b64f01e1b1423c9b2fbe25 Mon Sep 17 00:00:00 2001 From: Roman Bogorodskiy Date: Sun, 26 Nov 2017 18:17:44 +0400 Subject: [PATCH 02/86] Update CHANGES for autosectionlabel_max_depth --- CHANGES | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 39b9c5903..5c43c76f0 100644 --- a/CHANGES +++ b/CHANGES @@ -45,7 +45,8 @@ Features added * HTML themes can set up default sidebars through ``theme.conf`` * #3160: html: Use ```` to represent ``:kbd:`` role * #4212: autosummary: catch all exceptions when importing modules - +* #4261: autosectionlabel: Add new config value; + :confval:`autosectionlabel_max_depth` Features removed ---------------- From 61f9005b73273cbda0ee4633a79b9ffbf3a17d33 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 7 Sep 2018 15:14:01 +0300 Subject: [PATCH 03/86] _MockModule and _MockObject now display meaningful names in type annotations and superclass names --- sphinx/ext/autodoc/importer.py | 37 ++++++++++++++++++++++-------- tests/test_ext_autodoc_importer.py | 18 ++++++++++++++- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py index 4b92a7b59..6af8125f9 100644 --- a/sphinx/ext/autodoc/importer.py +++ b/sphinx/ext/autodoc/importer.py @@ -31,13 +31,18 @@ logger = logging.getLogger(__name__) class _MockObject(object): """Used by autodoc_mock_imports.""" + __display_name__ = '_MockObject' + def __new__(cls, *args, **kwargs): # type: (Any, Any) -> Any - if len(args) == 3 and isinstance(args[1], tuple) and args[1][-1].__class__ is cls: - # subclassing MockObject - return type(args[0], (_MockObject,), args[2], **kwargs) # type: ignore - else: - return super(_MockObject, cls).__new__(cls) + if len(args) == 3 and isinstance(args[1], tuple): + superclass = args[1][-1].__class__ + if superclass is cls: + # subclassing MockObject + return _make_subclass(args[0], superclass.__display_name__, + superclass=superclass, attributes=args[2]) + + return super(_MockObject, cls).__new__(cls) def __init__(self, *args, **kwargs): # type: (Any, Any) -> None @@ -61,11 +66,11 @@ class _MockObject(object): def __getitem__(self, key): # type: (str) -> _MockObject - return self + return _make_subclass(key, self.__display_name__, self.__class__)() def __getattr__(self, key): # type: (str) -> _MockObject - return self + return _make_subclass(key, self.__display_name__, self.__class__)() def __call__(self, *args, **kw): # type: (Any, Any) -> Any @@ -74,6 +79,17 @@ class _MockObject(object): return args[0] return self + def __repr__(self): + return self.__display_name__ + + +def _make_subclass(name, module, superclass=_MockObject, attributes=None): + # type: (str, str, Any, dict) -> _MockObject + attrs = {'__module__': module, '__display_name__': module + '.' + name} + attrs.update(attributes or {}) + + return type(name, (superclass,), attrs) + class _MockModule(ModuleType): """Used by autodoc_mock_imports.""" @@ -88,9 +104,10 @@ class _MockModule(ModuleType): def __getattr__(self, name): # type: (str) -> _MockObject - o = _MockObject() - o.__module__ = self.__name__ - return o + return _make_subclass(name, self.__name__)() + + def __repr__(self): + return self.__name__ class _MockImporter(object): diff --git a/tests/test_ext_autodoc_importer.py b/tests/test_ext_autodoc_importer.py index fe0c9f2bc..108d06d7c 100644 --- a/tests/test_ext_autodoc_importer.py +++ b/tests/test_ext_autodoc_importer.py @@ -9,7 +9,7 @@ :license: BSD, see LICENSE for details. """ -from sphinx.ext.autodoc.importer import _MockObject +from sphinx.ext.autodoc.importer import _MockObject, _MockModule def test_MockObject(): @@ -21,6 +21,7 @@ def test_MockObject(): class SubClass(mock.SomeClass): """docstring of SubClass""" + def method(self): return "string" @@ -29,3 +30,18 @@ def test_MockObject(): assert isinstance(obj, SubClass) assert obj.method() == "string" assert isinstance(obj.other_method(), SubClass) + + +def test_MockModule(): + mock = _MockModule('mocked_module', None) + assert isinstance(mock.some_attr, _MockObject) + assert isinstance(mock.some_method, _MockObject) + assert isinstance(mock.attr1.attr2, _MockObject) + assert isinstance(mock.attr1.attr2.meth(), _MockObject) + + assert repr(mock.some_attr) == 'mocked_module.some_attr' + assert repr(mock.some_method) == 'mocked_module.some_method' + assert repr(mock.attr1.attr2) == 'mocked_module.attr1.attr2' + assert repr(mock.attr1.attr2.meth) == 'mocked_module.attr1.attr2.meth' + + assert repr(mock) == 'mocked_module' From c0b5f4712d8051f3542fadaa47a22723f9aba201 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 7 Sep 2018 15:45:23 +0300 Subject: [PATCH 04/86] fix returned type --- sphinx/ext/autodoc/importer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py index 6af8125f9..f70d3e1ad 100644 --- a/sphinx/ext/autodoc/importer.py +++ b/sphinx/ext/autodoc/importer.py @@ -84,11 +84,11 @@ class _MockObject(object): def _make_subclass(name, module, superclass=_MockObject, attributes=None): - # type: (str, str, Any, dict) -> _MockObject + # type: (str, str, Any, dict) -> Any attrs = {'__module__': module, '__display_name__': module + '.' + name} attrs.update(attributes or {}) - return type(name, (superclass,), attrs) + return type(name, (superclass,), attrs) # type: ignore class _MockModule(ModuleType): From 4d94c3c97b39c7566d6d2eb5dafc58b67f09febc Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 7 Sep 2018 19:10:28 +0300 Subject: [PATCH 05/86] removed redundant "type: ignore" --- sphinx/ext/autodoc/importer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py index f70d3e1ad..39bd1c0dd 100644 --- a/sphinx/ext/autodoc/importer.py +++ b/sphinx/ext/autodoc/importer.py @@ -80,6 +80,7 @@ class _MockObject(object): return self def __repr__(self): + # type: () -> str return self.__display_name__ @@ -88,7 +89,7 @@ def _make_subclass(name, module, superclass=_MockObject, attributes=None): attrs = {'__module__': module, '__display_name__': module + '.' + name} attrs.update(attributes or {}) - return type(name, (superclass,), attrs) # type: ignore + return type(name, (superclass,), attrs) class _MockModule(ModuleType): @@ -107,6 +108,7 @@ class _MockModule(ModuleType): return _make_subclass(name, self.__name__)() def __repr__(self): + # type: () -> str return self.__name__ From 3e2f680af341e6148900b6ae252d3fb1ec9a1fd3 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 19 Jan 2018 13:55:17 +0000 Subject: [PATCH 06/86] Add bindep integration The bindep (BINary DEPendency automation) tool is a tool for checking the presence of binary packages needed to use an application / library. Use it to specify the dependencies needed to successfully pass the LaTex tests. Only Fedora targets are currently provided. Signed-off-by: Stephen Finucane --- bindep.txt | 15 +++++++++++++++ tox.ini | 8 ++++++++ 2 files changed, 23 insertions(+) create mode 100644 bindep.txt diff --git a/bindep.txt b/bindep.txt new file mode 100644 index 000000000..b99782017 --- /dev/null +++ b/bindep.txt @@ -0,0 +1,15 @@ +texlive [platform:rpm] +texlive-fncychap [platform:rpm] +texlive-titlesec [platform:rpm] +texlive-tabulary [platform:rpm] +texlive-framed [platform:rpm] +texlive-wrapfig [platform:rpm] +texlive-upquote [platform:rpm] +texlive-capt-of [platform:rpm] +texlive-needspace [platform:rpm] +texlive-polyglossia [platform:rpm] +texlive-luatex85 [platform:rpm] +texlive-anyfontsize [platform:rpm] +texlive-ctablestack [platform:rpm] +texlive-gnu-freefont [platform:rpm] +latexmk [platform:rpm] diff --git a/tox.ini b/tox.ini index 20f8cfdc8..3d4f589a0 100644 --- a/tox.ini +++ b/tox.ini @@ -67,3 +67,11 @@ deps = sphinxcontrib-websupport commands = python setup.py build_sphinx {posargs} + +[testenv:bindep] +description = + Install binary dependencies. +deps = + bindep +commands = + bindep test From 805861066fe7be69adff95e9fcdc0d691c3f8c54 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 6 Feb 2019 00:04:51 +0900 Subject: [PATCH 07/86] Add a helper function; get_node_line() --- sphinx/builders/linkcheck.py | 8 ++------ sphinx/util/nodes.py | 8 ++++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index 95892dbaf..230a573d2 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -25,7 +25,7 @@ from sphinx.util import encode_uri, requests, logging from sphinx.util.console import ( # type: ignore purple, red, darkgreen, darkgray, darkred, turquoise ) -from sphinx.util.nodes import traverse_parent +from sphinx.util.nodes import get_node_line from sphinx.util.requests import is_ssl_error if False: @@ -275,11 +275,7 @@ class CheckExternalLinksBuilder(Builder): if 'refuri' not in node: continue uri = node['refuri'] - lineno = None - for parent in traverse_parent(node): - if parent.line: - lineno = parent.line - break + lineno = get_node_line(node) self.wqueue.put((uri, docname, lineno), False) n += 1 done = 0 diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 484616b03..7870438f7 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -285,6 +285,14 @@ def find_source_node(node): return None +def get_node_line(node): + # type: (nodes.Element) -> int + for pnode in traverse_parent(node): + if pnode.line: + return pnode.line + return None + + def traverse_parent(node, cls=None): # type: (nodes.Element, Any) -> Iterable[nodes.Element] while node: From f551915e9fdbb8dc5a7df0e1769f539f61af215d Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 6 Feb 2019 00:21:52 +0900 Subject: [PATCH 08/86] Close #5196: linkcheck also checks remote images exist --- CHANGES | 1 + sphinx/builders/linkcheck.py | 19 +++++++++++++++---- tests/roots/test-linkcheck/links.txt | 3 +++ tests/test_build_linkcheck.py | 9 +++++++-- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 9eb401173..7a9ece74b 100644 --- a/CHANGES +++ b/CHANGES @@ -167,6 +167,7 @@ Features added * #1341 the HTML search considers words that contain a search term of length three or longer a match. * #4611: epub: Show warning for duplicated ToC entries +* #5196: linkcheck also checks remote images exist Bugs fixed ---------- diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index 230a573d2..1161edb9d 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -271,13 +271,24 @@ class CheckExternalLinksBuilder(Builder): # type: (str, nodes.Node) -> None logger.info('') n = 0 - for node in doctree.traverse(nodes.reference): - if 'refuri' not in node: + + # reference nodes + for refnode in doctree.traverse(nodes.reference): + if 'refuri' not in refnode: continue - uri = node['refuri'] - lineno = get_node_line(node) + uri = refnode['refuri'] + lineno = get_node_line(refnode) self.wqueue.put((uri, docname, lineno), False) n += 1 + + # image nodes + for imgnode in doctree.traverse(nodes.image): + uri = imgnode['candidates'].get('?') + if uri and '://' in uri: + lineno = get_node_line(imgnode) + self.wqueue.put((uri, docname, lineno), False) + n += 1 + done = 0 while done < n: self.process_result(self.rqueue.get()) diff --git a/tests/roots/test-linkcheck/links.txt b/tests/roots/test-linkcheck/links.txt index d5057e08e..ac5ed3246 100644 --- a/tests/roots/test-linkcheck/links.txt +++ b/tests/roots/test-linkcheck/links.txt @@ -11,3 +11,6 @@ Some additional anchors to exercise ignore code * `Example Bar invalid `_ * `Example anchor invalid `_ * `Complete nonsense `_ + +.. image:: http://example.com/image.png +.. figure:: http://example.com/image2.png diff --git a/tests/test_build_linkcheck.py b/tests/test_build_linkcheck.py index 6e50bd758..6d25058eb 100644 --- a/tests/test_build_linkcheck.py +++ b/tests/test_build_linkcheck.py @@ -24,7 +24,10 @@ def test_defaults(app, status, warning): assert "Anchor 'does-not-exist' not found" in content # looking for non-existent URL should fail assert " Max retries exceeded with url: /doesnotexist" in content - assert len(content.splitlines()) == 3 + # images should fail + assert "Not Found for url: http://example.com/image.png" in content + assert "Not Found for url: http://example.com/image2.png" in content + assert len(content.splitlines()) == 5 @pytest.mark.sphinx( @@ -32,7 +35,9 @@ def test_defaults(app, status, warning): confoverrides={'linkcheck_anchors_ignore': ["^!", "^top$"], 'linkcheck_ignore': [ 'https://localhost:7777/doesnotexist', - 'http://www.sphinx-doc.org/en/1.7/intro.html#'] + 'http://www.sphinx-doc.org/en/1.7/intro.html#', + 'http://example.com/image.png', + 'http://example.com/image2.png'] }) def test_anchors_ignored(app, status, warning): app.builder.build_all() From abce1569397d4542b7d0948f3b4632dd8ed84e9f Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 6 Feb 2019 01:06:07 +0900 Subject: [PATCH 09/86] Add SphinxRole --- sphinx/util/docutils.py | 43 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index e55cd016e..ba5a9d0ea 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -23,7 +23,7 @@ from docutils import nodes from docutils.io import FileOutput from docutils.parsers.rst import Directive, directives, roles, convert_directive_function from docutils.statemachine import StateMachine -from docutils.utils import Reporter +from docutils.utils import Reporter, unescape from sphinx.deprecation import RemovedInSphinx30Warning from sphinx.errors import ExtensionError @@ -36,7 +36,8 @@ report_re = re.compile('^(.+?:(?:\\d+)?): \\((DEBUG|INFO|WARNING|ERROR|SEVERE)/( if False: # For type annotation from types import ModuleType # NOQA - from typing import Any, Callable, Generator, List, Set, Tuple, Type # NOQA + from typing import Any, Callable, Dict, Generator, List, Set, Tuple, Type # NOQA + from docutils.parsers.rst.states import Inliner # NOQA from docutils.statemachine import State, StringList # NOQA from sphinx.builders import Builder # NOQA from sphinx.config import Config # NOQA @@ -383,6 +384,44 @@ class SphinxDirective(Directive): return self.env.config +class SphinxRole: + """A base class for Sphinx roles. + + This class provides helper methods for Sphinx roles. + + .. note:: The subclasses of this class might not work with docutils. + This class is strongly coupled with Sphinx. + """ + + def __call__(self, typ, rawtext, text, lineno, inliner, options={}, content=[]): + # type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA + self.type = typ + self.rawtext = rawtext + self.text = unescape(text) + self.lineno = lineno + self.inliner = inliner + self.options = options + self.content = content + + return self.run() + + def run(self): + # type: () -> Tuple[List[nodes.Node], List[nodes.system_message]] + raise NotImplementedError + + @property + def env(self): + # type: () -> BuildEnvironment + """Reference to the :class:`.BuildEnvironment` object.""" + return self.inliner.document.settings.env + + @property + def config(self): + # type: () -> Config + """Reference to the :class:`.Config` object.""" + return self.env.config + + class SphinxTranslator(nodes.NodeVisitor): """A base class for Sphinx translators. From ac70a4dd91fbfeaaf0a525a6802685273b67bf35 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 6 Feb 2019 01:03:48 +0900 Subject: [PATCH 10/86] Replace :abbr: role by class based implementation --- CHANGES | 1 + doc/extdev/index.rst | 5 +++++ sphinx/roles.py | 22 +++++++++++++++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 35d5d0900..4af1f6542 100644 --- a/CHANGES +++ b/CHANGES @@ -104,6 +104,7 @@ Deprecated * ``sphinx.io.SphinxFileInput.supported`` * ``sphinx.io.SphinxRSTFileInput`` * ``sphinx.registry.SphinxComponentRegistry.add_source_input()`` +* ``sphinx.roles.abbr_role()`` * ``sphinx.testing.util.remove_unicode_literal()`` * ``sphinx.util.attrdict`` * ``sphinx.util.force_decode()`` diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst index 56f2e7cee..08b9a9e65 100644 --- a/doc/extdev/index.rst +++ b/doc/extdev/index.rst @@ -365,6 +365,11 @@ The following is a list of deprecated interfaces. - 4.0 - ``sphinxcontrib.jsmath`` + * - ``sphinx.roles.abbr_role()`` + - 2.0 + - 4.0 + - ``sphinx.roles.Abbreviation`` + * - ``sphinx.testing.util.remove_unicode_literal()`` - 2.0 - 4.0 diff --git a/sphinx/roles.py b/sphinx/roles.py index 53ea144d8..e9a8786ca 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -9,13 +9,16 @@ """ import re +import warnings from docutils import nodes, utils from sphinx import addnodes +from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.errors import SphinxError from sphinx.locale import _ from sphinx.util import ws_re +from sphinx.util.docutils import SphinxRole from sphinx.util.nodes import split_explicit_title, process_index_entry, \ set_role_source_info @@ -338,6 +341,8 @@ _abbr_re = re.compile(r'\((.*)\)$', re.S) def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): # type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA + warnings.warn('abbr_role() is deprecated. Please use Abbrevation class instead.', + RemovedInSphinx40Warning, stacklevel=2) text = utils.unescape(text) m = _abbr_re.search(text) if m is None: @@ -349,6 +354,21 @@ def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): return [nodes.abbreviation(abbr, abbr, **options)], [] +class Abbreviation(SphinxRole): + abbr_re = re.compile(r'\((.*)\)$', re.S) + + def run(self): + # type: () -> Tuple[List[nodes.Node], List[nodes.system_message]] + matched = self.abbr_re.search(self.text) + if matched: + text = self.text[:matched.start()].strip() + self.options['explanation'] = matched.group(1) + else: + text = self.text + + return [nodes.abbreviation(self.rawtext, text, **self.options)], [] + + def index_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): # type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA # create new reference target @@ -390,7 +410,7 @@ specific_docroles = { 'menuselection': menusel_role, 'file': emph_literal_role, 'samp': emph_literal_role, - 'abbr': abbr_role, + 'abbr': Abbreviation(), 'index': index_role, } # type: Dict[str, RoleFunction] From 25027945f59fc810b511374d9cf2fad67068ecdf Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 5 Feb 2019 01:02:20 +0900 Subject: [PATCH 11/86] Close #4550: All tables and figures without ``align`` option are displayed to center --- CHANGES | 1 + sphinx/io.py | 4 +- sphinx/transforms/__init__.py | 15 ++- .../test-latex-table/expects/longtable.tex | 2 +- .../expects/longtable_having_caption.tex | 2 +- .../longtable_having_problematic_cell.tex | 2 +- ...ving_stub_columns_and_problematic_cell.tex | 2 +- .../expects/longtable_having_verbatim.tex | 2 +- .../expects/longtable_having_widths.tex | 2 +- ...ble_having_widths_and_problematic_cell.tex | 2 +- .../expects/longtable_with_tabularcolumn.tex | 2 +- tests/test_build_html.py | 120 +++++++++--------- tests/test_ext_autosummary.py | 2 +- tests/test_ext_graphviz.py | 4 +- tests/test_ext_inheritance_diagram.py | 6 +- 15 files changed, 91 insertions(+), 77 deletions(-) diff --git a/CHANGES b/CHANGES index e266c50da..3d0159f12 100644 --- a/CHANGES +++ b/CHANGES @@ -65,6 +65,7 @@ Incompatible changes * LaTeX: graphics inclusion of oversized images rescales to not exceed the text width and height, even if width and/or height option were used. (refs: #5956) +* #4550: All tables and figures without ``align`` option are displayed to center Deprecated ---------- diff --git a/sphinx/io.py b/sphinx/io.py index 9cc9f44e4..5a93fb334 100644 --- a/sphinx/io.py +++ b/sphinx/io.py @@ -21,7 +21,7 @@ from typing import Any, Union # NOQA from sphinx.deprecation import RemovedInSphinx30Warning from sphinx.transforms import ( ApplySourceWorkaround, ExtraTranslatableNodes, CitationReferences, - DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, SortIds, + DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, SortIds, FigureAligner, AutoNumbering, AutoIndexUpgrader, FilterSystemMessages, UnreferencedFootnotesDetector, SphinxSmartQuotes, DoctreeReadEvent, ManpageLink ) @@ -96,7 +96,7 @@ class SphinxStandaloneReader(SphinxBaseReader): """ transforms = [ApplySourceWorkaround, ExtraTranslatableNodes, PreserveTranslatableMessages, Locale, CitationReferences, DefaultSubstitutions, MoveModuleTargets, - HandleCodeBlocks, AutoNumbering, AutoIndexUpgrader, SortIds, + HandleCodeBlocks, AutoNumbering, AutoIndexUpgrader, SortIds, FigureAligner, RemoveTranslatableInline, FilterSystemMessages, RefOnlyBulletListTransform, UnreferencedFootnotesDetector, SphinxSmartQuotes, ManpageLink, SphinxDomains, SubstitutionDefinitionsRemover, DoctreeReadEvent, diff --git a/sphinx/transforms/__init__.py b/sphinx/transforms/__init__.py index 40d0a7ee2..890cfcb84 100644 --- a/sphinx/transforms/__init__.py +++ b/sphinx/transforms/__init__.py @@ -23,7 +23,7 @@ from sphinx.locale import _, __ from sphinx.util import logging from sphinx.util.docutils import new_document from sphinx.util.i18n import format_date -from sphinx.util.nodes import apply_source_workaround, is_smartquotable +from sphinx.util.nodes import NodeMatcher, apply_source_workaround, is_smartquotable if False: # For type annotation @@ -309,6 +309,19 @@ class UnreferencedFootnotesDetector(SphinxTransform): location=node) +class FigureAligner(SphinxTransform): + """ + Align figures to center by default. + """ + default_priority = 700 + + def apply(self, **kwargs): + # type: (Any) -> None + matcher = NodeMatcher(nodes.table, nodes.figure) + for node in self.document.traverse(matcher): # type: nodes.Element + node.setdefault('align', 'center') + + class FilterSystemMessages(SphinxTransform): """Filter system messages from a doctree.""" default_priority = 999 diff --git a/tests/roots/test-latex-table/expects/longtable.tex b/tests/roots/test-latex-table/expects/longtable.tex index 7c8699c75..ecc156a50 100644 --- a/tests/roots/test-latex-table/expects/longtable.tex +++ b/tests/roots/test-latex-table/expects/longtable.tex @@ -1,6 +1,6 @@ \label{\detokenize{longtable:longtable}} -\begin{savenotes}\sphinxatlongtablestart\begin{longtable}{|l|l|} +\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|l|l|} \hline \sphinxstyletheadfamily header1 diff --git a/tests/roots/test-latex-table/expects/longtable_having_caption.tex b/tests/roots/test-latex-table/expects/longtable_having_caption.tex index 23e9ca958..1ac901794 100644 --- a/tests/roots/test-latex-table/expects/longtable_having_caption.tex +++ b/tests/roots/test-latex-table/expects/longtable_having_caption.tex @@ -1,6 +1,6 @@ \label{\detokenize{longtable:longtable-having-caption}} -\begin{savenotes}\sphinxatlongtablestart\begin{longtable}{|l|l|} +\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|l|l|} \sphinxthelongtablecaptionisattop \caption{caption for longtable\strut}\label{\detokenize{longtable:id1}}\\*[\sphinxlongtablecapskipadjust] \hline diff --git a/tests/roots/test-latex-table/expects/longtable_having_problematic_cell.tex b/tests/roots/test-latex-table/expects/longtable_having_problematic_cell.tex index 7fe48817f..2b9d4f8c5 100644 --- a/tests/roots/test-latex-table/expects/longtable_having_problematic_cell.tex +++ b/tests/roots/test-latex-table/expects/longtable_having_problematic_cell.tex @@ -1,6 +1,6 @@ \label{\detokenize{longtable:longtable-having-problematic-cell}} -\begin{savenotes}\sphinxatlongtablestart\begin{longtable}{|*{2}{\X{1}{2}|}} +\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|*{2}{\X{1}{2}|}} \hline \sphinxstyletheadfamily header1 diff --git a/tests/roots/test-latex-table/expects/longtable_having_stub_columns_and_problematic_cell.tex b/tests/roots/test-latex-table/expects/longtable_having_stub_columns_and_problematic_cell.tex index 137752c64..e0c2b87ec 100644 --- a/tests/roots/test-latex-table/expects/longtable_having_stub_columns_and_problematic_cell.tex +++ b/tests/roots/test-latex-table/expects/longtable_having_stub_columns_and_problematic_cell.tex @@ -1,6 +1,6 @@ \label{\detokenize{longtable:longtable-having-both-stub-columns-and-problematic-cell}} -\begin{savenotes}\sphinxatlongtablestart\begin{longtable}{|*{3}{\X{1}{3}|}} +\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|*{3}{\X{1}{3}|}} \hline \sphinxstyletheadfamily header1 diff --git a/tests/roots/test-latex-table/expects/longtable_having_verbatim.tex b/tests/roots/test-latex-table/expects/longtable_having_verbatim.tex index 097449cd9..1891b2416 100644 --- a/tests/roots/test-latex-table/expects/longtable_having_verbatim.tex +++ b/tests/roots/test-latex-table/expects/longtable_having_verbatim.tex @@ -1,6 +1,6 @@ \label{\detokenize{longtable:longtable-having-verbatim}} -\begin{savenotes}\sphinxatlongtablestart\begin{longtable}{|*{2}{\X{1}{2}|}} +\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|*{2}{\X{1}{2}|}} \hline \sphinxstyletheadfamily header1 diff --git a/tests/roots/test-latex-table/expects/longtable_having_widths.tex b/tests/roots/test-latex-table/expects/longtable_having_widths.tex index 505ae4d70..aa78608f6 100644 --- a/tests/roots/test-latex-table/expects/longtable_having_widths.tex +++ b/tests/roots/test-latex-table/expects/longtable_having_widths.tex @@ -1,6 +1,6 @@ \label{\detokenize{longtable:longtable-having-widths-option}} -\begin{savenotes}\sphinxatlongtablestart\begin{longtable}{|\X{30}{100}|\X{70}{100}|} +\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|\X{30}{100}|\X{70}{100}|} \hline\noalign{\phantomsection\label{\detokenize{longtable:namedlongtable}}\label{\detokenize{longtable:mylongtable}}}% \sphinxstyletheadfamily header1 diff --git a/tests/roots/test-latex-table/expects/longtable_having_widths_and_problematic_cell.tex b/tests/roots/test-latex-table/expects/longtable_having_widths_and_problematic_cell.tex index b299bfeb8..26da90781 100644 --- a/tests/roots/test-latex-table/expects/longtable_having_widths_and_problematic_cell.tex +++ b/tests/roots/test-latex-table/expects/longtable_having_widths_and_problematic_cell.tex @@ -1,6 +1,6 @@ \label{\detokenize{longtable:longtable-having-both-widths-and-problematic-cell}} -\begin{savenotes}\sphinxatlongtablestart\begin{longtable}{|\X{30}{100}|\X{70}{100}|} +\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|\X{30}{100}|\X{70}{100}|} \hline \sphinxstyletheadfamily header1 diff --git a/tests/roots/test-latex-table/expects/longtable_with_tabularcolumn.tex b/tests/roots/test-latex-table/expects/longtable_with_tabularcolumn.tex index 8777ef090..7710f75a1 100644 --- a/tests/roots/test-latex-table/expects/longtable_with_tabularcolumn.tex +++ b/tests/roots/test-latex-table/expects/longtable_with_tabularcolumn.tex @@ -1,6 +1,6 @@ \label{\detokenize{longtable:longtable-with-tabularcolumn}} -\begin{savenotes}\sphinxatlongtablestart\begin{longtable}{|c|c|} +\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|c|c|} \hline \sphinxstyletheadfamily header1 diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 91fe933b4..514d0030c 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -527,7 +527,7 @@ def test_numfig_disabled_warn(app, warning): @pytest.mark.parametrize("fname,expect", flat_dict({ 'index.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", None, True), (".//table/caption/span[@class='caption-number']", None, True), (".//div[@class='code-block-caption']/" @@ -544,21 +544,21 @@ def test_numfig_disabled_warn(app, warning): (".//li/a/span", '^Sect.1 Foo$', True), ], 'foo.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", None, True), (".//table/caption/span[@class='caption-number']", None, True), (".//div[@class='code-block-caption']/" "span[@class='caption-number']", None, True), ], 'bar.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", None, True), (".//table/caption/span[@class='caption-number']", None, True), (".//div[@class='code-block-caption']/" "span[@class='caption-number']", None, True), ], 'baz.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", None, True), (".//table/caption/span[@class='caption-number']", None, True), (".//div[@class='code-block-caption']/" @@ -593,9 +593,9 @@ def test_numfig_without_numbered_toctree_warn(app, warning): @pytest.mark.parametrize("fname,expect", flat_dict({ 'index.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 9 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 10 $', True), (".//table/caption/span[@class='caption-number']", '^Table 9 $', True), @@ -617,13 +617,13 @@ def test_numfig_without_numbered_toctree_warn(app, warning): (".//li/code/span", '^Sect.{number}$', True), ], 'foo.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 3 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 4 $', True), (".//table/caption/span[@class='caption-number']", '^Table 1 $', True), @@ -643,11 +643,11 @@ def test_numfig_without_numbered_toctree_warn(app, warning): "span[@class='caption-number']", '^Listing 4 $', True), ], 'bar.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 5 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 7 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 8 $', True), (".//table/caption/span[@class='caption-number']", '^Table 5 $', True), @@ -663,7 +663,7 @@ def test_numfig_without_numbered_toctree_warn(app, warning): "span[@class='caption-number']", '^Listing 8 $', True), ], 'baz.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 6 $', True), (".//table/caption/span[@class='caption-number']", '^Table 6 $', True), @@ -699,9 +699,9 @@ def test_numfig_with_numbered_toctree_warn(app, warning): @pytest.mark.parametrize("fname,expect", flat_dict({ 'index.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2 $', True), (".//table/caption/span[@class='caption-number']", '^Table 1 $', True), @@ -723,13 +723,13 @@ def test_numfig_with_numbered_toctree_warn(app, warning): (".//li/a/span", '^Sect.1 Foo$', True), ], 'foo.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1.1 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1.2 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1.3 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1.4 $', True), (".//table/caption/span[@class='caption-number']", '^Table 1.1 $', True), @@ -749,11 +749,11 @@ def test_numfig_with_numbered_toctree_warn(app, warning): "span[@class='caption-number']", '^Listing 1.4 $', True), ], 'bar.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2.1 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2.3 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2.4 $', True), (".//table/caption/span[@class='caption-number']", '^Table 2.1 $', True), @@ -769,7 +769,7 @@ def test_numfig_with_numbered_toctree_warn(app, warning): "span[@class='caption-number']", '^Listing 2.4 $', True), ], 'baz.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2.2 $', True), (".//table/caption/span[@class='caption-number']", '^Table 2.2 $', True), @@ -802,9 +802,9 @@ def test_numfig_with_prefix_warn(app, warning): @pytest.mark.parametrize("fname,expect", flat_dict({ 'index.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Figure:1 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Figure:2 $', True), (".//table/caption/span[@class='caption-number']", '^Tab_1 $', True), @@ -826,13 +826,13 @@ def test_numfig_with_prefix_warn(app, warning): (".//li/a/span", '^Sect.1 Foo$', True), ], 'foo.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Figure:1.1 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Figure:1.2 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Figure:1.3 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Figure:1.4 $', True), (".//table/caption/span[@class='caption-number']", '^Tab_1.1 $', True), @@ -852,11 +852,11 @@ def test_numfig_with_prefix_warn(app, warning): "span[@class='caption-number']", '^Code-1.4 $', True), ], 'bar.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Figure:2.1 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Figure:2.3 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Figure:2.4 $', True), (".//table/caption/span[@class='caption-number']", '^Tab_2.1 $', True), @@ -872,7 +872,7 @@ def test_numfig_with_prefix_warn(app, warning): "span[@class='caption-number']", '^Code-2.4 $', True), ], 'baz.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Figure:2.2 $', True), (".//table/caption/span[@class='caption-number']", '^Tab_2.2 $', True), @@ -906,9 +906,9 @@ def test_numfig_with_secnum_depth_warn(app, warning): @pytest.mark.parametrize("fname,expect", flat_dict({ 'index.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2 $', True), (".//table/caption/span[@class='caption-number']", '^Table 1 $', True), @@ -930,13 +930,13 @@ def test_numfig_with_secnum_depth_warn(app, warning): (".//li/a/span", '^Sect.1 Foo$', True), ], 'foo.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1.1 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1.1.1 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1.1.2 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1.2.1 $', True), (".//table/caption/span[@class='caption-number']", '^Table 1.1 $', True), @@ -956,11 +956,11 @@ def test_numfig_with_secnum_depth_warn(app, warning): "span[@class='caption-number']", '^Listing 1.2.1 $', True), ], 'bar.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2.1.1 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2.1.3 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2.2.1 $', True), (".//table/caption/span[@class='caption-number']", '^Table 2.1.1 $', True), @@ -976,7 +976,7 @@ def test_numfig_with_secnum_depth_warn(app, warning): "span[@class='caption-number']", '^Listing 2.2.1 $', True), ], 'baz.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2.1.2 $', True), (".//table/caption/span[@class='caption-number']", '^Table 2.1.2 $', True), @@ -994,9 +994,9 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect): @pytest.mark.parametrize("fname,expect", flat_dict({ 'index.html': [ - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2 $', True), (".//table/caption/span[@class='caption-number']", '^Table 1 $', True), @@ -1016,13 +1016,13 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect): (".//li/a/span", '^Section.2.1$', True), (".//li/a/span", '^Fig.1 should be Fig.1$', True), (".//li/a/span", '^Sect.1 Foo$', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1.1 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1.2 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1.3 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1.4 $', True), (".//table/caption/span[@class='caption-number']", '^Table 1.1 $', True), @@ -1040,11 +1040,11 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect): "span[@class='caption-number']", '^Listing 1.3 $', True), (".//div[@class='code-block-caption']/" "span[@class='caption-number']", '^Listing 1.4 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2.1 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2.3 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2.4 $', True), (".//table/caption/span[@class='caption-number']", '^Table 2.1 $', True), @@ -1058,7 +1058,7 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect): "span[@class='caption-number']", '^Listing 2.3 $', True), (".//div[@class='code-block-caption']/" "span[@class='caption-number']", '^Listing 2.4 $', True), - (".//div[@class='figure']/p[@class='caption']/" + (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2.2 $', True), (".//table/caption/span[@class='caption-number']", '^Table 2.2 $', True), @@ -1076,12 +1076,12 @@ def test_numfig_with_singlehtml(app, cached_etree_parse, fname, expect): @pytest.mark.parametrize("fname,expect", flat_dict({ 'index.html': [ - (".//div[@class='figure']/p[@class='caption']/span[@class='caption-number']", - "Fig. 1", True), - (".//div[@class='figure']/p[@class='caption']/span[@class='caption-number']", - "Fig. 2", True), - (".//div[@class='figure']/p[@class='caption']/span[@class='caption-number']", - "Fig. 3", True), + (".//div[@class='figure align-center']/p[@class='caption']" + "/span[@class='caption-number']", "Fig. 1", True), + (".//div[@class='figure align-center']/p[@class='caption']" + "/span[@class='caption-number']", "Fig. 2", True), + (".//div[@class='figure align-center']/p[@class='caption']" + "/span[@class='caption-number']", "Fig. 3", True), (".//div//span[@class='caption-number']", "No.1 ", True), (".//div//span[@class='caption-number']", "No.2 ", True), (".//li/a/span", 'Fig. 1', True), diff --git a/tests/test_ext_autosummary.py b/tests/test_ext_autosummary.py index e1cbaf274..64c7df01c 100644 --- a/tests/test_ext_autosummary.py +++ b/tests/test_ext_autosummary.py @@ -203,7 +203,7 @@ def test_autosummary_latex_table_colspec(app, status, warning): result = (app.outdir / 'python.tex').text(encoding='utf8') print(status.getvalue()) print(warning.getvalue()) - assert r'\begin{longtable}{\X{1}{2}\X{1}{2}}' in result + assert r'\begin{longtable}[c]{\X{1}{2}\X{1}{2}}' in result assert r'p{0.5\linewidth}' not in result diff --git a/tests/test_ext_graphviz.py b/tests/test_ext_graphviz.py index 75a8717ea..6a3096c23 100644 --- a/tests/test_ext_graphviz.py +++ b/tests/test_ext_graphviz.py @@ -21,7 +21,7 @@ def test_graphviz_png_html(app, status, warning): app.builder.build_all() content = (app.outdir / 'index.html').text() - html = (r'
    \s*' + html = (r'
    \s*' r'
    \s*

    ' r'caption of graph.*

    \s*
    ') assert re.search(html, content, re.S) @@ -52,7 +52,7 @@ def test_graphviz_svg_html(app, status, warning): content = (app.outdir / 'index.html').text() - html = (r'
    \n' + html = (r'
    \n' r'
    \n' r'\s*

    digraph foo {\n' r'bar -> baz\n' diff --git a/tests/test_ext_inheritance_diagram.py b/tests/test_ext_inheritance_diagram.py index 76ef36a17..9e5d3e60f 100644 --- a/tests/test_ext_inheritance_diagram.py +++ b/tests/test_ext_inheritance_diagram.py @@ -23,7 +23,7 @@ def test_inheritance_diagram_png_html(app, status, warning): content = (app.outdir / 'index.html').text() - pattern = ('

    \n' + pattern = ('
    \n' '
    ' 'Inheritance diagram of test.Foo
    \n

    ' @@ -40,7 +40,7 @@ def test_inheritance_diagram_svg_html(app, status, warning): content = (app.outdir / 'index.html').text() - pattern = ('

    \n' + pattern = ('
    \n' '
    ' '\n' @@ -80,7 +80,7 @@ def test_inheritance_diagram_latex_alias(app, status, warning): content = (app.outdir / 'index.html').text() - pattern = ('
    \n' + pattern = ('
    \n' '
    ' 'Inheritance diagram of test.Foo
    \n

    ' From 1d5e3cba0e2c50d71f28a24361373af1d58c7766 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 9 Feb 2019 00:36:25 +0900 Subject: [PATCH 12/86] refactor: test-docutilsconf Use options for reST parser instead of HTML4 writer's. --- tests/roots/test-docutilsconf/conf.py | 4 -- tests/roots/test-docutilsconf/index.rst | 6 ++ tests/roots/test-docutilsconf/index.txt | 15 ----- tests/test_docutilsconf.py | 86 ++++--------------------- 4 files changed, 20 insertions(+), 91 deletions(-) create mode 100644 tests/roots/test-docutilsconf/index.rst delete mode 100644 tests/roots/test-docutilsconf/index.txt diff --git a/tests/roots/test-docutilsconf/conf.py b/tests/roots/test-docutilsconf/conf.py index d7f27e6e1..e69de29bb 100644 --- a/tests/roots/test-docutilsconf/conf.py +++ b/tests/roots/test-docutilsconf/conf.py @@ -1,4 +0,0 @@ -project = 'Sphinx docutils conf ' -source_suffix = '.txt' -keep_warnings = True -exclude_patterns = ['_build'] diff --git a/tests/roots/test-docutilsconf/index.rst b/tests/roots/test-docutilsconf/index.rst new file mode 100644 index 000000000..d292e3227 --- /dev/null +++ b/tests/roots/test-docutilsconf/index.rst @@ -0,0 +1,6 @@ +test-docutilsconf +================== + +Sphinx [1]_ + +.. [1] Python Documentation Generator diff --git a/tests/roots/test-docutilsconf/index.txt b/tests/roots/test-docutilsconf/index.txt deleted file mode 100644 index b20204e61..000000000 --- a/tests/roots/test-docutilsconf/index.txt +++ /dev/null @@ -1,15 +0,0 @@ -docutils conf -============= - -field-name-limit ----------------- - -:short: desc -:long long long long: long title - -option-limit ------------- - ---short short desc ---long-long-long-long long desc - diff --git a/tests/test_docutilsconf.py b/tests/test_docutilsconf.py index 57ae785db..a8cfde7ef 100644 --- a/tests/test_docutilsconf.py +++ b/tests/test_docutilsconf.py @@ -8,88 +8,30 @@ :license: BSD, see LICENSE for details. """ -import re - import pytest +from docutils import nodes -from sphinx.testing.path import path +from sphinx.testing.util import assert_node from sphinx.util.docutils import patch_docutils -def regex_count(expr, result): - return len(re.findall(expr, result)) - - -@pytest.mark.sphinx('html', testroot='docutilsconf', freshenv=True, docutilsconf='') +@pytest.mark.sphinx('dummy', testroot='docutilsconf') def test_html_with_default_docutilsconf(app, status, warning): with patch_docutils(app.confdir): - app.builder.build(['contents']) + app.build() - result = (app.outdir / 'index.html').text() - - assert regex_count(r'', result) == 1 - assert regex_count(r'', result) == 1 - assert regex_count(r'', result) == 1 - assert regex_count(r'', result) == 1 + doctree = app.env.get_doctree('index') + assert_node(doctree[0][1], [nodes.paragraph, ("Sphinx ", + [nodes.footnote_reference, "1"])]) -@pytest.mark.sphinx('html', testroot='docutilsconf', freshenv=True, docutilsconf=( - '\n[html4css1 writer]' - '\noption-limit:1' - '\nfield-name-limit:1' - '\n') -) +@pytest.mark.sphinx('dummy', testroot='docutilsconf', + docutilsconf=('[restructuredtext parser]\n' + 'trim_footnote_reference_space: true\n')) def test_html_with_docutilsconf(app, status, warning): with patch_docutils(app.confdir): - app.builder.build(['contents']) + app.build() - result = (app.outdir / 'index.html').text() - - assert regex_count(r'', result) == 0 - assert regex_count(r'', result) == 2 - assert regex_count(r'', result) == 0 - assert regex_count(r'', result) == 2 - - -@pytest.mark.sphinx('html', testroot='docutilsconf') -def test_html(app, status, warning): - with patch_docutils(app.confdir): - app.builder.build(['contents']) - assert warning.getvalue() == '' - - -@pytest.mark.sphinx('latex', testroot='docutilsconf') -def test_latex(app, status, warning): - with patch_docutils(app.confdir): - app.builder.build(['contents']) - assert warning.getvalue() == '' - - -@pytest.mark.sphinx('man', testroot='docutilsconf') -def test_man(app, status, warning): - with patch_docutils(app.confdir): - app.builder.build(['contents']) - assert warning.getvalue() == '' - - -@pytest.mark.sphinx('texinfo', testroot='docutilsconf') -def test_texinfo(app, status, warning): - with patch_docutils(app.confdir): - app.builder.build(['contents']) - - -@pytest.mark.sphinx('html', testroot='docutilsconf', - docutilsconf='[general]\nsource_link=true\n') -def test_docutils_source_link_with_nonascii_file(app, status, warning): - srcdir = path(app.srcdir) - mb_name = '\u65e5\u672c\u8a9e' - try: - (srcdir / (mb_name + '.txt')).write_text('') - except UnicodeEncodeError: - from sphinx.testing.path import FILESYSTEMENCODING - raise pytest.skip.Exception( - 'nonascii filename not supported on this filesystem encoding: ' - '%s', FILESYSTEMENCODING) - - with patch_docutils(app.confdir): - app.builder.build_all() + doctree = app.env.get_doctree('index') + assert_node(doctree[0][1], [nodes.paragraph, ("Sphinx", + [nodes.footnote_reference, "1"])]) From 47058cd1756ae8b4a0d0536d4b84ec628ade9099 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 9 Feb 2019 14:44:37 +0900 Subject: [PATCH 13/86] test: Give freshenv=True to test_docutilsconf for windows environment --- tests/test_docutilsconf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_docutilsconf.py b/tests/test_docutilsconf.py index a8cfde7ef..ab4778d7e 100644 --- a/tests/test_docutilsconf.py +++ b/tests/test_docutilsconf.py @@ -15,7 +15,7 @@ from sphinx.testing.util import assert_node from sphinx.util.docutils import patch_docutils -@pytest.mark.sphinx('dummy', testroot='docutilsconf') +@pytest.mark.sphinx('dummy', testroot='docutilsconf', freshenv=True) def test_html_with_default_docutilsconf(app, status, warning): with patch_docutils(app.confdir): app.build() @@ -25,7 +25,7 @@ def test_html_with_default_docutilsconf(app, status, warning): [nodes.footnote_reference, "1"])]) -@pytest.mark.sphinx('dummy', testroot='docutilsconf', +@pytest.mark.sphinx('dummy', testroot='docutilsconf', freshenv=True, docutilsconf=('[restructuredtext parser]\n' 'trim_footnote_reference_space: true\n')) def test_html_with_docutilsconf(app, status, warning): From 264a4e83805025046041a6adb7cd3e017423c858 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 26 Jan 2019 15:31:52 +0900 Subject: [PATCH 14/86] refactor: htmlhelp: Copy .stp file from template --- sphinx/builders/htmlhelp.py | 46 +++++++++++++-------------- sphinx/templates/htmlhelp/project.stp | 33 +++++++++++++++++++ 2 files changed, 55 insertions(+), 24 deletions(-) create mode 100644 sphinx/templates/htmlhelp/project.stp diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py index 582bbbb12..4a3ca98c4 100644 --- a/sphinx/builders/htmlhelp.py +++ b/sphinx/builders/htmlhelp.py @@ -17,11 +17,14 @@ from os import path from docutils import nodes from sphinx import addnodes +from sphinx import package_dir from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.environment.adapters.indexentries import IndexEntries from sphinx.locale import __ from sphinx.util import logging +from sphinx.util import progress_message +from sphinx.util.fileutil import copy_asset_file from sphinx.util.nodes import NodeMatcher from sphinx.util.osutil import make_filename_from_project @@ -34,6 +37,8 @@ if False: logger = logging.getLogger(__name__) +template_dir = path.join(package_dir, 'templates', 'htmlhelp') + # Project file (*.hhp) template. 'outname' is the file basename (like # the pythlp in pythlp.hhp); 'version' is the doc version number (like @@ -116,24 +121,6 @@ object_sitemap = '''\

    ''' -# List of words the full text search facility shouldn't index. This -# becomes file outname.stp. Note that this list must be pretty small! -# Different versions of the MS docs claim the file has a maximum size of -# 256 or 512 bytes (including \r\n at the end of each line). -# Note that "and", "or", "not" and "near" are operators in the search -# language, so no point indexing them even if we wanted to. -stopwords = """ -a and are as at -be but by -for -if in into is it -near no not -of on or -such -that the their then there these they this to -was will with -""".split() - # The following list includes only languages supported by Sphinx. See # https://docs.microsoft.com/en-us/previous-versions/windows/embedded/ms930130(v=msdn.10) # for more. @@ -234,6 +221,7 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): def handle_finish(self): # type: () -> None + self.copy_stopword_list() self.build_hhx(self.outdir, self.config.htmlhelp_basename) def write_doc(self, docname, doctree): @@ -245,14 +233,24 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): super().write_doc(docname, doctree) + @progress_message(__('copying stopword list')) + def copy_stopword_list(self): + # type: () -> None + """Copy a stopword list (.stp) to outdir. + + The stopword list contains a list of words the full text search facility + shouldn't index. Note that this list must be pretty small. Different + versions of the MS docs claim the file has a maximum size of 256 or 512 + bytes (including \r\n at the end of each line). Note that "and", "or", + "not" and "near" are operators in the search language, so no point + indexing them even if we wanted to. + """ + template = path.join(template_dir, 'project.stp') + filename = path.join(self.outdir, self.config.htmlhelp_basename + '.stp') + copy_asset_file(template, filename) + def build_hhx(self, outdir, outname): # type: (str, str) -> None - logger.info(__('dumping stopword list...')) - filename = path.join(outdir, outname + '.stp') - with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f: - for word in sorted(stopwords): - print(word, file=f) - logger.info(__('writing project file...')) filename = path.join(outdir, outname + '.hhp') with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f: diff --git a/sphinx/templates/htmlhelp/project.stp b/sphinx/templates/htmlhelp/project.stp new file mode 100644 index 000000000..bae1f90c4 --- /dev/null +++ b/sphinx/templates/htmlhelp/project.stp @@ -0,0 +1,33 @@ +a +and +are +as +at +be +but +by +for +if +in +into +is +it +near +no +not +of +on +or +such +that +the +their +then +there +these +they +this +to +was +will +with From 28b0b744b65e4bc3887cdc6a5898e92d86db07b7 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 26 Jan 2019 16:30:15 +0900 Subject: [PATCH 15/86] refactor: htmlhelp: Generate .hhp file from template --- sphinx/builders/htmlhelp.py | 76 ++++++++++++--------------- sphinx/templates/htmlhelp/project.hhp | 21 ++++++++ tests/test_build_htmlhelp.py | 23 ++++++++ 3 files changed, 77 insertions(+), 43 deletions(-) create mode 100644 sphinx/templates/htmlhelp/project.hhp diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py index 4a3ca98c4..a182c7632 100644 --- a/sphinx/builders/htmlhelp.py +++ b/sphinx/builders/htmlhelp.py @@ -26,7 +26,8 @@ from sphinx.util import logging from sphinx.util import progress_message from sphinx.util.fileutil import copy_asset_file from sphinx.util.nodes import NodeMatcher -from sphinx.util.osutil import make_filename_from_project +from sphinx.util.osutil import make_filename_from_project, relpath +from sphinx.util.template import SphinxRenderer if False: # For type annotation @@ -74,28 +75,6 @@ template_dir = path.join(package_dir, 'templates', 'htmlhelp') # 0x200000 TOC Next # 0x400000 TOC Prev -project_template = '''\ -[OPTIONS] -Binary TOC=No -Binary Index=No -Compiled file=%(outname)s.chm -Contents file=%(outname)s.hhc -Default Window=%(outname)s -Default topic=%(master_doc)s -Display compile progress=No -Full text search stop list file=%(outname)s.stp -Full-text search=Yes -Index file=%(outname)s.hhk -Language=%(lcid)#x -Title=%(title)s - -[WINDOWS] -%(outname)s="%(title)s","%(outname)s.hhc","%(outname)s.hhk",\ -"%(master_doc)s","%(master_doc)s",,,,,0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0 - -[FILES] -''' - contents_header = '''\ @@ -222,6 +201,7 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): def handle_finish(self): # type: () -> None self.copy_stopword_list() + self.build_project_file() self.build_hhx(self.outdir, self.config.htmlhelp_basename) def write_doc(self, docname, doctree): @@ -233,6 +213,11 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): super().write_doc(docname, doctree) + def render(self, name, context): + # type: (str, Dict) -> str + template = SphinxRenderer(template_dir) + return template.render(name, context) + @progress_message(__('copying stopword list')) def copy_stopword_list(self): # type: () -> None @@ -249,32 +234,37 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): filename = path.join(self.outdir, self.config.htmlhelp_basename + '.stp') copy_asset_file(template, filename) - def build_hhx(self, outdir, outname): - # type: (str, str) -> None - logger.info(__('writing project file...')) - filename = path.join(outdir, outname + '.hhp') + @progress_message(__('writing project file')) + def build_project_file(self): + # type: () -> None + """Create a project file (.hhp) on outdir.""" + # scan project files + project_files = [] # type: List[str] + for root, dirs, files in os.walk(self.outdir): + dirs.sort() + files.sort() + in_staticdir = root.startswith(path.join(self.outdir, '_static')) + for fn in sorted(files): + if (in_staticdir and not fn.endswith('.js')) or fn.endswith('.html'): + fn = relpath(path.join(root, fn), self.outdir) + project_files.append(fn.replace(os.sep, '\\')) + + filename = path.join(self.outdir, self.config.htmlhelp_basename + '.hhp') with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f: - f.write(project_template % { - 'outname': outname, + context = { + 'outname': self.config.htmlhelp_basename, 'title': self.config.html_title, 'version': self.config.version, 'project': self.config.project, 'lcid': self.lcid, - 'master_doc': self.config.master_doc + self.out_suffix - }) - if not outdir.endswith(os.sep): - outdir += os.sep - olen = len(outdir) - for root, dirs, files in os.walk(outdir): - dirs.sort() - files.sort() - staticdir = root.startswith(path.join(outdir, '_static')) - for fn in sorted(files): - if (staticdir and not fn.endswith('.js')) or \ - fn.endswith('.html'): - print(path.join(root, fn)[olen:].replace(os.sep, '\\'), - file=f) + 'master_doc': self.config.master_doc + self.out_suffix, + 'files': project_files, + } + body = self.render('project.hhp', context) + f.write(body) + def build_hhx(self, outdir, outname): + # type: (str, str) -> None logger.info(__('writing TOC file...')) filename = path.join(outdir, outname + '.hhc') with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f: diff --git a/sphinx/templates/htmlhelp/project.hhp b/sphinx/templates/htmlhelp/project.hhp new file mode 100644 index 000000000..b647b3713 --- /dev/null +++ b/sphinx/templates/htmlhelp/project.hhp @@ -0,0 +1,21 @@ +[OPTIONS] +Binary TOC=No +Binary Index=No +Compiled file={{ outname }}.chm +Contents file={{ outname }}.hhc +Default Window={{ outname }} +Default topic={{ master_doc }} +Display compile progress=No +Full text search stop list file={{ outname }}.stp +Full-text search=Yes +Index file={{ outname }}.hhk +Language={{ "%#x"|format(lcid) }} +Title={{ title }} + +[WINDOWS] +{{ outname }}="{{ title }}","{{ outname }}.hhc","{{ outname }}.hhk","{{ master_doc }}","{{ master_doc }}",,,,,0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0 + +[FILES] +{%- for filename in files %} +{{ filename }} +{%- endfor %} diff --git a/tests/test_build_htmlhelp.py b/tests/test_build_htmlhelp.py index 980a565e5..18acca921 100644 --- a/tests/test_build_htmlhelp.py +++ b/tests/test_build_htmlhelp.py @@ -18,6 +18,29 @@ from sphinx.builders.htmlhelp import default_htmlhelp_basename from sphinx.config import Config +@pytest.mark.sphinx('htmlhelp', testroot='basic') +def test_build_htmlhelp(app, status, warning): + app.build() + + hhp = (app.outdir / 'pythondoc.hhp').text() + assert 'Compiled file=pythondoc.chm' in hhp + assert 'Contents file=pythondoc.hhc' in hhp + assert 'Default Window=pythondoc' in hhp + assert 'Default topic=index.html' in hhp + assert 'Full text search stop list file=pythondoc.stp' in hhp + assert 'Index file=pythondoc.hhk' in hhp + assert 'Language=0x409' in hhp + assert 'Title=Python documentation' in hhp + assert ('pythondoc="Python documentation","pythondoc.hhc",' + '"pythondoc.hhk","index.html","index.html",,,,,' + '0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0' in hhp) + + files = ['genindex.html', 'index.html', '_static\\alabaster.css', '_static\\basic.css', + '_static\\custom.css', '_static\\file.png', '_static\\minus.png', + '_static\\plus.png', '_static\\pygments.css'] + assert '[FILES]\n%s' % '\n'.join(files) in hhp + + @pytest.mark.sphinx('htmlhelp', testroot='basic') def test_default_htmlhelp_file_suffix(app, warning): assert app.builder.out_suffix == '.html' From 7415f64eab0fa19e7466a5b2299e62dd23cad7e5 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 26 Jan 2019 17:45:07 +0900 Subject: [PATCH 16/86] refactor: htmlhelp: Generate .hhc file from template --- sphinx/builders/htmlhelp.py | 138 ++++++++++++++---------- sphinx/templates/htmlhelp/project.hhc | 31 ++++++ tests/roots/test-htmlhelp-hhc/bar.rst | 2 + tests/roots/test-htmlhelp-hhc/baz.rst | 2 + tests/roots/test-htmlhelp-hhc/conf.py | 1 + tests/roots/test-htmlhelp-hhc/foo.rst | 6 ++ tests/roots/test-htmlhelp-hhc/index.rst | 15 +++ tests/test_build_htmlhelp.py | 51 ++++++++- 8 files changed, 185 insertions(+), 61 deletions(-) create mode 100644 sphinx/templates/htmlhelp/project.hhc create mode 100644 tests/roots/test-htmlhelp-hhc/bar.rst create mode 100644 tests/roots/test-htmlhelp-hhc/baz.rst create mode 100644 tests/roots/test-htmlhelp-hhc/conf.py create mode 100644 tests/roots/test-htmlhelp-hhc/foo.rst create mode 100644 tests/roots/test-htmlhelp-hhc/index.rst diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py index a182c7632..ce30affba 100644 --- a/sphinx/builders/htmlhelp.py +++ b/sphinx/builders/htmlhelp.py @@ -75,24 +75,6 @@ template_dir = path.join(package_dir, 'templates', 'htmlhelp') # 0x200000 TOC Next # 0x400000 TOC Prev -contents_header = '''\ - - - - - - - - - - -
      -''' - -contents_footer = '''\ -
    -''' - object_sitemap = '''\ @@ -151,6 +133,63 @@ def chm_htmlescape(s, quote=True): return s +class ToCTreeVisitor(nodes.NodeVisitor): + def __init__(self, document): + # type: (nodes.document) -> None + super().__init__(document) + self.body = [] # type: List[str] + self.depth = 0 + + def append(self, text): + # type: (str) -> None + indent = ' ' * (self.depth - 1) + self.body.append(indent + text) + + def astext(self): + # type: () -> str + return '\n'.join(self.body) + + def unknown_visit(self, node): + # type: (nodes.Node) -> None + pass + + def unknown_departure(self, node): + # type: (nodes.Node) -> None + pass + + def visit_bullet_list(self, node): + # type: (nodes.Element) -> None + if self.depth > 0: + self.append('
      ') + + self.depth += 1 + + def depart_bullet_list(self, node): + # type: (nodes.Element) -> None + self.depth -= 1 + if self.depth > 0: + self.append('
    ') + + def visit_list_item(self, node): + # type: (nodes.Element) -> None + self.append('
  • ') + self.depth += 1 + + def depart_list_item(self, node): + # type: (nodes.Element) -> None + self.depth -= 1 + self.append('
  • ') + + def visit_reference(self, node): + # type: (nodes.Element) -> None + title = chm_htmlescape(node.astext(), True) + self.append('') + self.append(' ' % title) + self.append(' ' % node['refuri']) + self.append('') + raise nodes.SkipNode + + class HTMLHelpBuilder(StandaloneHTMLBuilder): """ Builder that also outputs Windows HTML help project, contents and @@ -202,6 +241,7 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): # type: () -> None self.copy_stopword_list() self.build_project_file() + self.build_toc_file() self.build_hhx(self.outdir, self.config.htmlhelp_basename) def write_doc(self, docname, doctree): @@ -263,48 +303,30 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): body = self.render('project.hhp', context) f.write(body) + @progress_message(__('writing TOC file')) + def build_toc_file(self): + # type: () -> None + """Create a ToC file (.hhp) on outdir.""" + filename = path.join(self.outdir, self.config.htmlhelp_basename + '.hhc') + with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f: + toctree = self.env.get_and_resolve_doctree(self.config.master_doc, self, + prune_toctrees=False) + visitor = ToCTreeVisitor(toctree) + matcher = NodeMatcher(addnodes.compact_paragraph, toctree=True) + for node in toctree.traverse(matcher): # type: addnodes.compact_paragraph + node.walkabout(visitor) + + context = { + 'body': visitor.astext(), + 'suffix': self.out_suffix, + 'short_title': self.config.html_short_title, + 'master_doc': self.config.master_doc, + 'domain_indices': self.domain_indices, + } + f.write(self.render('project.hhc', context)) + def build_hhx(self, outdir, outname): # type: (str, str) -> None - logger.info(__('writing TOC file...')) - filename = path.join(outdir, outname + '.hhc') - with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f: - f.write(contents_header) - # special books - f.write('
  • ' + object_sitemap % (self.config.html_short_title, - self.config.master_doc + self.out_suffix)) - for indexname, indexcls, content, collapse in self.domain_indices: - f.write('
  • ' + object_sitemap % (indexcls.localname, - '%s.html' % indexname)) - # the TOC - tocdoc = self.env.get_and_resolve_doctree( - self.config.master_doc, self, prune_toctrees=False) - - def write_toc(node, ullevel=0): - # type: (nodes.Node, int) -> None - if isinstance(node, nodes.list_item): - f.write('
  • ') - for subnode in node: - write_toc(subnode, ullevel) - elif isinstance(node, nodes.reference): - link = node['refuri'] - title = chm_htmlescape(node.astext(), True) - f.write(object_sitemap % (title, link)) - elif isinstance(node, nodes.bullet_list): - if ullevel != 0: - f.write('
      \n') - for subnode in node: - write_toc(subnode, ullevel + 1) - if ullevel != 0: - f.write('
    \n') - elif isinstance(node, addnodes.compact_paragraph): - for subnode in node: - write_toc(subnode, ullevel) - - matcher = NodeMatcher(addnodes.compact_paragraph, toctree=True) - for node in tocdoc.traverse(matcher): # type: addnodes.compact_paragraph - write_toc(node) - f.write(contents_footer) - logger.info(__('writing index file...')) index = IndexEntries(self.env).create_index(self) filename = path.join(outdir, outname + '.hhk') diff --git a/sphinx/templates/htmlhelp/project.hhc b/sphinx/templates/htmlhelp/project.hhc new file mode 100644 index 000000000..c1096e711 --- /dev/null +++ b/sphinx/templates/htmlhelp/project.hhc @@ -0,0 +1,31 @@ +{%- macro sitemap(name, docname) -%} + + + + +{%- endmacro -%} + + + + + + + + + + + + +
      +
    • + {{ sitemap(short_title, master_doc)|indent(8) }} +
    • + {%- for indexname, indexcls, content, collapse in domain_indices %} +
    • + {{ sitemap(indexcls.localname, indexname)|indent(8) }} +
    • + {%- endfor %} + {{ body|indent(6) }} +
    + + diff --git a/tests/roots/test-htmlhelp-hhc/bar.rst b/tests/roots/test-htmlhelp-hhc/bar.rst new file mode 100644 index 000000000..0d1785bcf --- /dev/null +++ b/tests/roots/test-htmlhelp-hhc/bar.rst @@ -0,0 +1,2 @@ +bar +--- diff --git a/tests/roots/test-htmlhelp-hhc/baz.rst b/tests/roots/test-htmlhelp-hhc/baz.rst new file mode 100644 index 000000000..69fe26493 --- /dev/null +++ b/tests/roots/test-htmlhelp-hhc/baz.rst @@ -0,0 +1,2 @@ +baz +--- diff --git a/tests/roots/test-htmlhelp-hhc/conf.py b/tests/roots/test-htmlhelp-hhc/conf.py new file mode 100644 index 000000000..20447e040 --- /dev/null +++ b/tests/roots/test-htmlhelp-hhc/conf.py @@ -0,0 +1 @@ +html_short_title = "Sphinx's documentation" diff --git a/tests/roots/test-htmlhelp-hhc/foo.rst b/tests/roots/test-htmlhelp-hhc/foo.rst new file mode 100644 index 000000000..6e06b7e69 --- /dev/null +++ b/tests/roots/test-htmlhelp-hhc/foo.rst @@ -0,0 +1,6 @@ +foo +--- + +.. toctree:: + + bar diff --git a/tests/roots/test-htmlhelp-hhc/index.rst b/tests/roots/test-htmlhelp-hhc/index.rst new file mode 100644 index 000000000..9b43b4129 --- /dev/null +++ b/tests/roots/test-htmlhelp-hhc/index.rst @@ -0,0 +1,15 @@ +test-htmlhelp-domain_indices +---------------------------- + +section +~~~~~~~ + +.. py:module:: sphinx + +subsection +^^^^^^^^^^ + +.. toctree:: + + foo + baz diff --git a/tests/test_build_htmlhelp.py b/tests/test_build_htmlhelp.py index 18acca921..4ad244a4e 100644 --- a/tests/test_build_htmlhelp.py +++ b/tests/test_build_htmlhelp.py @@ -11,10 +11,9 @@ import re import pytest +from html5lib import HTMLParser -from sphinx.builders.htmlhelp import chm_htmlescape - -from sphinx.builders.htmlhelp import default_htmlhelp_basename +from sphinx.builders.htmlhelp import chm_htmlescape, default_htmlhelp_basename from sphinx.config import Config @@ -72,6 +71,52 @@ def test_chm(app): assert m is None, 'Hex escaping exists in .hhk file: ' + str(m.group(0)) +@pytest.mark.sphinx('htmlhelp', testroot='htmlhelp-hhc') +def test_htmlhelp_hhc(app): + app.build() + + def assert_sitemap(node, name, filename): + assert node.tag == 'object' + assert len(node) == 2 + assert node[0].tag == 'param' + assert node[0].attrib == {'name': 'Name', 'value': name} + assert node[1].tag == 'param' + assert node[1].attrib == {'name': 'Local', 'value': filename} + + # .hhc file + hhc = (app.outdir / 'pythondoc.hhc').text() + tree = HTMLParser(namespaceHTMLElements=False).parse(hhc) + items = tree.find('.//body/ul') + assert len(items) == 4 + + # index + assert items[0].tag == 'li' + assert len(items[0]) == 1 + assert_sitemap(items[0][0], "Sphinx's documentation", 'index.html') + + # py-modindex + assert items[1].tag == 'li' + assert len(items[1]) == 1 + assert_sitemap(items[1][0], 'Python Module Index', 'py-modindex.html') + + # toctree + assert items[2].tag == 'li' + assert len(items[2]) == 2 + assert_sitemap(items[2][0], 'foo', 'foo.html') + + assert items[2][1].tag == 'ul' + assert len(items[2][1]) == 1 + assert items[2][1][0].tag == 'li' + assert_sitemap(items[2][1][0][0], 'bar', 'bar.html') + + assert items[3].tag == 'li' + assert len(items[3]) == 1 + assert_sitemap(items[3][0], 'baz', 'baz.html') + + # single quotes should be escaped as decimal (') + assert "Sphinx's documentation" in hhc + + def test_chm_htmlescape(): assert chm_htmlescape('Hello world') == 'Hello world' assert chm_htmlescape(u'Unicode 文字') == u'Unicode 文字' From 77b1d713a8d7b21ed6ad0f0a3d9f13a391b0a605 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 9 Feb 2019 01:13:35 +0900 Subject: [PATCH 17/86] quickstart: Simplify generated conf.py (language) --- sphinx/templates/quickstart/conf.py_t | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sphinx/templates/quickstart/conf.py_t b/sphinx/templates/quickstart/conf.py_t index 98af7febc..0a535a5b9 100644 --- a/sphinx/templates/quickstart/conf.py_t +++ b/sphinx/templates/quickstart/conf.py_t @@ -61,6 +61,7 @@ source_suffix = {{ suffix | repr }} master_doc = {{ master | repr }} {% endif -%} +{% if language -%} # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # @@ -68,6 +69,7 @@ master_doc = {{ master | repr }} # Usually you set "language" from the command line for these cases. language = {{ language | repr }} +{% endif -%} # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. From 319adefa9f7a1cb779f257bded7a32a610af76ee Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 9 Feb 2019 01:18:24 +0900 Subject: [PATCH 18/86] epub: ``epub_title`` defaults to the ``project`` option --- CHANGES | 1 + doc/usage/configuration.rst | 6 +++++- sphinx/builders/epub3.py | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index e266c50da..e52d25c10 100644 --- a/CHANGES +++ b/CHANGES @@ -65,6 +65,7 @@ Incompatible changes * LaTeX: graphics inclusion of oversized images rescales to not exceed the text width and height, even if width and/or height option were used. (refs: #5956) +* epub: ``epub_title`` defaults to the :confval:`project` option Deprecated ---------- diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index 8b82147c1..53e4e9fdd 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -1561,7 +1561,11 @@ the `Dublin Core metadata `_. .. confval:: epub_title The title of the document. It defaults to the :confval:`html_title` option - but can be set independently for epub creation. + but can be set independently for epub creation. It defaults to the + :confval:`project` option. + + .. versionchanged:: 2.0 + It defaults to the ``project`` option. .. confval:: epub_description diff --git a/sphinx/builders/epub3.py b/sphinx/builders/epub3.py index 83088bb45..f97d96396 100644 --- a/sphinx/builders/epub3.py +++ b/sphinx/builders/epub3.py @@ -269,7 +269,7 @@ def setup(app): app.add_config_value('epub_version', 3.0, 'epub') # experimental app.add_config_value('epub_theme', 'epub', 'epub') app.add_config_value('epub_theme_options', {}, 'epub') - app.add_config_value('epub_title', lambda self: self.html_title, 'epub') + app.add_config_value('epub_title', lambda self: self.project, 'epub') app.add_config_value('epub_author', lambda self: self.author, 'epub') app.add_config_value('epub_language', lambda self: self.language or 'en', 'epub') app.add_config_value('epub_publisher', lambda self: self.author, 'epub') From d08903d9b905c67f30549cd4cb93d66d585b879c Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 9 Feb 2019 01:22:53 +0900 Subject: [PATCH 19/86] quickstart: Simplify generated conf.py (EPUB) --- sphinx/templates/quickstart/conf.py_t | 18 ------------------ tests/test_build_epub.py | 9 ++++----- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/sphinx/templates/quickstart/conf.py_t b/sphinx/templates/quickstart/conf.py_t index 0a535a5b9..7529a1865 100644 --- a/sphinx/templates/quickstart/conf.py_t +++ b/sphinx/templates/quickstart/conf.py_t @@ -103,24 +103,6 @@ html_static_path = ['{{ dot }}static'] # 'searchbox.html']``. # # html_sidebars = {} - - -# -- Options for Epub output ------------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = project - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -# -# epub_identifier = '' - -# A unique identification for the text. -# -# epub_uid = '' - -# A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] {%- if extensions %} diff --git a/tests/test_build_epub.py b/tests/test_build_epub.py index 337363832..894810f99 100644 --- a/tests/test_build_epub.py +++ b/tests/test_build_epub.py @@ -70,7 +70,7 @@ def test_build_epub(app): # toc.ncx toc = EPUBElementTree.fromstring((app.outdir / 'toc.ncx').text()) - assert toc.find("./ncx:docTitle/ncx:text").text == 'Python documentation' + assert toc.find("./ncx:docTitle/ncx:text").text == 'Python' # toc.ncx / head meta = list(toc.find("./ncx:head")) @@ -94,7 +94,7 @@ def test_build_epub(app): # content.opf / metadata metadata = opf.find("./idpf:metadata") assert metadata.find("./dc:language").text == 'en' - assert metadata.find("./dc:title").text == 'Python documentation' + assert metadata.find("./dc:title").text == 'Python' assert metadata.find("./dc:description").text == 'unknown' assert metadata.find("./dc:creator").text == 'unknown' assert metadata.find("./dc:contributor").text == 'unknown' @@ -174,7 +174,7 @@ def test_nested_toc(app): # toc.ncx toc = EPUBElementTree.fromstring((app.outdir / 'toc.ncx').bytes()) - assert toc.find("./ncx:docTitle/ncx:text").text == 'Python documentation' + assert toc.find("./ncx:docTitle/ncx:text").text == 'Python' # toc.ncx / navPoint def navinfo(elem): @@ -229,8 +229,7 @@ def test_escaped_toc(app): # toc.ncx toc = EPUBElementTree.fromstring((app.outdir / 'toc.ncx').bytes()) - assert toc.find("./ncx:docTitle/ncx:text").text == ('need "escaped" ' - 'project documentation') + assert toc.find("./ncx:docTitle/ncx:text").text == 'need "escaped" project' # toc.ncx / navPoint def navinfo(elem): From d129f447c30263a49efefb813d2939238952dfd2 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 9 Feb 2019 01:36:28 +0900 Subject: [PATCH 20/86] quickstart: Simplify generated conf.py (version and release) --- sphinx/templates/quickstart/conf.py_t | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sphinx/templates/quickstart/conf.py_t b/sphinx/templates/quickstart/conf.py_t index 7529a1865..524f1671d 100644 --- a/sphinx/templates/quickstart/conf.py_t +++ b/sphinx/templates/quickstart/conf.py_t @@ -30,10 +30,16 @@ project = {{ project | repr }} copyright = {{ copyright | repr }} author = {{ author | repr }} +{%- if version %} + # The short X.Y version version = {{ version | repr }} +{%- endif %} +{%- if release %} + # The full version, including alpha/beta/rc tags release = {{ release | repr }} +{%- endif %} # -- General configuration --------------------------------------------------- From d4df28c643083f19fc662c99a0d423162755b165 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 9 Feb 2019 01:39:29 +0900 Subject: [PATCH 21/86] quickstart: Simplify generated conf.py (source_suffix) --- sphinx/templates/quickstart/conf.py_t | 2 ++ tests/test_quickstart.py | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/templates/quickstart/conf.py_t b/sphinx/templates/quickstart/conf.py_t index 524f1671d..d1ba65713 100644 --- a/sphinx/templates/quickstart/conf.py_t +++ b/sphinx/templates/quickstart/conf.py_t @@ -56,12 +56,14 @@ extensions = [ # Add any paths that contain templates here, relative to this directory. templates_path = ['{{ dot }}templates'] +{% if suffix != '.rst' -%} # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = {{ suffix | repr }} +{% endif -%} {% if master_doc != 'index' -%} # The master toctree document. master_doc = {{ master | repr }} diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py index 10e015fd9..f3563be50 100644 --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -112,7 +112,6 @@ def test_quickstart_defaults(tempdir): execfile_(conffile, ns) assert ns['extensions'] == [] assert ns['templates_path'] == ['_templates'] - assert ns['source_suffix'] == '.rst' assert ns['master_doc'] == 'index' assert ns['project'] == 'Sphinx Test' assert ns['copyright'] == '%s, Georg Brandl' % time.strftime('%Y') From 3791013c08f646712daa8692eba41f7f2f0fb1e4 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 9 Feb 2019 01:39:39 +0900 Subject: [PATCH 22/86] quickstart: Simplify generated conf.py (master_doc) --- sphinx/templates/quickstart/conf.py_t | 2 +- tests/test_quickstart.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/sphinx/templates/quickstart/conf.py_t b/sphinx/templates/quickstart/conf.py_t index d1ba65713..d5773881f 100644 --- a/sphinx/templates/quickstart/conf.py_t +++ b/sphinx/templates/quickstart/conf.py_t @@ -64,7 +64,7 @@ templates_path = ['{{ dot }}templates'] source_suffix = {{ suffix | repr }} {% endif -%} -{% if master_doc != 'index' -%} +{% if master != 'index' -%} # The master toctree document. master_doc = {{ master | repr }} diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py index f3563be50..bd7e2cb2f 100644 --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -112,7 +112,6 @@ def test_quickstart_defaults(tempdir): execfile_(conffile, ns) assert ns['extensions'] == [] assert ns['templates_path'] == ['_templates'] - assert ns['master_doc'] == 'index' assert ns['project'] == 'Sphinx Test' assert ns['copyright'] == '%s, Georg Brandl' % time.strftime('%Y') assert ns['version'] == '0.1' From 1a96f2b2cb54b16c01898d59574eb098789dc425 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 9 Feb 2019 15:21:53 +0900 Subject: [PATCH 23/86] Do test with docutils-0.12 --- .travis.yml | 9 +++++---- tests/test_build_epub.py | 4 ++++ tests/test_build_html5.py | 5 +++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index b7110f065..5a49bf106 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,13 +13,14 @@ matrix: include: - python: '3.5' env: - - TOXENV=du13 + - TOXENV=du12 - python: '3.6' env: - - TOXENV=py36 - - PYTEST_ADDOPTS="--cov ./ --cov-append --cov-config setup.cfg" + - TOXENV=du13 - python: '3.7' - env: TOXENV=py37 + env: + - TOXENV=py37 + - PYTEST_ADDOPTS="--cov ./ --cov-append --cov-config setup.cfg" - python: 'nightly' env: TOXENV=py38 - python: '3.6' diff --git a/tests/test_build_epub.py b/tests/test_build_epub.py index 337363832..acf325a9d 100644 --- a/tests/test_build_epub.py +++ b/tests/test_build_epub.py @@ -15,6 +15,8 @@ from xml.etree import ElementTree import pytest +from sphinx.util import docutils + # check given command is runnable def runnable(command): @@ -351,6 +353,8 @@ def test_epub_css_files(app): 'href="https://example.com/custom.css" />' not in content) +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') @pytest.mark.sphinx('epub', testroot='roles-download') def test_html_download_role(app, status, warning): app.build() diff --git a/tests/test_build_html5.py b/tests/test_build_html5.py index dcf0ffcdd..2c4ae517a 100644 --- a/tests/test_build_html5.py +++ b/tests/test_build_html5.py @@ -20,6 +20,7 @@ import pytest from html5lib import HTMLParser from test_build_html import flat_dict, tail_check, check_xpath +from sphinx.util import docutils from sphinx.util.docutils import is_html5_writer_available @@ -319,6 +320,8 @@ def test_html5_output(app, cached_etree_parse, fname, expect): check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect) +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') @pytest.mark.sphinx('html', tags=['testtag'], confoverrides={ 'html_context.hckey_co': 'hcval_co', 'html_experimental_html5_writer': True}) @@ -345,6 +348,8 @@ def test_html_download(app): assert matched.group(1) == filename +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') @pytest.mark.sphinx('html', testroot='roles-download', confoverrides={'html_experimental_html5_writer': True}) def test_html_download_role(app, status, warning): From 2f7e27810be576cd648b4c5d0837a9e19205aff6 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 10 Feb 2019 19:47:41 +0900 Subject: [PATCH 24/86] Fix #6046: LaTeX: ``TypeError`` is raised when invalid latex_elements given --- CHANGES | 1 + sphinx/builders/latex/__init__.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 2313f4ddf..ba2f15cb9 100644 --- a/CHANGES +++ b/CHANGES @@ -17,6 +17,7 @@ Bugs fixed ---------- * LaTeX: Remove extraneous space after author names on PDF title page (refs: #6004) +* #6046: LaTeX: ``TypeError`` is raised when invalid latex_elements given Testing -------- diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py index 3aba09b08..4ab408064 100644 --- a/sphinx/builders/latex/__init__.py +++ b/sphinx/builders/latex/__init__.py @@ -376,7 +376,7 @@ def validate_config_values(app, config): for key in list(config.latex_elements): if key not in DEFAULT_SETTINGS: msg = __("Unknown configure key: latex_elements[%r]. ignored.") - logger.warning(msg % key) + logger.warning(msg % (key,)) config.latex_elements.pop(key) From 9ecf45e6baa86dddfe38491ba5003b7ee9747e52 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 00:06:58 +0900 Subject: [PATCH 25/86] Fix #6026: LaTeX: A cross reference to definition list does not work --- CHANGES | 1 + sphinx/util/nodes.py | 9 +++++++++ sphinx/writers/latex.py | 12 +++++++++--- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 2313f4ddf..75e8da5ca 100644 --- a/CHANGES +++ b/CHANGES @@ -17,6 +17,7 @@ Bugs fixed ---------- * LaTeX: Remove extraneous space after author names on PDF title page (refs: #6004) +* #6026: LaTeX: A cross reference to definition list does not work Testing -------- diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 361150c93..1777f5843 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -238,6 +238,15 @@ def traverse_parent(node, cls=None): node = node.parent +def get_prev_node(node): + # type: (nodes.Node) -> nodes.Node + pos = node.parent.index(node) + if pos > 0: + return node.parent[pos - 1] + else: + return None + + def traverse_translatable_index(doctree): # type: (nodes.Node) -> Iterable[Tuple[nodes.Node, List[unicode]]] """Traverse translatable index node from a document tree.""" diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index c97554e6a..7cef81ff4 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -29,7 +29,7 @@ from sphinx.errors import SphinxError from sphinx.locale import admonitionlabels, _, __ from sphinx.util import split_into, logging from sphinx.util.i18n import format_date -from sphinx.util.nodes import clean_astext +from sphinx.util.nodes import clean_astext, get_prev_node from sphinx.util.template import LaTeXRenderer from sphinx.util.texescape import tex_escape_map, tex_replace_map @@ -1876,9 +1876,15 @@ class LaTeXTranslator(nodes.NodeVisitor): elif domain.get_enumerable_node_type(next_node) and domain.get_numfig_title(next_node): return - if 'refuri' in node or 'refid' in node or 'refname' in node: - # skip indirect targets (external hyperlink and internal links) + if 'refuri' in node: return + if node.get('refid'): + prev_node = get_prev_node(node) + if isinstance(prev_node, nodes.reference) and node['refid'] == prev_node['refid']: + # a target for a hyperlink reference having alias + pass + else: + add_target(node['refid']) for id in node['ids']: add_target(id) From a6ef8190cef63d74c3e1cef5546348ebd67035e6 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 01:46:38 +0900 Subject: [PATCH 26/86] Add testcase for mocked objects in autodoc --- tests/roots/test-ext-autodoc/target/need_mocks.py | 5 +++++ tests/test_autodoc.py | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/roots/test-ext-autodoc/target/need_mocks.py b/tests/roots/test-ext-autodoc/target/need_mocks.py index 4f83579a4..b8a661581 100644 --- a/tests/roots/test-ext-autodoc/target/need_mocks.py +++ b/tests/roots/test-ext-autodoc/target/need_mocks.py @@ -15,6 +15,11 @@ def decoratedFunction(): return None +def func(arg: missing_module.Class): + """a function takes mocked object as an argument""" + pass + + class TestAutodoc(object): """TestAutodoc docstring.""" @missing_name diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index a25a13639..a56226b42 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -1345,7 +1345,7 @@ def test_autofunction_for_callable(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_mocked_module_imports(app, warning): # no autodoc_mock_imports - options = {"members": 'TestAutodoc,decoratedFunction'} + options = {"members": 'TestAutodoc,decoratedFunction,func'} actual = do_autodoc(app, 'module', 'target.need_mocks', options) assert list(actual) == [] assert "autodoc: failed to import module 'need_mocks'" in warning.getvalue() @@ -1382,6 +1382,12 @@ def test_mocked_module_imports(app, warning): ' :module: target.need_mocks', '', ' decoratedFunction docstring', + ' ', + '', + '.. py:function:: func(arg: missing_module.Class)', + ' :module: target.need_mocks', + '', + ' a function takes mocked object as an argument', ' ' ] assert warning.getvalue() == '' From 0f6398efbb9f48f12bce02eb8a31d9f259605476 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 01:48:21 +0900 Subject: [PATCH 27/86] Close #5394: autodoc: Display readable names in type annotations for mocked objects --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index 3d0159f12..336b4ea7c 100644 --- a/CHANGES +++ b/CHANGES @@ -155,6 +155,7 @@ Features added * #4182: autodoc: Support :confval:`suppress_warnings` * #5533: autodoc: :confval:`autodoc_default_options` supports ``member-order`` +* #5394: autodoc: Display readable names in type annotations for mocked objects * #4018: htmlhelp: Add :confval:`htmlhelp_file_suffix` and :confval:`htmlhelp_link_suffix` * #5559: text: Support complex tables (colspan and rowspan) From b0148c69214c7f37001f53b73e024711b4eb1d49 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 02:03:09 +0900 Subject: [PATCH 28/86] Closes #5459: autodoc: autodoc_default_options accepts True as a value --- CHANGES | 1 + doc/usage/extensions/autodoc.rst | 9 ++++++--- sphinx/ext/autodoc/__init__.py | 2 +- tests/test_autodoc.py | 6 ++++++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 3d0159f12..47c58187c 100644 --- a/CHANGES +++ b/CHANGES @@ -155,6 +155,7 @@ Features added * #4182: autodoc: Support :confval:`suppress_warnings` * #5533: autodoc: :confval:`autodoc_default_options` supports ``member-order`` +* #5459: autodoc: :confval:`autodoc_default_options` accepts ``True`` as a value * #4018: htmlhelp: Add :confval:`htmlhelp_file_suffix` and :confval:`htmlhelp_link_suffix` * #5559: text: Support complex tables (colspan and rowspan) diff --git a/doc/usage/extensions/autodoc.rst b/doc/usage/extensions/autodoc.rst index 36d497543..45789e0a2 100644 --- a/doc/usage/extensions/autodoc.rst +++ b/doc/usage/extensions/autodoc.rst @@ -376,12 +376,12 @@ There are also new config values that you can set: 'members': 'var1, var2', 'member-order': 'bysource', 'special-members': '__init__', - 'undoc-members': None, + 'undoc-members': True, 'exclude-members': '__weakref__' } - Setting ``None`` is equivalent to giving the option name in the list format - (i.e. it means "yes/true/on"). + Setting ``None`` or ``True`` to the value is equivalent to giving only the + option name to the directives. The supported options are ``'members'``, ``'member-order'``, ``'undoc-members'``, ``'private-members'``, ``'special-members'``, @@ -390,6 +390,9 @@ There are also new config values that you can set: .. versionadded:: 1.8 + .. versionchanged:: 2.0 + Accepts ``True`` as a value. + .. confval:: autodoc_docstring_signature Functions imported from C modules cannot be introspected, and therefore the diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 86f80de8f..9e65e595d 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -72,7 +72,7 @@ INSTANCEATTR = object() def members_option(arg): # type: (Any) -> Union[object, List[str]] """Used to convert the :members: option to auto directives.""" - if arg is None: + if arg is None or arg is True: return ALL return [x.strip() for x in arg.split(',')] diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index a25a13639..5faa0c46b 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -1511,6 +1511,12 @@ def test_autodoc_default_options(app): assert ' .. py:attribute:: EnumCls.val1' in actual assert ' .. py:attribute:: EnumCls.val4' not in actual + # with :members: = True + app.config.autodoc_default_options = {'members': True} + actual = do_autodoc(app, 'class', 'target.enum.EnumCls') + assert ' .. py:attribute:: EnumCls.val1' in actual + assert ' .. py:attribute:: EnumCls.val4' not in actual + # with :members: and :undoc-members: app.config.autodoc_default_options = { 'members': None, From 33c8b1d9525c0e01dbd97557af74797d6ddb23dd Mon Sep 17 00:00:00 2001 From: Waleed Khan Date: Thu, 10 Jan 2019 19:42:20 -0800 Subject: [PATCH 29/86] githubpages: support custom domains Github Pages allows you to link a custom domain to your Github Pages site by adding a `CNAME` file at the top-level of your `docs` directory. The `githubpages` extension already inserts a `.nojekyll` file in the `docs` directory, so it's a good place to add support for this `CNAME` file as well. --- sphinx/ext/githubpages.py | 17 +++++++++++++---- tests/test_ext_githubpages.py | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/sphinx/ext/githubpages.py b/sphinx/ext/githubpages.py index f39d1cb58..5cecddd09 100644 --- a/sphinx/ext/githubpages.py +++ b/sphinx/ext/githubpages.py @@ -9,6 +9,7 @@ """ import os +import urllib import sphinx @@ -19,14 +20,22 @@ if False: from sphinx.environment import BuildEnvironment # NOQA -def create_nojekyll(app, env): +def create_nojekyll_and_cname(app, env): # type: (Sphinx, BuildEnvironment) -> None if app.builder.format == 'html': - path = os.path.join(app.builder.outdir, '.nojekyll') - open(path, 'wt').close() + open(os.path.join(app.builder.outdir, '.nojekyll'), 'wt').close() + + html_baseurl = app.config.html_baseurl + if html_baseurl: + domain = urllib.parse.urlparse(html_baseurl).hostname + if domain and not domain.endswith(".github.io"): + with open(os.path.join(app.builder.outdir, 'CNAME'), 'wt') as f: + # NOTE: don't write a trailing newline. The `CNAME` file that's + # auto-generated by the Github UI doesn't have one. + f.write(domain) def setup(app): # type: (Sphinx) -> Dict[str, Any] - app.connect('env-updated', create_nojekyll) + app.connect('env-updated', create_nojekyll_and_cname) return {'version': sphinx.__display_version__, 'parallel_read_safe': True} diff --git a/tests/test_ext_githubpages.py b/tests/test_ext_githubpages.py index 21b101b73..450a25498 100644 --- a/tests/test_ext_githubpages.py +++ b/tests/test_ext_githubpages.py @@ -15,3 +15,18 @@ import pytest def test_githubpages(app, status, warning): app.builder.build_all() assert (app.outdir / '.nojekyll').exists() + assert not (app.outdir / 'CNAME').exists() + + +@pytest.mark.sphinx('html', testroot='ext-githubpages', confoverrides={'html_baseurl': 'https://sphinx-doc.github.io'}) +def test_no_cname_for_github_io_domain(app, status, warning): + app.builder.build_all() + assert (app.outdir / '.nojekyll').exists() + assert not (app.outdir / 'CNAME').exists() + + +@pytest.mark.sphinx('html', testroot='ext-githubpages', confoverrides={'html_baseurl': 'https://sphinx-doc.org'}) +def test_cname_for_custom_domain(app, status, warning): + app.builder.build_all() + assert (app.outdir / '.nojekyll').exists() + assert (app.outdir / 'CNAME').text() == 'sphinx-doc.org' From 175a7e6212878b69bf34f391d8ba9f829d9da512 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 12:43:39 +0900 Subject: [PATCH 30/86] Update CHANGES for PR #5924 --- CHANGES | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index aa12ffe69..fb4fc6a93 100644 --- a/CHANGES +++ b/CHANGES @@ -179,7 +179,10 @@ Features added result links from changing their position when the search terminates. This makes navigating search results easier. * #5196: linkcheck also checks remote images exist - +* #5924: githubpages: create CNAME file for custom domains when + :confval:`html_baseurl` set + + Bugs fixed ---------- From 37ce1eb3b341ab118b2e12ee1fb70a2adc9b8f39 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 12:46:35 +0900 Subject: [PATCH 31/86] docs: githubpage now supports CNAME file --- doc/usage/extensions/githubpages.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/usage/extensions/githubpages.rst b/doc/usage/extensions/githubpages.rst index f19666ef1..ac99cc872 100644 --- a/doc/usage/extensions/githubpages.rst +++ b/doc/usage/extensions/githubpages.rst @@ -6,5 +6,11 @@ .. versionadded:: 1.4 +.. versionchaned:: 2.0 + Support ``CNAME`` file + This extension creates ``.nojekyll`` file on generated HTML directory to publish the document on GitHub Pages. + +It also creates a ``CNAME`` file for custom domains when :confval:`html_baseurl` +set. From 057ef31182453b488fe65a329b328545a2bcf753 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 13:25:24 +0900 Subject: [PATCH 32/86] Fix typo --- doc/usage/extensions/githubpages.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/usage/extensions/githubpages.rst b/doc/usage/extensions/githubpages.rst index ac99cc872..6d56a3036 100644 --- a/doc/usage/extensions/githubpages.rst +++ b/doc/usage/extensions/githubpages.rst @@ -6,7 +6,7 @@ .. versionadded:: 1.4 -.. versionchaned:: 2.0 +.. versionchanged:: 2.0 Support ``CNAME`` file This extension creates ``.nojekyll`` file on generated HTML directory to publish From 89c3c4ab5090f867cf49ce3c661d8459a6718cc4 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 10 Feb 2019 19:47:41 +0900 Subject: [PATCH 33/86] Fix #6046: LaTeX: ``TypeError`` is raised when invalid latex_elements given --- CHANGES | 1 + sphinx/builders/latex/__init__.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 2313f4ddf..ba2f15cb9 100644 --- a/CHANGES +++ b/CHANGES @@ -17,6 +17,7 @@ Bugs fixed ---------- * LaTeX: Remove extraneous space after author names on PDF title page (refs: #6004) +* #6046: LaTeX: ``TypeError`` is raised when invalid latex_elements given Testing -------- diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py index 3aba09b08..4ab408064 100644 --- a/sphinx/builders/latex/__init__.py +++ b/sphinx/builders/latex/__init__.py @@ -376,7 +376,7 @@ def validate_config_values(app, config): for key in list(config.latex_elements): if key not in DEFAULT_SETTINGS: msg = __("Unknown configure key: latex_elements[%r]. ignored.") - logger.warning(msg % key) + logger.warning(msg % (key,)) config.latex_elements.pop(key) From e0ca11605748d3905c1deae5cb4149c3d2d6bc6a Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 13:46:32 +0900 Subject: [PATCH 34/86] docs: Update location of builders --- doc/usage/builders/index.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/usage/builders/index.rst b/doc/usage/builders/index.rst index 5cc6df31c..b0a10483b 100644 --- a/doc/usage/builders/index.rst +++ b/doc/usage/builders/index.rst @@ -28,6 +28,7 @@ The builder's "name" must be given to the **-b** command-line option of .. autoattribute:: supported_image_types +.. module:: sphinx.builders.dirhtml .. class:: DirectoryHTMLBuilder This is a subclass of the standard HTML builder. Its output is a directory @@ -45,6 +46,7 @@ The builder's "name" must be given to the **-b** command-line option of .. versionadded:: 0.6 +.. module:: sphinx.builders.singlehtml .. class:: SingleFileHTMLBuilder This is an HTML builder that combines the whole project in one output file. From e799f94d24388fc54f33124cfd705f9b53dd3619 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 8 Jan 2019 01:00:30 +0900 Subject: [PATCH 35/86] Remove unused template variables --- sphinx/cmd/quickstart.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/sphinx/cmd/quickstart.py b/sphinx/cmd/quickstart.py index d7465b731..6639bf7e9 100644 --- a/sphinx/cmd/quickstart.py +++ b/sphinx/cmd/quickstart.py @@ -17,7 +17,6 @@ import time import warnings from collections import OrderedDict from os import path -from urllib.parse import quote # try to import readline, unix specific enhancement try: @@ -37,11 +36,10 @@ import sphinx.locale from sphinx import __display_version__, package_dir from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.locale import __ -from sphinx.util import texescape from sphinx.util.console import ( # type: ignore colorize, bold, red, turquoise, nocolor, color_terminal ) -from sphinx.util.osutil import ensuredir, make_filename +from sphinx.util.osutil import ensuredir from sphinx.util.template import SphinxRenderer if False: @@ -375,25 +373,15 @@ def generate(d, overwrite=True, silent=False, templatedir=None): """Generate project based on values in *d*.""" template = QuickstartRenderer(templatedir=templatedir) - texescape.init() - if 'mastertoctree' not in d: d['mastertoctree'] = '' if 'mastertocmaxdepth' not in d: d['mastertocmaxdepth'] = 2 - d['PY3'] = True - d['project_fn'] = make_filename(d['project']) - d['project_url'] = quote(d['project'].encode('idna')) - d['project_manpage'] = d['project_fn'].lower() d['now'] = time.asctime() d['project_underline'] = column_width(d['project']) * '=' d.setdefault('extensions', []) d['copyright'] = time.strftime('%Y') + ', ' + d['author'] - d['author_texescaped'] = d['author'].translate(texescape.tex_escape_map) - d['project_doc'] = d['project'] + ' Documentation' - d['project_doc_texescaped'] = (d['project'] + ' Documentation').\ - translate(texescape.tex_escape_map) ensuredir(d['path']) From 1c9142577635711356af1ce3788511981bafe334 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 8 Jan 2019 23:46:02 +0900 Subject: [PATCH 36/86] Skip escaping navpoint because it's HTML safe --- sphinx/builders/_epub_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/builders/_epub_base.py b/sphinx/builders/_epub_base.py index 5516fdaf8..5f7a00e3c 100644 --- a/sphinx/builders/_epub_base.py +++ b/sphinx/builders/_epub_base.py @@ -655,7 +655,7 @@ class EpubBuilder(StandaloneHTMLBuilder): if incr: self.playorder += 1 self.tocid += 1 - return NavPoint(self.esc('navPoint%d' % self.tocid), self.playorder, + return NavPoint('navPoint%d' % self.tocid, self.playorder, node['text'], node['refuri'], []) def build_navpoints(self, nodes): From e96ebc59aa794c970f0f2dec76826246bd019294 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 14:48:16 +0900 Subject: [PATCH 37/86] Fix typo --- sphinx/deprecation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/deprecation.py b/sphinx/deprecation.py index ea4099a93..e10ec8b32 100644 --- a/sphinx/deprecation.py +++ b/sphinx/deprecation.py @@ -48,7 +48,7 @@ class _ModuleWrapper(object): def __getattr__(self, name): # type: (str) -> Any if name in self._objects: - warnings.warn("%s.%s is now deprecated. Please refer CHANGES to grasp" + warnings.warn("%s.%s is now deprecated. Please refer CHANGES to grasp " "the changes of Sphinx API." % (self._modname, name), self._warning, stacklevel=3) return self._objects[name] From f82a26681468b70b82b90037e60db7bd5508a59e Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 19 Jan 2019 21:45:46 +0900 Subject: [PATCH 38/86] texinfo: refactor with progress_message() --- sphinx/builders/texinfo.py | 67 +++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/sphinx/builders/texinfo.py b/sphinx/builders/texinfo.py index d01ef68b6..2655d7a0b 100644 --- a/sphinx/builders/texinfo.py +++ b/sphinx/builders/texinfo.py @@ -22,8 +22,8 @@ from sphinx.environment import NoUri from sphinx.environment.adapters.asset import ImageAdapter from sphinx.locale import _, __ from sphinx.util import logging -from sphinx.util import status_iterator -from sphinx.util.console import bold, darkgreen # type: ignore +from sphinx.util import progress_message, status_iterator +from sphinx.util.console import darkgreen # type: ignore from sphinx.util.docutils import new_document from sphinx.util.fileutil import copy_asset_file from sphinx.util.nodes import inline_all_toctrees @@ -113,28 +113,27 @@ class TexinfoBuilder(Builder): destination = FileOutput( destination_path=path.join(self.outdir, targetname), encoding='utf-8') - logger.info(__("processing %s..."), targetname, nonl=True) - doctree = self.assemble_doctree( - docname, toctree_only, - appendices=(self.config.texinfo_appendices or [])) - logger.info(__("writing... "), nonl=True) - self.post_process_images(doctree) - docwriter = TexinfoWriter(self) - settings = OptionParser( - defaults=self.env.settings, - components=(docwriter,), - read_config_files=True).get_default_values() # type: Any - settings.author = author - settings.title = title - settings.texinfo_filename = targetname[:-5] + '.info' - settings.texinfo_elements = self.config.texinfo_elements - settings.texinfo_dir_entry = direntry or '' - settings.texinfo_dir_category = category or '' - settings.texinfo_dir_description = description or '' - settings.docname = docname - doctree.settings = settings - docwriter.write(doctree, destination) - logger.info(__("done")) + with progress_message(__("processing %s") % targetname): + appendices = self.config.texinfo_appendices or [] + doctree = self.assemble_doctree(docname, toctree_only, appendices=appendices) + + with progress_message(__("writing")): + self.post_process_images(doctree) + docwriter = TexinfoWriter(self) + settings = OptionParser( + defaults=self.env.settings, + components=(docwriter,), + read_config_files=True).get_default_values() # type: Any + settings.author = author + settings.title = title + settings.texinfo_filename = targetname[:-5] + '.info' + settings.texinfo_elements = self.config.texinfo_elements + settings.texinfo_dir_entry = direntry or '' + settings.texinfo_dir_category = category or '' + settings.texinfo_dir_description = description or '' + settings.docname = docname + doctree.settings = settings + docwriter.write(doctree, destination) def assemble_doctree(self, indexfile, toctree_only, appendices): # type: (str, bool, List[str]) -> nodes.document @@ -182,16 +181,7 @@ class TexinfoBuilder(Builder): def finish(self): # type: () -> None self.copy_image_files() - - logger.info(bold(__('copying Texinfo support files... ')), nonl=True) - # copy Makefile - fn = path.join(self.outdir, 'Makefile') - logger.info(fn, nonl=True) - try: - copy_asset_file(os.path.join(template_dir, 'Makefile'), fn) - except OSError as err: - logger.warning(__("error writing file %s: %s"), fn, err) - logger.info(__(' done')) + self.copy_support_files() def copy_image_files(self): # type: () -> None @@ -208,6 +198,15 @@ class TexinfoBuilder(Builder): logger.warning(__('cannot copy image file %r: %s'), path.join(self.srcdir, src), err) + def copy_support_files(self): + # type: () -> None + try: + with progress_message(__('copying Texinfo support files')): + logger.info('Makefile ', nonl=True) + copy_asset_file(os.path.join(template_dir, 'Makefile'), self.outdir) + except OSError as err: + logger.warning(__("error writing file Makefile: %s"), err) + def default_texinfo_documents(config): # type: (Config) -> List[Tuple[str, str, str, str, str, str, str]] From 81208ad704fabc2cbcd6afb71b16d46014ef6472 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 19 Jan 2019 22:23:45 +0900 Subject: [PATCH 39/86] manpage: refactor with progress_message() --- sphinx/builders/manpage.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sphinx/builders/manpage.py b/sphinx/builders/manpage.py index 6658adc38..214ed6afa 100644 --- a/sphinx/builders/manpage.py +++ b/sphinx/builders/manpage.py @@ -18,7 +18,8 @@ from sphinx.builders import Builder from sphinx.environment import NoUri from sphinx.locale import __ from sphinx.util import logging -from sphinx.util.console import bold, darkgreen # type: ignore +from sphinx.util import progress_message +from sphinx.util.console import darkgreen # type: ignore from sphinx.util.nodes import inline_all_toctrees from sphinx.util.osutil import make_filename_from_project from sphinx.writers.manpage import ManualPageWriter, ManualPageTranslator @@ -60,6 +61,7 @@ class ManualPageBuilder(Builder): return '' raise NoUri + @progress_message(__('writing')) def write(self, *ignored): # type: (Any) -> None docwriter = ManualPageWriter(self) @@ -68,8 +70,6 @@ class ManualPageBuilder(Builder): components=(docwriter,), read_config_files=True).get_default_values() # type: Any - logger.info(bold(__('writing... ')), nonl=True) - for info in self.config.man_pages: docname, name, description, authors, section = info if docname not in self.env.all_docs: @@ -105,7 +105,6 @@ class ManualPageBuilder(Builder): pendingnode.replace_self(pendingnode.children) docwriter.write(largetree, destination) - logger.info('') def finish(self): # type: () -> None From 66353b333067adfbd1471883ba1243e1b686f1fd Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 19 Jan 2019 22:34:56 +0900 Subject: [PATCH 40/86] latex: refactor with progress_message() --- sphinx/builders/latex/__init__.py | 72 ++++++++++++++++--------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py index 20dee64b4..42160487a 100644 --- a/sphinx/builders/latex/__init__.py +++ b/sphinx/builders/latex/__init__.py @@ -28,7 +28,7 @@ from sphinx.environment.adapters.asset import ImageAdapter from sphinx.errors import SphinxError from sphinx.locale import _, __ from sphinx.transforms import SphinxTransformer -from sphinx.util import texescape, logging, status_iterator +from sphinx.util import texescape, logging, progress_message, status_iterator from sphinx.util.console import bold, darkgreen # type: ignore from sphinx.util.docutils import SphinxFileOutput, new_document from sphinx.util.fileutil import copy_asset_file @@ -250,33 +250,32 @@ class LaTeXBuilder(Builder): toctree_only = entry[5] destination = SphinxFileOutput(destination_path=path.join(self.outdir, targetname), encoding='utf-8', overwrite_if_changed=True) - logger.info(__("processing %s..."), targetname, nonl=True) - toctrees = self.env.get_doctree(docname).traverse(addnodes.toctree) - if toctrees: - if toctrees[0].get('maxdepth') > 0: - tocdepth = toctrees[0].get('maxdepth') + with progress_message(__("processing %s") % targetname): + toctrees = self.env.get_doctree(docname).traverse(addnodes.toctree) + if toctrees: + if toctrees[0].get('maxdepth') > 0: + tocdepth = toctrees[0].get('maxdepth') + else: + tocdepth = None else: tocdepth = None - else: - tocdepth = None - doctree = self.assemble_doctree( - docname, toctree_only, - appendices=((docclass != 'howto') and self.config.latex_appendices or [])) - doctree['tocdepth'] = tocdepth - self.apply_transforms(doctree) - self.post_process_images(doctree) - self.update_doc_context(title, author) + doctree = self.assemble_doctree( + docname, toctree_only, + appendices=((docclass != 'howto') and self.config.latex_appendices or [])) + doctree['tocdepth'] = tocdepth + self.apply_transforms(doctree) + self.post_process_images(doctree) + self.update_doc_context(title, author) - logger.info(__("writing... "), nonl=True) - docsettings.author = author - docsettings.title = title - docsettings.contentsname = self.get_contentsname(docname) - docsettings.docname = docname - docsettings.docclass = docclass + with progress_message(__("writing")): + docsettings.author = author + docsettings.title = title + docsettings.contentsname = self.get_contentsname(docname) + docsettings.docname = docname + docsettings.docclass = docclass - doctree.settings = docsettings - docwriter.write(doctree, destination) - logger.info(__("done")) + doctree.settings = docsettings + docwriter.write(doctree, destination) def get_contentsname(self, indexfile): # type: (str) -> str @@ -354,8 +353,15 @@ class LaTeXBuilder(Builder): # type: () -> None self.copy_image_files() self.write_message_catalog() + self.copy_support_files() - # copy TeX support files from texinputs + if self.config.latex_additional_files: + self.copy_latex_additional_files() + + @progress_message(__('copying TeX support files')) + def copy_support_files(self): + # type: () -> None + """copy TeX support files from texinputs.""" # configure usage of xindy (impacts Makefile and latexmkrc) # FIXME: convert this rather to a confval with suitable default # according to language ? but would require extra documentation @@ -386,21 +392,19 @@ class LaTeXBuilder(Builder): copy_asset_file(path.join(staticdirname, 'Makefile_t'), self.outdir, context=context) - # copy additional files - if self.config.latex_additional_files: - logger.info(bold(__('copying additional files...')), nonl=True) - for filename in self.config.latex_additional_files: - logger.info(' ' + filename, nonl=True) - copy_asset_file(path.join(self.confdir, filename), self.outdir) - logger.info('') - # the logo is handled differently if self.config.latex_logo: if not path.isfile(path.join(self.confdir, self.config.latex_logo)): raise SphinxError(__('logo file %r does not exist') % self.config.latex_logo) else: copy_asset_file(path.join(self.confdir, self.config.latex_logo), self.outdir) - logger.info(__('done')) + + @progress_message(__('copying additional files')) + def copy_latex_additional_files(self): + # type: () -> None + for filename in self.config.latex_additional_files: + logger.info(' ' + filename, nonl=True) + copy_asset_file(path.join(self.confdir, filename), self.outdir) def copy_image_files(self): # type: () -> None From 53c30c4aff2b1880c8bac31761d958d5de01ec94 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 19 Jan 2019 22:50:13 +0900 Subject: [PATCH 41/86] Builder: refactor with progress_message() --- sphinx/builders/__init__.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index c8cd44e55..335880ef0 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -19,7 +19,7 @@ from sphinx.environment.adapters.asset import ImageAdapter from sphinx.errors import SphinxError from sphinx.io import read_doc from sphinx.locale import __ -from sphinx.util import i18n, import_object, logging, rst, status_iterator +from sphinx.util import i18n, import_object, logging, rst, progress_message, status_iterator from sphinx.util.build_phase import BuildPhase from sphinx.util.console import bold # type: ignore from sphinx.util.docutils import sphinx_domains @@ -351,16 +351,14 @@ class Builder: if updated_docnames: # save the environment from sphinx.application import ENV_PICKLE_FILENAME - logger.info(bold(__('pickling environment... ')), nonl=True) - with open(path.join(self.doctreedir, ENV_PICKLE_FILENAME), 'wb') as f: - pickle.dump(self.env, f, pickle.HIGHEST_PROTOCOL) - logger.info(__('done')) + with progress_message(__('pickling environment')): + with open(path.join(self.doctreedir, ENV_PICKLE_FILENAME), 'wb') as f: + pickle.dump(self.env, f, pickle.HIGHEST_PROTOCOL) # global actions self.app.phase = BuildPhase.CONSISTENCY_CHECK - logger.info(bold(__('checking consistency... ')), nonl=True) - self.env.check_consistency() - logger.info(__('done')) + with progress_message(__('checking consistency')): + self.env.check_consistency() else: if method == 'update' and not docnames: logger.info(bold(__('no targets are out of date.'))) @@ -559,9 +557,8 @@ class Builder: docnames.add(tocdocname) docnames.add(self.config.master_doc) - logger.info(bold(__('preparing documents... ')), nonl=True) - self.prepare_writing(docnames) - logger.info(__('done')) + with progress_message(__('preparing documents')): + self.prepare_writing(docnames) if self.parallel_ok: # number of subprocesses is parallel-1 because the main process From 3ffacc7db2374e7051262da79dbaf95e26a1c4a4 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 19 Jan 2019 23:22:23 +0900 Subject: [PATCH 42/86] singlehtml: refactor with progress_message() --- sphinx/builders/singlehtml.py | 46 +++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/sphinx/builders/singlehtml.py b/sphinx/builders/singlehtml.py index e5c7159c0..1ee5a37b1 100644 --- a/sphinx/builders/singlehtml.py +++ b/sphinx/builders/singlehtml.py @@ -16,7 +16,8 @@ from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.environment.adapters.toctree import TocTree from sphinx.locale import __ from sphinx.util import logging -from sphinx.util.console import bold, darkgreen # type: ignore +from sphinx.util import progress_message +from sphinx.util.console import darkgreen # type: ignore from sphinx.util.nodes import inline_all_toctrees if False: @@ -162,24 +163,32 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder): # type: (Any) -> None docnames = self.env.all_docs - logger.info(bold(__('preparing documents... ')), nonl=True) - self.prepare_writing(docnames) # type: ignore - logger.info(__('done')) + with progress_message(__('preparing documents')): + self.prepare_writing(docnames) # type: ignore - logger.info(bold(__('assembling single document... ')), nonl=True) - doctree = self.assemble_doctree() - self.env.toc_secnumbers = self.assemble_toc_secnumbers() - self.env.toc_fignumbers = self.assemble_toc_fignumbers() - logger.info('') - logger.info(bold(__('writing... ')), nonl=True) - self.write_doc_serialized(self.config.master_doc, doctree) - self.write_doc(self.config.master_doc, doctree) - logger.info(__('done')) + with progress_message(__('assembling single document')): + doctree = self.assemble_doctree() + self.env.toc_secnumbers = self.assemble_toc_secnumbers() + self.env.toc_fignumbers = self.assemble_toc_fignumbers() + + with progress_message(__('writing')): + self.write_doc_serialized(self.config.master_doc, doctree) + self.write_doc(self.config.master_doc, doctree) def finish(self): + # type: () -> None + self.write_additional_files() + self.copy_image_files() + self.copy_download_files() + self.copy_static_files() + self.copy_extra_files() + self.write_buildinfo() + self.dump_inventory() + + @progress_message(__('writing additional files')) + def write_additional_files(self): # type: () -> None # no indices or search pages are supported - logger.info(bold(__('writing additional files...')), nonl=True) # additional pages from conf.py for pagename, template in self.config.html_additional_pages.items(): @@ -191,15 +200,6 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder): fn = path.join(self.outdir, '_static', 'opensearch.xml') self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn) - logger.info('') - - self.copy_image_files() - self.copy_download_files() - self.copy_static_files() - self.copy_extra_files() - self.write_buildinfo() - self.dump_inventory() - def setup(app): # type: (Sphinx) -> Dict[str, Any] From 644d5558373d71268d0d8d3af760782a188e1e4c Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 7 Jan 2019 22:33:24 +0900 Subject: [PATCH 43/86] Don't import pycompat in sphinx.application (not used) --- sphinx/application.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sphinx/application.py b/sphinx/application.py index cce9926b5..dfeeea6dc 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -37,7 +37,6 @@ from sphinx.registry import SphinxComponentRegistry from sphinx.util import docutils from sphinx.util import import_object, progress_message from sphinx.util import logging -from sphinx.util import pycompat # noqa: F401 from sphinx.util.build_phase import BuildPhase from sphinx.util.console import bold # type: ignore from sphinx.util.docutils import directive_helper From d40ce68c7b2b02603725cf3072262ebcc82b40fe Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 7 Jan 2019 22:38:48 +0900 Subject: [PATCH 44/86] refactor: Move terminal_safe() to sphinx.util.console --- CHANGES | 1 + doc/extdev/index.rst | 5 +++++ sphinx/cmd/build.py | 3 +-- sphinx/util/console.py | 6 ++++++ sphinx/util/pycompat.py | 9 +++------ 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index 598db0b6a..b1f4bcd2b 100644 --- a/CHANGES +++ b/CHANGES @@ -124,6 +124,7 @@ Deprecated * ``sphinx.util.pycompat.UnicodeMixin`` * ``sphinx.util.pycompat.htmlescape`` * ``sphinx.util.pycompat.indent`` +* ``sphinx.util.pycompat.terminal_safe()`` * ``sphinx.util.pycompat.u`` * ``sphinx.writers.latex.ExtBabel`` * ``sphinx.writers.latex.LaTeXTranslator._make_visit_admonition()`` diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst index 95564936c..719fb3e66 100644 --- a/doc/extdev/index.rst +++ b/doc/extdev/index.rst @@ -440,6 +440,11 @@ The following is a list of deprecated interfaces. - 4.0 - ``textwrap.indent()`` + * - ``sphinx.util.pycompat.terminal_safe()`` + - 2.0 + - 4.0 + - ``sphinx.util.console.terminal_safe()`` + * - ``sphinx.util.pycompat.u`` - 2.0 - 4.0 diff --git a/sphinx/cmd/build.py b/sphinx/cmd/build.py index 1d4674b9e..6fd954296 100644 --- a/sphinx/cmd/build.py +++ b/sphinx/cmd/build.py @@ -23,9 +23,8 @@ from sphinx.application import Sphinx from sphinx.errors import SphinxError from sphinx.locale import __ from sphinx.util import Tee, format_exception_cut_frames, save_traceback -from sphinx.util.console import red, nocolor, color_terminal # type: ignore +from sphinx.util.console import red, nocolor, color_terminal, terminal_safe # type: ignore from sphinx.util.docutils import docutils_namespace, patch_docutils -from sphinx.util.pycompat import terminal_safe if False: # For type annotation diff --git a/sphinx/util/console.py b/sphinx/util/console.py index b419e1284..c207d32ac 100644 --- a/sphinx/util/console.py +++ b/sphinx/util/console.py @@ -27,6 +27,12 @@ _ansi_re = re.compile('\x1b\\[(\\d\\d;){0,2}\\d\\dm') codes = {} # type: Dict[str, str] +def terminal_safe(s): + # type: (str) -> str + """safely encode a string for printing to the terminal.""" + return s.encode('ascii', 'backslashreplace').decode('ascii') + + def get_terminal_width(): # type: () -> int """Borrowed from the py lib.""" diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index 5f02bd979..51697432e 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -17,6 +17,8 @@ import warnings from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias from sphinx.locale import __ from sphinx.util import logging +from sphinx.util.console import terminal_safe + if False: # For type annotation @@ -36,12 +38,6 @@ NoneType = type(None) sys_encoding = sys.getdefaultencoding() -# terminal_safe(): safely encode a string for printing to the terminal -def terminal_safe(s): - # type: (str) -> str - return s.encode('ascii', 'backslashreplace').decode('ascii') - - # convert_with_2to3(): # support for running 2to3 over config files def convert_with_2to3(filepath): @@ -102,6 +98,7 @@ deprecated_alias('sphinx.util.pycompat', 'TextIOWrapper': io.TextIOWrapper, 'htmlescape': html.escape, 'indent': textwrap.indent, + 'terminal_safe': terminal_safe, 'u': '', }, RemovedInSphinx40Warning) From b25deb259e55a1296332d32712676a2988d7fb06 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 7 Jan 2019 22:43:25 +0900 Subject: [PATCH 45/86] refactor: Move NoneType to sphinx.util.typing --- CHANGES | 1 + doc/extdev/index.rst | 5 +++++ sphinx/config.py | 3 ++- sphinx/util/inspect.py | 2 +- sphinx/util/pycompat.py | 5 ++--- sphinx/util/typing.py | 3 +++ 6 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index b1f4bcd2b..09465b56d 100644 --- a/CHANGES +++ b/CHANGES @@ -120,6 +120,7 @@ Deprecated * ``sphinx.util.osutil.EPIPE`` * ``sphinx.util.osutil.walk()`` * ``sphinx.util.PeekableIterator`` +* ``sphinx.util.pycompat.NoneType`` * ``sphinx.util.pycompat.TextIOWrapper`` * ``sphinx.util.pycompat.UnicodeMixin`` * ``sphinx.util.pycompat.htmlescape`` diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst index 719fb3e66..d650554d1 100644 --- a/doc/extdev/index.rst +++ b/doc/extdev/index.rst @@ -420,6 +420,11 @@ The following is a list of deprecated interfaces. - 4.0 - ``os.walk()`` + * - ``sphinx.util.pycompat.NoneType`` + - 2.0 + - 4.0 + - ``sphinx.util.typing.NoneType`` + * - ``sphinx.util.pycompat.TextIOWrapper`` - 2.0 - 4.0 diff --git a/sphinx/config.py b/sphinx/config.py index e4087385f..f844549da 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -22,7 +22,8 @@ from sphinx.locale import _, __ from sphinx.util import logging from sphinx.util.i18n import format_date from sphinx.util.osutil import cd -from sphinx.util.pycompat import execfile_, NoneType +from sphinx.util.pycompat import execfile_ +from sphinx.util.typing import NoneType if False: # For type annotation diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index d4c0f1948..89dd842e3 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -20,7 +20,7 @@ from io import StringIO from sphinx.deprecation import RemovedInSphinx30Warning from sphinx.util import logging -from sphinx.util.pycompat import NoneType +from sphinx.util.typing import NoneType if False: # For type annotation diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index 51697432e..ddacd0a8e 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -18,7 +18,7 @@ from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias from sphinx.locale import __ from sphinx.util import logging from sphinx.util.console import terminal_safe - +from sphinx.util.typing import NoneType if False: # For type annotation @@ -28,8 +28,6 @@ if False: logger = logging.getLogger(__name__) -NoneType = type(None) - # ------------------------------------------------------------------------------ # Python 2/3 compatibility @@ -95,6 +93,7 @@ def execfile_(filepath, _globals, open=open): deprecated_alias('sphinx.util.pycompat', { + 'NoneType': NoneType, # type: ignore 'TextIOWrapper': io.TextIOWrapper, 'htmlescape': html.escape, 'indent': textwrap.indent, diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index 6fb729458..78e8fe61f 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -20,6 +20,9 @@ DirectiveOption = Callable[[str], Any] # Text like nodes which are initialized with text and rawsource TextlikeNode = Union[nodes.Text, nodes.TextElement] +# type of None +NoneType = type(None) + # common role functions RoleFunction = Callable[[str, str, str, int, Inliner, Dict, List[str]], Tuple[List[nodes.Node], List[nodes.system_message]]] From 2efc1065c0dcd49e10973cc47cd1b569accd3b21 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 7 Jan 2019 22:49:48 +0900 Subject: [PATCH 46/86] Deprecate sphinx.util.pycompat:sys_encoding --- CHANGES | 1 + doc/extdev/index.rst | 5 +++++ sphinx/ext/imgmath.py | 6 +++--- sphinx/util/pycompat.py | 6 +----- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index 09465b56d..4c032f04c 100644 --- a/CHANGES +++ b/CHANGES @@ -125,6 +125,7 @@ Deprecated * ``sphinx.util.pycompat.UnicodeMixin`` * ``sphinx.util.pycompat.htmlescape`` * ``sphinx.util.pycompat.indent`` +* ``sphinx.util.pycompat.sys_encoding`` * ``sphinx.util.pycompat.terminal_safe()`` * ``sphinx.util.pycompat.u`` * ``sphinx.writers.latex.ExtBabel`` diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst index d650554d1..2bbcd49d3 100644 --- a/doc/extdev/index.rst +++ b/doc/extdev/index.rst @@ -445,6 +445,11 @@ The following is a list of deprecated interfaces. - 4.0 - ``textwrap.indent()`` + * - ``sphinx.util.pycompat.sys_encoding`` + - 2.0 + - 4.0 + - ``sys.getdefaultencoding()`` + * - ``sphinx.util.pycompat.terminal_safe()`` - 2.0 - 4.0 diff --git a/sphinx/ext/imgmath.py b/sphinx/ext/imgmath.py index 00aae13f2..3c6aca78b 100644 --- a/sphinx/ext/imgmath.py +++ b/sphinx/ext/imgmath.py @@ -12,6 +12,7 @@ import posixpath import re import shutil import subprocess +import sys import tempfile from hashlib import sha1 from os import path @@ -26,7 +27,6 @@ from sphinx.util import logging from sphinx.util.math import get_node_equation_number, wrap_displaymath from sphinx.util.osutil import ensuredir from sphinx.util.png import read_png_depth, write_png_depth -from sphinx.util.pycompat import sys_encoding if False: # For type annotation @@ -46,9 +46,9 @@ class MathExtError(SphinxError): def __init__(self, msg, stderr=None, stdout=None): # type: (str, bytes, bytes) -> None if stderr: - msg += '\n[stderr]\n' + stderr.decode(sys_encoding, 'replace') + msg += '\n[stderr]\n' + stderr.decode(sys.getdefaultencoding(), 'replace') if stdout: - msg += '\n[stdout]\n' + stdout.decode(sys_encoding, 'replace') + msg += '\n[stdout]\n' + stdout.decode(sys.getdefaultencoding(), 'replace') super().__init__(msg) diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index ddacd0a8e..fe5f0803e 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -31,11 +31,6 @@ logger = logging.getLogger(__name__) # ------------------------------------------------------------------------------ # Python 2/3 compatibility -# sys_encoding: some kind of default system encoding; should be used with -# a lenient error handler -sys_encoding = sys.getdefaultencoding() - - # convert_with_2to3(): # support for running 2to3 over config files def convert_with_2to3(filepath): @@ -98,6 +93,7 @@ deprecated_alias('sphinx.util.pycompat', 'htmlescape': html.escape, 'indent': textwrap.indent, 'terminal_safe': terminal_safe, + 'sys_encoding': sys.getdefaultencoding(), 'u': '', }, RemovedInSphinx40Warning) From ae13f943dbf492de6e0a1d29bcebff742bc58187 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 7 Jan 2019 22:53:13 +0900 Subject: [PATCH 47/86] Fix import order --- sphinx/search/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index c556adc42..dd30b5045 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -18,8 +18,8 @@ from docutils import nodes from sphinx import addnodes from sphinx import package_dir from sphinx.deprecation import RemovedInSphinx40Warning -from sphinx.util import jsdump, rpartition from sphinx.search.jssplitter import splitter_code +from sphinx.util import jsdump, rpartition if False: # For type annotation From 469e8afef7148c1ddd276585599699793356d848 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 16:48:43 +0900 Subject: [PATCH 48/86] docs: Use "Courier New" font to code blocks --- doc/_themes/sphinx13/static/sphinx13.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/_themes/sphinx13/static/sphinx13.css b/doc/_themes/sphinx13/static/sphinx13.css index 6315800f7..235bfdcc4 100644 --- a/doc/_themes/sphinx13/static/sphinx13.css +++ b/doc/_themes/sphinx13/static/sphinx13.css @@ -337,7 +337,7 @@ a tt:hover { } pre { - font-family: 'Consolas', 'DejaVu Sans Mono', + font-family: 'Consolas', 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; font-size: 13px; letter-spacing: 0.015em; From a3cdd465ecf018fa5213b6b2c1c4e495973a2896 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 3 Jan 2019 01:05:16 +0900 Subject: [PATCH 49/86] HTML builder outputs HTML5 by default --- CHANGES | 6 + doc/usage/configuration.rst | 6 + sphinx/builders/_epub_base.py | 2 - sphinx/builders/html.py | 24 +- tests/test_api_translator.py | 7 +- tests/test_build_html.py | 324 ++++++++++++++----------- tests/test_build_html5.py | 372 ----------------------------- tests/test_domain_cpp.py | 12 +- tests/test_ext_autosectionlabel.py | 26 +- tests/test_ext_todo.py | 32 +-- 10 files changed, 253 insertions(+), 558 deletions(-) delete mode 100644 tests/test_build_html5.py diff --git a/CHANGES b/CHANGES index 4c032f04c..eebe32a30 100644 --- a/CHANGES +++ b/CHANGES @@ -66,6 +66,7 @@ Incompatible changes the text width and height, even if width and/or height option were used. (refs: #5956) * #4550: All tables and figures without ``align`` option are displayed to center +* #4587: html: Output HTML5 by default Deprecated ---------- @@ -76,6 +77,10 @@ Deprecated ``EpubBuilder.build_container()``, ``EpubBuilder.bulid_content()``, ``EpubBuilder.build_toc()`` and ``EpubBuilder.build_epub()`` * The arguments of ``Epub3Builder.build_navigation_doc()`` +* The config variables + + - :confval:`html_experimental_html5_writer` + * The ``encoding`` argument of ``autodoc.Documenter.get_doc()``, ``autodoc.DocstringSignatureMixin.get_doc()``, ``autodoc.DocstringSignatureMixin._find_signature()``, and @@ -179,6 +184,7 @@ Features added * #4611: epub: Show warning for duplicated ToC entries * #1851: Allow to omit an argument for :rst:dir:`code-block` directive. If omitted, it follows :rst:dir:`highlight` or :confval:`highlight_language` +* #4587: html: Add :confval:`html4_writer` to use old HTML4 writer * #6016: HTML search: A placeholder for the search summary prevents search result links from changing their position when the search terminates. This makes navigating search results easier. diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index 8b82147c1..f0db65aad 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -1331,6 +1331,12 @@ that use Sphinx's HTMLWriter class. .. versionadded:: 1.6 + .. deprecated:: 2.0 + +.. confval:: html4_writer + + Output is processed with HTML4 writer. Default is ``False``. + Options for Single HTML output ------------------------------- diff --git a/sphinx/builders/_epub_base.py b/sphinx/builders/_epub_base.py index 5f7a00e3c..00fa7187d 100644 --- a/sphinx/builders/_epub_base.py +++ b/sphinx/builders/_epub_base.py @@ -134,8 +134,6 @@ class EpubBuilder(StandaloneHTMLBuilder): html_scaled_image_link = False # don't generate search index or include search page search = False - # use html5 translator by default - default_html5_translator = True coverpage_name = COVERPAGE_NAME toctree_template = TOCTREE_TEMPLATE diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 09ba8d677..8368b07db 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -18,7 +18,6 @@ import warnings from hashlib import md5 from os import path -import docutils from docutils import nodes from docutils.core import publish_parts from docutils.frontend import OptionParser @@ -58,7 +57,7 @@ if False: from sphinx.domains import Domain, Index, IndexEntry # NOQA from sphinx.util.tags import Tags # NOQA -# Experimental HTML5 Writer +# HTML5 Writer is avialable or not if is_html5_writer_available(): from sphinx.writers.html5 import HTML5Translator html5_ready = True @@ -242,8 +241,6 @@ class StandaloneHTMLBuilder(Builder): search = True # for things like HTML help and Apple help: suppress search use_index = False download_support = True # enable download role - # use html5 translator by default - default_html5_translator = False imgpath = None # type: str domain_indices = [] # type: List[Tuple[str, Type[Index], List[Tuple[str, List[IndexEntry]]], bool]] # NOQA @@ -285,11 +282,6 @@ class StandaloneHTMLBuilder(Builder): self.use_index = self.get_builder_config('use_index', 'html') - if self.config.html_experimental_html5_writer and not html5_ready: - logger.warning(__('html_experimental_html5_writer is set, but current version ' - 'is old. Docutils\' version should be 0.13 or newer, but %s.'), - docutils.__version__) - def create_build_info(self): # type: () -> BuildInfo return BuildInfo(self.config, self.tags, ['html']) @@ -374,14 +366,10 @@ class StandaloneHTMLBuilder(Builder): @property def default_translator_class(self): # type: ignore # type: () -> Type[nodes.NodeVisitor] - use_html5_writer = self.config.html_experimental_html5_writer - if use_html5_writer is None: - use_html5_writer = self.default_html5_translator - - if use_html5_writer and html5_ready: - return HTML5Translator - else: + if not html5_ready or self.config.html4_writer: return HTMLTranslator + else: + return HTML5Translator @property def math_renderer_name(self): @@ -562,7 +550,7 @@ class StandaloneHTMLBuilder(Builder): 'parents': [], 'logo': logo, 'favicon': favicon, - 'html5_doctype': self.config.html_experimental_html5_writer and html5_ready, + 'html5_doctype': html5_ready and not self.config.html4_writer } if self.theme: self.globalcontext.update( @@ -1440,9 +1428,9 @@ def setup(app): app.add_config_value('html_search_options', {}, 'html') app.add_config_value('html_search_scorer', '', None) app.add_config_value('html_scaled_image_link', True, 'html') - app.add_config_value('html_experimental_html5_writer', None, 'html') app.add_config_value('html_baseurl', '', 'html') app.add_config_value('html_math_renderer', None, 'env') + app.add_config_value('html4_writer', False, 'html') # event handlers app.connect('config-inited', convert_html_css_files) diff --git a/tests/test_api_translator.py b/tests/test_api_translator.py index 7e2767681..3e97cbc5a 100644 --- a/tests/test_api_translator.py +++ b/tests/test_api_translator.py @@ -12,6 +12,8 @@ import sys import pytest +from sphinx.util import docutils + @pytest.fixture(scope='module', autouse=True) def setup_module(rootdir): @@ -26,7 +28,10 @@ def test_html_translator(app, status, warning): # no set_translator() translator_class = app.builder.get_translator_class() assert translator_class - assert translator_class.__name__ == 'HTMLTranslator' + if docutils.__version_info__ < (0, 13): + assert translator_class.__name__ == 'HTMLTranslator' + else: + assert translator_class.__name__ == 'HTML5Translator' @pytest.mark.sphinx('html', testroot='api-set-translator') diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 514d0030c..77c86962e 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -10,6 +10,7 @@ import os import re +from hashlib import md5 from itertools import cycle, chain import pytest @@ -17,6 +18,7 @@ from html5lib import HTMLParser from sphinx.errors import ConfigError from sphinx.testing.util import strip_escseq +from sphinx.util import docutils from sphinx.util.inventory import InventoryFile @@ -128,6 +130,11 @@ def test_html_warnings(app, warning): '--- Got:\n' + html_warnings +@pytest.mark.sphinx('html', confoverrides={'html4_writer': True}) +def test_html4_output(app, status, warning): + app.build() + + @pytest.mark.parametrize("fname,expect", flat_dict({ 'images.html': [ (".//img[@src='_images/img.png']", ''), @@ -192,18 +199,18 @@ def test_html_warnings(app, warning): # an option list (".//span[@class='option']", '--help'), # admonitions - (".//p[@class='first admonition-title']", 'My Admonition'), - (".//p[@class='last']", 'Note text.'), - (".//p[@class='last']", 'Warning text.'), + (".//p[@class='admonition-title']", 'My Admonition'), + (".//div[@class='admonition note']/p", 'Note text.'), + (".//div[@class='admonition warning']/p", 'Warning text.'), # inline markup - (".//li/strong", r'^command\\n$'), - (".//li/strong", r'^program\\n$'), - (".//li/em", r'^dfn\\n$'), - (".//li/kbd", r'^kbd\\n$'), - (".//li/span", 'File \N{TRIANGULAR BULLET} Close'), - (".//li/code/span[@class='pre']", '^a/$'), - (".//li/code/em/span[@class='pre']", '^varpart$'), - (".//li/code/em/span[@class='pre']", '^i$'), + (".//li/p/strong", r'^command\\n$'), + (".//li/p/strong", r'^program\\n$'), + (".//li/p/em", r'^dfn\\n$'), + (".//li/p/kbd", r'^kbd\\n$'), + (".//li/p/span", 'File \N{TRIANGULAR BULLET} Close'), + (".//li/p/code/span[@class='pre']", '^a/$'), + (".//li/p/code/em/span[@class='pre']", '^varpart$'), + (".//li/p/code/em/span[@class='pre']", '^i$'), (".//a[@href='https://www.python.org/dev/peps/pep-0008']" "[@class='pep reference external']/strong", 'PEP 8'), (".//a[@href='https://www.python.org/dev/peps/pep-0008']" @@ -236,13 +243,13 @@ def test_html_warnings(app, warning): (".//div[@class='versionchanged']/p", 'Second paragraph of versionchanged'), # footnote reference - (".//a[@class='footnote-reference']", r'\[1\]'), + (".//a[@class='footnote-reference brackets']", r'1'), # created by reference lookup (".//a[@href='index.html#ref1']", ''), # ``seealso`` directive - (".//div/p[@class='first admonition-title']", 'See also'), + (".//div/p[@class='admonition-title']", 'See also'), # a ``hlist`` directive - (".//table[@class='hlist']/tbody/tr/td/ul/li", '^This$'), + (".//table[@class='hlist']/tbody/tr/td/ul/li/p", '^This$'), # a ``centered`` directive (".//p[@class='centered']/strong", 'LICENSE'), # a glossary @@ -261,10 +268,10 @@ def test_html_warnings(app, warning): # tests for numeric labels (".//a[@href='#id1'][@class='reference internal']/span", 'Testing various markup'), # tests for smartypants - (".//li", 'Smart “quotes” in English ‘text’.'), - (".//li", 'Smart — long and – short dashes.'), - (".//li", 'Ellipsis…'), - (".//li//code//span[@class='pre']", 'foo--"bar"...'), + (".//li/p", 'Smart “quotes” in English ‘text’.'), + (".//li/p", 'Smart — long and – short dashes.'), + (".//li/p", 'Ellipsis…'), + (".//li/p/code/span[@class='pre']", 'foo--"bar"...'), (".//p", 'Этот «абзац» должен использовать „русские“ кавычки.'), (".//p", 'Il dit : « C’est “super” ! »'), ], @@ -294,24 +301,24 @@ def test_html_warnings(app, warning): (".//li[@class='toctree-l1']/a[@href='markup.html']", 'Testing various markup'), # test unknown field names - (".//th[@class='field-name']", 'Field_name:'), - (".//th[@class='field-name']", 'Field_name all lower:'), - (".//th[@class='field-name']", 'FIELD_NAME:'), - (".//th[@class='field-name']", 'FIELD_NAME ALL CAPS:'), - (".//th[@class='field-name']", 'Field_Name:'), - (".//th[@class='field-name']", 'Field_Name All Word Caps:'), - (".//th[@class='field-name']", 'Field_name:'), - (".//th[@class='field-name']", 'Field_name First word cap:'), - (".//th[@class='field-name']", 'FIELd_name:'), - (".//th[@class='field-name']", 'FIELd_name PARTial caps:'), + (".//dt[@class='field-odd']", 'Field_name'), + (".//dt[@class='field-even']", 'Field_name all lower'), + (".//dt[@class='field-odd']", 'FIELD_NAME'), + (".//dt[@class='field-even']", 'FIELD_NAME ALL CAPS'), + (".//dt[@class='field-odd']", 'Field_Name'), + (".//dt[@class='field-even']", 'Field_Name All Word Caps'), + (".//dt[@class='field-odd']", 'Field_name'), + (".//dt[@class='field-even']", 'Field_name First word cap'), + (".//dt[@class='field-odd']", 'FIELd_name'), + (".//dt[@class='field-even']", 'FIELd_name PARTial caps'), # custom sidebar (".//h4", 'Custom sidebar'), # docfields - (".//td[@class='field-body']/strong", '^moo$'), - (".//td[@class='field-body']/strong", tail_check(r'\(Moo\) .* Moo')), - (".//td[@class='field-body']/ul/li/strong", '^hour$'), - (".//td[@class='field-body']/ul/li/em", '^DuplicateType$'), - (".//td[@class='field-body']/ul/li/em", tail_check(r'.* Some parameter')), + (".//dd[@class='field-odd']/p/strong", '^moo$'), + (".//dd[@class='field-odd']/p/strong", tail_check(r'\(Moo\) .* Moo')), + (".//dd[@class='field-odd']/ul/li/p/strong", '^hour$'), + (".//dd[@class='field-odd']/ul/li/p/em", '^DuplicateType$'), + (".//dd[@class='field-odd']/ul/li/p/em", tail_check(r'.* Some parameter')), # others (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-p']/code/span", 'perl'), @@ -340,17 +347,17 @@ def test_html_warnings(app, warning): 'index.html': [ (".//meta[@name='hc'][@content='hcval']", ''), (".//meta[@name='hc_co'][@content='hcval_co']", ''), - (".//td[@class='label']", r'\[Ref1\]'), - (".//td[@class='label']", ''), + (".//dt[@class='label']/span[@class='brackets']", r'Ref1'), + (".//dt[@class='label']", ''), (".//li[@class='toctree-l1']/a", 'Testing various markup'), (".//li[@class='toctree-l2']/a", 'Inline markup'), (".//title", 'Sphinx '), (".//div[@class='footer']", 'Georg Brandl & Team'), (".//a[@href='http://python.org/']" "[@class='reference external']", ''), - (".//li/a[@href='genindex.html']/span", 'Index'), - (".//li/a[@href='py-modindex.html']/span", 'Module Index'), - (".//li/a[@href='search.html']/span", 'Search Page'), + (".//li/p/a[@href='genindex.html']/span", 'Index'), + (".//li/p/a[@href='py-modindex.html']/span", 'Module Index'), + (".//li/p/a[@href='search.html']/span", 'Search Page'), # custom sidebar only for contents (".//h4", 'Contents sidebar'), # custom JavaScript @@ -381,37 +388,41 @@ def test_html_warnings(app, warning): (".//li/a", "double"), ], 'footnote.html': [ - (".//a[@class='footnote-reference'][@href='#id9'][@id='id1']", r"\[1\]"), - (".//a[@class='footnote-reference'][@href='#id10'][@id='id2']", r"\[2\]"), - (".//a[@class='footnote-reference'][@href='#foo'][@id='id3']", r"\[3\]"), + (".//a[@class='footnote-reference brackets'][@href='#id9'][@id='id1']", r"1"), + (".//a[@class='footnote-reference brackets'][@href='#id10'][@id='id2']", r"2"), + (".//a[@class='footnote-reference brackets'][@href='#foo'][@id='id3']", r"3"), (".//a[@class='reference internal'][@href='#bar'][@id='id4']", r"\[bar\]"), (".//a[@class='reference internal'][@href='#baz-qux'][@id='id5']", r"\[baz_qux\]"), - (".//a[@class='footnote-reference'][@href='#id11'][@id='id6']", r"\[4\]"), - (".//a[@class='footnote-reference'][@href='#id12'][@id='id7']", r"\[5\]"), - (".//a[@class='fn-backref'][@href='#id1']", r"\[1\]"), - (".//a[@class='fn-backref'][@href='#id2']", r"\[2\]"), - (".//a[@class='fn-backref'][@href='#id3']", r"\[3\]"), - (".//a[@class='fn-backref'][@href='#id4']", r"\[bar\]"), - (".//a[@class='fn-backref'][@href='#id5']", r"\[baz_qux\]"), - (".//a[@class='fn-backref'][@href='#id6']", r"\[4\]"), - (".//a[@class='fn-backref'][@href='#id7']", r"\[5\]"), - (".//a[@class='fn-backref'][@href='#id8']", r"\[6\]"), + (".//a[@class='footnote-reference brackets'][@href='#id11'][@id='id6']", r"4"), + (".//a[@class='footnote-reference brackets'][@href='#id12'][@id='id7']", r"5"), + (".//a[@class='fn-backref'][@href='#id1']", r"1"), + (".//a[@class='fn-backref'][@href='#id2']", r"2"), + (".//a[@class='fn-backref'][@href='#id3']", r"3"), + (".//a[@class='fn-backref'][@href='#id4']", r"bar"), + (".//a[@class='fn-backref'][@href='#id5']", r"baz_qux"), + (".//a[@class='fn-backref'][@href='#id6']", r"4"), + (".//a[@class='fn-backref'][@href='#id7']", r"5"), + (".//a[@class='fn-backref'][@href='#id8']", r"6"), ], 'otherext.html': [ (".//h1", "Generated section"), (".//a[@href='_sources/otherext.foo.txt']", ''), ] })) -@pytest.mark.sphinx('html', tags=['testtag'], confoverrides={ - 'html_context.hckey_co': 'hcval_co'}) +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') +@pytest.mark.sphinx('html', tags=['testtag'], + confoverrides={'html_context.hckey_co': 'hcval_co'}) @pytest.mark.test_params(shared_result='test_build_html_output') -def test_html_output(app, cached_etree_parse, fname, expect): +def test_html5_output(app, cached_etree_parse, fname, expect): app.build() + print(app.outdir / fname) check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect) -@pytest.mark.sphinx('html', tags=['testtag'], confoverrides={ - 'html_context.hckey_co': 'hcval_co'}) +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') +@pytest.mark.sphinx('html') @pytest.mark.test_params(shared_result='test_build_html_output') def test_html_download(app): app.build() @@ -435,6 +446,29 @@ def test_html_download(app): assert matched.group(1) == filename +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') +@pytest.mark.sphinx('html', testroot='roles-download') +def test_html_download_role(app, status, warning): + app.build() + digest = md5((app.srcdir / 'dummy.dat').encode()).hexdigest() + assert (app.outdir / '_downloads' / digest / 'dummy.dat').exists() + + content = (app.outdir / 'index.html').text() + assert (('
  • ' + '' + 'dummy.dat

  • ' % digest) + in content) + assert ('
  • ' + 'not_found.dat

  • ' in content) + assert ('
  • ' + '' + 'Sphinx logo' + '

  • ' in content) + + @pytest.mark.sphinx('html', testroot='build-html-translator') def test_html_translator(app): app.build() @@ -473,6 +507,8 @@ def test_html_translator(app): (".//h1", '2.1.1. Baz A', True), ], })) +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') @pytest.mark.sphinx('html', testroot='tocdepth') @pytest.mark.test_params(shared_result='test_build_html_tocdepth') def test_tocdepth(app, cached_etree_parse, fname, expect): @@ -508,6 +544,8 @@ def test_tocdepth(app, cached_etree_parse, fname, expect): (".//h4", '2.1.1. Baz A', True), ], })) +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') @pytest.mark.sphinx('singlehtml', testroot='tocdepth') @pytest.mark.test_params(shared_result='test_build_html_tocdepth') def test_tocdepth_singlehtml(app, cached_etree_parse, fname, expect): @@ -532,16 +570,16 @@ def test_numfig_disabled_warn(app, warning): (".//table/caption/span[@class='caption-number']", None, True), (".//div[@class='code-block-caption']/" "span[@class='caption-number']", None, True), - (".//li/code/span", '^fig1$', True), - (".//li/code/span", '^Figure%s$', True), - (".//li/code/span", '^table-1$', True), - (".//li/code/span", '^Table:%s$', True), - (".//li/code/span", '^CODE_1$', True), - (".//li/code/span", '^Code-%s$', True), - (".//li/a/span", '^Section 1$', True), - (".//li/a/span", '^Section 2.1$', True), - (".//li/code/span", '^Fig.{number}$', True), - (".//li/a/span", '^Sect.1 Foo$', True), + (".//li/p/code/span", '^fig1$', True), + (".//li/p/code/span", '^Figure%s$', True), + (".//li/p/code/span", '^table-1$', True), + (".//li/p/code/span", '^Table:%s$', True), + (".//li/p/code/span", '^CODE_1$', True), + (".//li/p/code/span", '^Code-%s$', True), + (".//li/p/a/span", '^Section 1$', True), + (".//li/p/a/span", '^Section 2.1$', True), + (".//li/p/code/span", '^Fig.{number}$', True), + (".//li/p/a/span", '^Sect.1 Foo$', True), ], 'foo.html': [ (".//div[@class='figure align-center']/p[@class='caption']/" @@ -565,6 +603,8 @@ def test_numfig_disabled_warn(app, warning): "span[@class='caption-number']", None, True), ], })) +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') @pytest.mark.sphinx('html', testroot='numfig') @pytest.mark.test_params(shared_result='test_build_html_numfig') def test_numfig_disabled(app, cached_etree_parse, fname, expect): @@ -605,16 +645,16 @@ def test_numfig_without_numbered_toctree_warn(app, warning): "span[@class='caption-number']", '^Listing 9 $', True), (".//div[@class='code-block-caption']/" "span[@class='caption-number']", '^Listing 10 $', True), - (".//li/a/span", '^Fig. 9$', True), - (".//li/a/span", '^Figure6$', True), - (".//li/a/span", '^Table 9$', True), - (".//li/a/span", '^Table:6$', True), - (".//li/a/span", '^Listing 9$', True), - (".//li/a/span", '^Code-6$', True), - (".//li/code/span", '^foo$', True), - (".//li/code/span", '^bar_a$', True), - (".//li/a/span", '^Fig.9 should be Fig.1$', True), - (".//li/code/span", '^Sect.{number}$', True), + (".//li/p/a/span", '^Fig. 9$', True), + (".//li/p/a/span", '^Figure6$', True), + (".//li/p/a/span", '^Table 9$', True), + (".//li/p/a/span", '^Table:6$', True), + (".//li/p/a/span", '^Listing 9$', True), + (".//li/p/a/span", '^Code-6$', True), + (".//li/p/code/span", '^foo$', True), + (".//li/p/code/span", '^bar_a$', True), + (".//li/p/a/span", '^Fig.9 should be Fig.1$', True), + (".//li/p/code/span", '^Sect.{number}$', True), ], 'foo.html': [ (".//div[@class='figure align-center']/p[@class='caption']/" @@ -671,6 +711,8 @@ def test_numfig_without_numbered_toctree_warn(app, warning): "span[@class='caption-number']", '^Listing 6 $', True), ], })) +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') @pytest.mark.sphinx( 'html', testroot='numfig', srcdir='test_numfig_without_numbered_toctree', @@ -711,16 +753,16 @@ def test_numfig_with_numbered_toctree_warn(app, warning): "span[@class='caption-number']", '^Listing 1 $', True), (".//div[@class='code-block-caption']/" "span[@class='caption-number']", '^Listing 2 $', True), - (".//li/a/span", '^Fig. 1$', True), - (".//li/a/span", '^Figure2.2$', True), - (".//li/a/span", '^Table 1$', True), - (".//li/a/span", '^Table:2.2$', True), - (".//li/a/span", '^Listing 1$', True), - (".//li/a/span", '^Code-2.2$', True), - (".//li/a/span", '^Section.1$', True), - (".//li/a/span", '^Section.2.1$', True), - (".//li/a/span", '^Fig.1 should be Fig.1$', True), - (".//li/a/span", '^Sect.1 Foo$', True), + (".//li/p/a/span", '^Fig. 1$', True), + (".//li/p/a/span", '^Figure2.2$', True), + (".//li/p/a/span", '^Table 1$', True), + (".//li/p/a/span", '^Table:2.2$', True), + (".//li/p/a/span", '^Listing 1$', True), + (".//li/p/a/span", '^Code-2.2$', True), + (".//li/p/a/span", '^Section.1$', True), + (".//li/p/a/span", '^Section.2.1$', True), + (".//li/p/a/span", '^Fig.1 should be Fig.1$', True), + (".//li/p/a/span", '^Sect.1 Foo$', True), ], 'foo.html': [ (".//div[@class='figure align-center']/p[@class='caption']/" @@ -777,6 +819,8 @@ def test_numfig_with_numbered_toctree_warn(app, warning): "span[@class='caption-number']", '^Listing 2.2 $', True), ], })) +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') @pytest.mark.sphinx('html', testroot='numfig', confoverrides={'numfig': True}) @pytest.mark.test_params(shared_result='test_build_html_numfig_on') def test_numfig_with_numbered_toctree(app, cached_etree_parse, fname, expect): @@ -814,16 +858,16 @@ def test_numfig_with_prefix_warn(app, warning): "span[@class='caption-number']", '^Code-1 $', True), (".//div[@class='code-block-caption']/" "span[@class='caption-number']", '^Code-2 $', True), - (".//li/a/span", '^Figure:1$', True), - (".//li/a/span", '^Figure2.2$', True), - (".//li/a/span", '^Tab_1$', True), - (".//li/a/span", '^Table:2.2$', True), - (".//li/a/span", '^Code-1$', True), - (".//li/a/span", '^Code-2.2$', True), - (".//li/a/span", '^SECTION-1$', True), - (".//li/a/span", '^SECTION-2.1$', True), - (".//li/a/span", '^Fig.1 should be Fig.1$', True), - (".//li/a/span", '^Sect.1 Foo$', True), + (".//li/p/a/span", '^Figure:1$', True), + (".//li/p/a/span", '^Figure2.2$', True), + (".//li/p/a/span", '^Tab_1$', True), + (".//li/p/a/span", '^Table:2.2$', True), + (".//li/p/a/span", '^Code-1$', True), + (".//li/p/a/span", '^Code-2.2$', True), + (".//li/p/a/span", '^SECTION-1$', True), + (".//li/p/a/span", '^SECTION-2.1$', True), + (".//li/p/a/span", '^Fig.1 should be Fig.1$', True), + (".//li/p/a/span", '^Sect.1 Foo$', True), ], 'foo.html': [ (".//div[@class='figure align-center']/p[@class='caption']/" @@ -880,20 +924,22 @@ def test_numfig_with_prefix_warn(app, warning): "span[@class='caption-number']", '^Code-2.2 $', True), ], })) -@pytest.mark.sphinx('html', testroot='numfig', confoverrides={ - 'numfig': True, - 'numfig_format': {'figure': 'Figure:%s', - 'table': 'Tab_%s', - 'code-block': 'Code-%s', - 'section': 'SECTION-%s'}}) +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') +@pytest.mark.sphinx('html', testroot='numfig', + confoverrides={'numfig': True, + 'numfig_format': {'figure': 'Figure:%s', + 'table': 'Tab_%s', + 'code-block': 'Code-%s', + 'section': 'SECTION-%s'}}) @pytest.mark.test_params(shared_result='test_build_html_numfig_format_warn') def test_numfig_with_prefix(app, cached_etree_parse, fname, expect): app.build() check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect) -@pytest.mark.sphinx('html', testroot='numfig', confoverrides={ - 'numfig': True, 'numfig_secnum_depth': 2}) +@pytest.mark.sphinx('html', testroot='numfig', + confoverrides={'numfig': True, 'numfig_secnum_depth': 2}) @pytest.mark.test_params(shared_result='test_build_html_numfig_depth_2') def test_numfig_with_secnum_depth_warn(app, warning): app.build() @@ -918,16 +964,16 @@ def test_numfig_with_secnum_depth_warn(app, warning): "span[@class='caption-number']", '^Listing 1 $', True), (".//div[@class='code-block-caption']/" "span[@class='caption-number']", '^Listing 2 $', True), - (".//li/a/span", '^Fig. 1$', True), - (".//li/a/span", '^Figure2.1.2$', True), - (".//li/a/span", '^Table 1$', True), - (".//li/a/span", '^Table:2.1.2$', True), - (".//li/a/span", '^Listing 1$', True), - (".//li/a/span", '^Code-2.1.2$', True), - (".//li/a/span", '^Section.1$', True), - (".//li/a/span", '^Section.2.1$', True), - (".//li/a/span", '^Fig.1 should be Fig.1$', True), - (".//li/a/span", '^Sect.1 Foo$', True), + (".//li/p/a/span", '^Fig. 1$', True), + (".//li/p/a/span", '^Figure2.1.2$', True), + (".//li/p/a/span", '^Table 1$', True), + (".//li/p/a/span", '^Table:2.1.2$', True), + (".//li/p/a/span", '^Listing 1$', True), + (".//li/p/a/span", '^Code-2.1.2$', True), + (".//li/p/a/span", '^Section.1$', True), + (".//li/p/a/span", '^Section.2.1$', True), + (".//li/p/a/span", '^Fig.1 should be Fig.1$', True), + (".//li/p/a/span", '^Sect.1 Foo$', True), ], 'foo.html': [ (".//div[@class='figure align-center']/p[@class='caption']/" @@ -984,8 +1030,11 @@ def test_numfig_with_secnum_depth_warn(app, warning): "span[@class='caption-number']", '^Listing 2.1.2 $', True), ], })) -@pytest.mark.sphinx('html', testroot='numfig', confoverrides={ - 'numfig': True, 'numfig_secnum_depth': 2}) +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') +@pytest.mark.sphinx('html', testroot='numfig', + confoverrides={'numfig': True, + 'numfig_secnum_depth': 2}) @pytest.mark.test_params(shared_result='test_build_html_numfig_depth_2') def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect): app.build() @@ -1006,16 +1055,16 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect): "span[@class='caption-number']", '^Listing 1 $', True), (".//div[@class='code-block-caption']/" "span[@class='caption-number']", '^Listing 2 $', True), - (".//li/a/span", '^Fig. 1$', True), - (".//li/a/span", '^Figure2.2$', True), - (".//li/a/span", '^Table 1$', True), - (".//li/a/span", '^Table:2.2$', True), - (".//li/a/span", '^Listing 1$', True), - (".//li/a/span", '^Code-2.2$', True), - (".//li/a/span", '^Section.1$', True), - (".//li/a/span", '^Section.2.1$', True), - (".//li/a/span", '^Fig.1 should be Fig.1$', True), - (".//li/a/span", '^Sect.1 Foo$', True), + (".//li/p/a/span", '^Fig. 1$', True), + (".//li/p/a/span", '^Figure2.2$', True), + (".//li/p/a/span", '^Table 1$', True), + (".//li/p/a/span", '^Table:2.2$', True), + (".//li/p/a/span", '^Listing 1$', True), + (".//li/p/a/span", '^Code-2.2$', True), + (".//li/p/a/span", '^Section.1$', True), + (".//li/p/a/span", '^Section.2.1$', True), + (".//li/p/a/span", '^Fig.1 should be Fig.1$', True), + (".//li/p/a/span", '^Sect.1 Foo$', True), (".//div[@class='figure align-center']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1.1 $', True), (".//div[@class='figure align-center']/p[@class='caption']/" @@ -1066,8 +1115,9 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect): "span[@class='caption-number']", '^Listing 2.2 $', True), ], })) -@pytest.mark.sphinx('singlehtml', testroot='numfig', confoverrides={ - 'numfig': True}) +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') +@pytest.mark.sphinx('singlehtml', testroot='numfig', confoverrides={'numfig': True}) @pytest.mark.test_params(shared_result='test_build_html_numfig_on') def test_numfig_with_singlehtml(app, cached_etree_parse, fname, expect): app.build() @@ -1084,17 +1134,17 @@ def test_numfig_with_singlehtml(app, cached_etree_parse, fname, expect): "/span[@class='caption-number']", "Fig. 3", True), (".//div//span[@class='caption-number']", "No.1 ", True), (".//div//span[@class='caption-number']", "No.2 ", True), - (".//li/a/span", 'Fig. 1', True), - (".//li/a/span", 'Fig. 2', True), - (".//li/a/span", 'Fig. 3', True), - (".//li/a/span", 'No.1', True), - (".//li/a/span", 'No.2', True), + (".//li/p/a/span", 'Fig. 1', True), + (".//li/p/a/span", 'Fig. 2', True), + (".//li/p/a/span", 'Fig. 3', True), + (".//li/p/a/span", 'No.1', True), + (".//li/p/a/span", 'No.2', True), ], })) -@pytest.mark.sphinx( - 'html', testroot='add_enumerable_node', - srcdir='test_enumerable_node', -) +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') +@pytest.mark.sphinx('html', testroot='add_enumerable_node', + srcdir='test_enumerable_node') def test_enumerable_node(app, cached_etree_parse, fname, expect): app.build() check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect) diff --git a/tests/test_build_html5.py b/tests/test_build_html5.py deleted file mode 100644 index 2c4ae517a..000000000 --- a/tests/test_build_html5.py +++ /dev/null @@ -1,372 +0,0 @@ -""" - test_build_html5 - ~~~~~~~~~~~~~~~~ - - Test the HTML5 writer and check output against XPath. - - This code is digest to reduce test running time. - Complete test code is here: - - https://github.com/sphinx-doc/sphinx/pull/2805/files - - :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -import re -from hashlib import md5 - -import pytest -from html5lib import HTMLParser -from test_build_html import flat_dict, tail_check, check_xpath - -from sphinx.util import docutils -from sphinx.util.docutils import is_html5_writer_available - - -etree_cache = {} - - -@pytest.mark.skipif(not is_html5_writer_available(), reason='HTML5 writer is not available') -@pytest.fixture(scope='module') -def cached_etree_parse(): - def parse(fname): - if fname in etree_cache: - return etree_cache[fname] - with (fname).open('rb') as fp: - etree = HTMLParser(namespaceHTMLElements=False).parse(fp) - etree_cache.clear() - etree_cache[fname] = etree - return etree - yield parse - etree_cache.clear() - - -@pytest.mark.skipif(not is_html5_writer_available(), reason='HTML5 writer is not available') -@pytest.mark.parametrize("fname,expect", flat_dict({ - 'images.html': [ - (".//img[@src='_images/img.png']", ''), - (".//img[@src='_images/img1.png']", ''), - (".//img[@src='_images/simg.png']", ''), - (".//img[@src='_images/svgimg.svg']", ''), - (".//a[@href='_sources/images.txt']", ''), - ], - 'subdir/images.html': [ - (".//img[@src='../_images/img1.png']", ''), - (".//img[@src='../_images/rimg.png']", ''), - ], - 'subdir/includes.html': [ - (".//a[@class='reference download internal']", ''), - (".//img[@src='../_images/img.png']", ''), - (".//p", 'This is an include file.'), - (".//pre/span", 'line 1'), - (".//pre/span", 'line 2'), - ], - 'includes.html': [ - (".//pre", 'Max Strauß'), - (".//a[@class='reference download internal']", ''), - (".//pre/span", '"quotes"'), - (".//pre/span", "'included'"), - (".//pre/span[@class='s2']", 'üöä'), - (".//div[@class='inc-pyobj1 highlight-text notranslate']//pre", - r'^class Foo:\n pass\n\s*$'), - (".//div[@class='inc-pyobj2 highlight-text notranslate']//pre", - r'^ def baz\(\):\n pass\n\s*$'), - (".//div[@class='inc-lines highlight-text notranslate']//pre", - r'^class Foo:\n pass\nclass Bar:\n$'), - (".//div[@class='inc-startend highlight-text notranslate']//pre", - '^foo = "Including Unicode characters: üöä"\\n$'), - (".//div[@class='inc-preappend highlight-text notranslate']//pre", - r'(?m)^START CODE$'), - (".//div[@class='inc-pyobj-dedent highlight-python notranslate']//span", - r'def'), - (".//div[@class='inc-tab3 highlight-text notranslate']//pre", - r'-| |-'), - (".//div[@class='inc-tab8 highlight-python notranslate']//pre/span", - r'-| |-'), - ], - 'autodoc.html': [ - (".//dt[@id='autodoc_target.Class']", ''), - (".//dt[@id='autodoc_target.function']/em", r'\*\*kwds'), - (".//dd/p", r'Return spam\.'), - ], - 'extapi.html': [ - (".//strong", 'from class: Bar'), - ], - 'markup.html': [ - (".//title", 'set by title directive'), - (".//p/em", 'Section author: Georg Brandl'), - (".//p/em", 'Module author: Georg Brandl'), - # created by the meta directive - (".//meta[@name='author'][@content='Me']", ''), - (".//meta[@name='keywords'][@content='docs, sphinx']", ''), - # a label created by ``.. _label:`` - (".//div[@id='label']", ''), - # code with standard code blocks - (".//pre", '^some code$'), - # an option list - (".//span[@class='option']", '--help'), - # admonitions - (".//p[@class='admonition-title']", 'My Admonition'), - (".//div[@class='admonition note']/p", 'Note text.'), - (".//div[@class='admonition warning']/p", 'Warning text.'), - # inline markup - (".//li/p/strong", r'^command\\n$'), - (".//li/p/strong", r'^program\\n$'), - (".//li/p/em", r'^dfn\\n$'), - (".//li/p/kbd", r'^kbd\\n$'), - (".//li/p/span", 'File \N{TRIANGULAR BULLET} Close'), - (".//li/p/code/span[@class='pre']", '^a/$'), - (".//li/p/code/em/span[@class='pre']", '^varpart$'), - (".//li/p/code/em/span[@class='pre']", '^i$'), - (".//a[@href='https://www.python.org/dev/peps/pep-0008']" - "[@class='pep reference external']/strong", 'PEP 8'), - (".//a[@href='https://www.python.org/dev/peps/pep-0008']" - "[@class='pep reference external']/strong", - 'Python Enhancement Proposal #8'), - (".//a[@href='https://tools.ietf.org/html/rfc1.html']" - "[@class='rfc reference external']/strong", 'RFC 1'), - (".//a[@href='https://tools.ietf.org/html/rfc1.html']" - "[@class='rfc reference external']/strong", 'Request for Comments #1'), - (".//a[@href='objects.html#envvar-HOME']" - "[@class='reference internal']/code/span[@class='pre']", 'HOME'), - (".//a[@href='#with']" - "[@class='reference internal']/code/span[@class='pre']", '^with$'), - (".//a[@href='#grammar-token-try-stmt']" - "[@class='reference internal']/code/span", '^statement$'), - (".//a[@href='#some-label'][@class='reference internal']/span", '^here$'), - (".//a[@href='#some-label'][@class='reference internal']/span", '^there$'), - (".//a[@href='subdir/includes.html']" - "[@class='reference internal']/span", 'Including in subdir'), - (".//a[@href='objects.html#cmdoption-python-c']" - "[@class='reference internal']/code/span[@class='pre']", '-c'), - # abbreviations - (".//abbr[@title='abbreviation']", '^abbr$'), - # version stuff - (".//div[@class='versionadded']/p/span", 'New in version 0.6: '), - (".//div[@class='versionadded']/p/span", - tail_check('First paragraph of versionadded')), - (".//div[@class='versionchanged']/p/span", - tail_check('First paragraph of versionchanged')), - (".//div[@class='versionchanged']/p", - 'Second paragraph of versionchanged'), - # footnote reference - (".//a[@class='footnote-reference brackets']", r'1'), - # created by reference lookup - (".//a[@href='index.html#ref1']", ''), - # ``seealso`` directive - (".//div/p[@class='admonition-title']", 'See also'), - # a ``hlist`` directive - (".//table[@class='hlist']/tbody/tr/td/ul/li/p", '^This$'), - # a ``centered`` directive - (".//p[@class='centered']/strong", 'LICENSE'), - # a glossary - (".//dl/dt[@id='term-boson']", 'boson'), - # a production list - (".//pre/strong", 'try_stmt'), - (".//pre/a[@href='#grammar-token-try1-stmt']/code/span", 'try1_stmt'), - # tests for ``only`` directive - (".//p", 'A global substitution.'), - (".//p", 'In HTML.'), - (".//p", 'In both.'), - (".//p", 'Always present'), - # tests for ``any`` role - (".//a[@href='#with']/span", 'headings'), - (".//a[@href='objects.html#func_without_body']/code/span", 'objects'), - # tests for numeric labels - (".//a[@href='#id1'][@class='reference internal']/span", 'Testing various markup'), - ], - 'objects.html': [ - (".//dt[@id='mod.Cls.meth1']", ''), - (".//dt[@id='errmod.Error']", ''), - (".//dt/code", r'long\(parameter,\s* list\)'), - (".//dt/code", 'another one'), - (".//a[@href='#mod.Cls'][@class='reference internal']", ''), - (".//dl[@class='userdesc']", ''), - (".//dt[@id='userdesc-myobj']", ''), - (".//a[@href='#userdesc-myobj'][@class='reference internal']", ''), - # docfields - (".//a[@class='reference internal'][@href='#TimeInt']/em", 'TimeInt'), - (".//a[@class='reference internal'][@href='#Time']", 'Time'), - (".//a[@class='reference internal'][@href='#errmod.Error']/strong", 'Error'), - # C references - (".//span[@class='pre']", 'CFunction()'), - (".//a[@href='#c.Sphinx_DoSomething']", ''), - (".//a[@href='#c.SphinxStruct.member']", ''), - (".//a[@href='#c.SPHINX_USE_PYTHON']", ''), - (".//a[@href='#c.SphinxType']", ''), - (".//a[@href='#c.sphinx_global']", ''), - # test global TOC created by toctree() - (".//ul[@class='current']/li[@class='toctree-l1 current']/a[@href='#']", - 'Testing object descriptions'), - (".//li[@class='toctree-l1']/a[@href='markup.html']", - 'Testing various markup'), - # test unknown field names - (".//dt[@class='field-odd']", 'Field_name'), - (".//dt[@class='field-even']", 'Field_name all lower'), - (".//dt[@class='field-odd']", 'FIELD_NAME'), - (".//dt[@class='field-even']", 'FIELD_NAME ALL CAPS'), - (".//dt[@class='field-odd']", 'Field_Name'), - (".//dt[@class='field-even']", 'Field_Name All Word Caps'), - (".//dt[@class='field-odd']", 'Field_name'), - (".//dt[@class='field-even']", 'Field_name First word cap'), - (".//dt[@class='field-odd']", 'FIELd_name'), - (".//dt[@class='field-even']", 'FIELd_name PARTial caps'), - # custom sidebar - (".//h4", 'Custom sidebar'), - # docfields - (".//dd[@class='field-odd']/p/strong", '^moo$'), - (".//dd[@class='field-odd']/p/strong", tail_check(r'\(Moo\) .* Moo')), - (".//dd[@class='field-odd']/ul/li/p/strong", '^hour$'), - (".//dd[@class='field-odd']/ul/li/p/em", '^DuplicateType$'), - (".//dd[@class='field-odd']/ul/li/p/em", tail_check(r'.* Some parameter')), - # others - (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-p']/code/span", - 'perl'), - (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-p']/code/span", - '\\+p'), - (".//a[@class='reference internal'][@href='#cmdoption-perl-objc']/code/span", - '--ObjC\\+\\+'), - (".//a[@class='reference internal'][@href='#cmdoption-perl-plugin-option']/code/span", - '--plugin.option'), - (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-create-auth-token']" - "/code/span", - 'create-auth-token'), - (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-arg']/code/span", - 'arg'), - (".//a[@class='reference internal'][@href='#cmdoption-hg-arg-commit']/code/span", - 'hg'), - (".//a[@class='reference internal'][@href='#cmdoption-hg-arg-commit']/code/span", - 'commit'), - (".//a[@class='reference internal'][@href='#cmdoption-git-commit-p']/code/span", - 'git'), - (".//a[@class='reference internal'][@href='#cmdoption-git-commit-p']/code/span", - 'commit'), - (".//a[@class='reference internal'][@href='#cmdoption-git-commit-p']/code/span", - '-p'), - ], - 'index.html': [ - (".//meta[@name='hc'][@content='hcval']", ''), - (".//meta[@name='hc_co'][@content='hcval_co']", ''), - (".//dt[@class='label']/span[@class='brackets']", r'Ref1'), - (".//dt[@class='label']", ''), - (".//li[@class='toctree-l1']/a", 'Testing various markup'), - (".//li[@class='toctree-l2']/a", 'Inline markup'), - (".//title", 'Sphinx '), - (".//div[@class='footer']", 'Georg Brandl & Team'), - (".//a[@href='http://python.org/']" - "[@class='reference external']", ''), - (".//li/p/a[@href='genindex.html']/span", 'Index'), - (".//li/p/a[@href='py-modindex.html']/span", 'Module Index'), - (".//li/p/a[@href='search.html']/span", 'Search Page'), - # custom sidebar only for contents - (".//h4", 'Contents sidebar'), - # custom JavaScript - (".//script[@src='file://moo.js']", ''), - # URL in contents - (".//a[@class='reference external'][@href='http://sphinx-doc.org/']", - 'http://sphinx-doc.org/'), - (".//a[@class='reference external'][@href='http://sphinx-doc.org/latest/']", - 'Latest reference'), - # Indirect hyperlink targets across files - (".//a[@href='markup.html#some-label'][@class='reference internal']/span", - '^indirect hyperref$'), - ], - 'bom.html': [ - (".//title", " File with UTF-8 BOM"), - ], - 'extensions.html': [ - (".//a[@href='http://python.org/dev/']", "http://python.org/dev/"), - (".//a[@href='http://bugs.python.org/issue1000']", "issue 1000"), - (".//a[@href='http://bugs.python.org/issue1042']", "explicit caption"), - ], - 'genindex.html': [ - # index entries - (".//a/strong", "Main"), - (".//a/strong", "[1]"), - (".//a/strong", "Other"), - (".//a", "entry"), - (".//li/a", "double"), - ], - 'footnote.html': [ - (".//a[@class='footnote-reference brackets'][@href='#id9'][@id='id1']", r"1"), - (".//a[@class='footnote-reference brackets'][@href='#id10'][@id='id2']", r"2"), - (".//a[@class='footnote-reference brackets'][@href='#foo'][@id='id3']", r"3"), - (".//a[@class='reference internal'][@href='#bar'][@id='id4']", r"\[bar\]"), - (".//a[@class='reference internal'][@href='#baz-qux'][@id='id5']", r"\[baz_qux\]"), - (".//a[@class='footnote-reference brackets'][@href='#id11'][@id='id6']", r"4"), - (".//a[@class='footnote-reference brackets'][@href='#id12'][@id='id7']", r"5"), - (".//a[@class='fn-backref'][@href='#id1']", r"1"), - (".//a[@class='fn-backref'][@href='#id2']", r"2"), - (".//a[@class='fn-backref'][@href='#id3']", r"3"), - (".//a[@class='fn-backref'][@href='#id4']", r"bar"), - (".//a[@class='fn-backref'][@href='#id5']", r"baz_qux"), - (".//a[@class='fn-backref'][@href='#id6']", r"4"), - (".//a[@class='fn-backref'][@href='#id7']", r"5"), - (".//a[@class='fn-backref'][@href='#id8']", r"6"), - ], - 'otherext.html': [ - (".//h1", "Generated section"), - (".//a[@href='_sources/otherext.foo.txt']", ''), - ] -})) -@pytest.mark.sphinx('html', tags=['testtag'], confoverrides={ - 'html_context.hckey_co': 'hcval_co', - 'html_experimental_html5_writer': True}) -@pytest.mark.test_params(shared_result='test_build_html5_output') -def test_html5_output(app, cached_etree_parse, fname, expect): - app.build() - print(app.outdir / fname) - check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect) - - -@pytest.mark.skipif(docutils.__version_info__ < (0, 13), - reason='docutils-0.13 or above is required') -@pytest.mark.sphinx('html', tags=['testtag'], confoverrides={ - 'html_context.hckey_co': 'hcval_co', - 'html_experimental_html5_writer': True}) -@pytest.mark.test_params(shared_result='test_build_html_output') -def test_html_download(app): - app.build() - - # subdir/includes.html - result = (app.outdir / 'subdir' / 'includes.html').text() - pattern = ('') - matched = re.search(pattern, result) - assert matched - assert (app.outdir / matched.group(1)).exists() - filename = matched.group(1) - - # includes.html - result = (app.outdir / 'includes.html').text() - pattern = ('') - matched = re.search(pattern, result) - assert matched - assert (app.outdir / matched.group(1)).exists() - assert matched.group(1) == filename - - -@pytest.mark.skipif(docutils.__version_info__ < (0, 13), - reason='docutils-0.13 or above is required') -@pytest.mark.sphinx('html', testroot='roles-download', - confoverrides={'html_experimental_html5_writer': True}) -def test_html_download_role(app, status, warning): - app.build() - digest = md5((app.srcdir / 'dummy.dat').encode()).hexdigest() - assert (app.outdir / '_downloads' / digest / 'dummy.dat').exists() - - content = (app.outdir / 'index.html').text() - assert (('
  • ' - '' - 'dummy.dat

  • ' % digest) - in content) - assert ('
  • ' - 'not_found.dat

  • ' in content) - assert ('
  • ' - '' - 'Sphinx logo' - '

  • ' in content) diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index fe5e37f23..4d7d3e592 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -17,6 +17,7 @@ import sphinx.domains.cpp as cppDomain from sphinx import addnodes from sphinx.domains.cpp import DefinitionParser, DefinitionError, NoOldIdError from sphinx.domains.cpp import Symbol, _max_id, _id_prefix +from sphinx.util import docutils def parse(name, string): @@ -734,12 +735,14 @@ def test_build_domain_cpp_misuse_of_roles(app, status, warning): # TODO: properly check for the warnings we expect +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') @pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'add_function_parentheses': True}) def test_build_domain_cpp_with_add_function_parentheses_is_True(app, status, warning): app.builder.build_all() def check(spec, text, file): - pattern = '
  • %s%s
  • ' % spec + pattern = '
  • %s%s

  • ' % spec res = re.search(pattern, text) if not res: print("Pattern\n\t%s\nnot found in %s" % (pattern, file)) @@ -775,13 +778,14 @@ def test_build_domain_cpp_with_add_function_parentheses_is_True(app, status, war check(s, t, f) -@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={ - 'add_function_parentheses': False}) +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') +@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'add_function_parentheses': False}) def test_build_domain_cpp_with_add_function_parentheses_is_False(app, status, warning): app.builder.build_all() def check(spec, text, file): - pattern = '
  • %s%s
  • ' % spec + pattern = '
  • %s%s

  • ' % spec res = re.search(pattern, text) if not res: print("Pattern\n\t%s\nnot found in %s" % (pattern, file)) diff --git a/tests/test_ext_autosectionlabel.py b/tests/test_ext_autosectionlabel.py index 0b4d355a8..9a139a687 100644 --- a/tests/test_ext_autosectionlabel.py +++ b/tests/test_ext_autosectionlabel.py @@ -12,37 +12,43 @@ import re import pytest +from sphinx.util import docutils + +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') @pytest.mark.sphinx('html', testroot='ext-autosectionlabel') def test_autosectionlabel_html(app, status, warning): app.builder.build_all() content = (app.outdir / 'index.html').text() - html = ('
  • ' - 'Introduce of Sphinx
  • ') + html = ('
  • ' + 'Introduce of Sphinx

  • ') assert re.search(html, content, re.S) - html = ('
  • ' - 'Installation
  • ') + html = ('
  • ' + 'Installation

  • ') assert re.search(html, content, re.S) - html = ('
  • ' - 'For Windows users
  • ') + html = ('
  • ' + 'For Windows users

  • ') assert re.search(html, content, re.S) - html = ('
  • ' - 'For UNIX users
  • ') + html = ('
  • ' + 'For UNIX users

  • ') assert re.search(html, content, re.S) # for smart_quotes (refs: #4027) - html = ('
  • ' 'This one’s got an apostrophe' - '

  • ') + '

    ') assert re.search(html, content, re.S) # Re-use test definition from above, just change the test root directory +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') @pytest.mark.sphinx('html', testroot='ext-autosectionlabel-prefix-document') def test_autosectionlabel_prefix_document_html(app, status, warning): return test_autosectionlabel_html(app, status, warning) diff --git a/tests/test_ext_todo.py b/tests/test_ext_todo.py index a6e258347..2ce7ac95e 100644 --- a/tests/test_ext_todo.py +++ b/tests/test_ext_todo.py @@ -12,7 +12,11 @@ import re import pytest +from sphinx.util import docutils + +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') @pytest.mark.sphinx('html', testroot='ext-todo', freshenv=True, confoverrides={'todo_include_todos': True, 'todo_emit_warnings': True}) def test_todo(app, status, warning): @@ -26,22 +30,22 @@ def test_todo(app, status, warning): # check todolist content = (app.outdir / 'index.html').text() - html = ('

    Todo

    \n' - '

    todo in foo

    ') + html = ('

    Todo

    \n' + '

    todo in foo

    ') assert re.search(html, content, re.S) - html = ('

    Todo

    \n' - '

    todo in bar

    ') + html = ('

    Todo

    \n' + '

    todo in bar

    ') assert re.search(html, content, re.S) # check todo content = (app.outdir / 'foo.html').text() - html = ('

    Todo

    \n' - '

    todo in foo

    ') + html = ('

    Todo

    \n' + '

    todo in foo

    ') assert re.search(html, content, re.S) - html = ('

    Todo

    \n' - '

    todo in param field

    ') + html = ('

    Todo

    \n' + '

    todo in param field

    ') assert re.search(html, content, re.S) # check emitted warnings @@ -68,18 +72,18 @@ def test_todo_not_included(app, status, warning): # check todolist content = (app.outdir / 'index.html').text() - html = ('

    Todo

    \n' - '

    todo in foo

    ') + html = ('

    Todo

    \n' + '

    todo in foo

    ') assert not re.search(html, content, re.S) - html = ('

    Todo

    \n' - '

    todo in bar

    ') + html = ('

    Todo

    \n' + '

    todo in bar

    ') assert not re.search(html, content, re.S) # check todo content = (app.outdir / 'foo.html').text() - html = ('

    Todo

    \n' - '

    todo in foo

    ') + html = ('

    Todo

    \n' + '

    todo in foo

    ') assert not re.search(html, content, re.S) # check emitted warnings From 12e65061f57e78fdfecead0d36b51107ddccd495 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 9 Feb 2019 17:49:24 +0900 Subject: [PATCH 50/86] Update basic.css for HTML5 writer --- doc/_themes/sphinx13/static/sphinx13.css | 21 ++++---- sphinx/themes/basic/static/basic.css_t | 66 +++++++++++++++++++++++- 2 files changed, 74 insertions(+), 13 deletions(-) diff --git a/doc/_themes/sphinx13/static/sphinx13.css b/doc/_themes/sphinx13/static/sphinx13.css index 235bfdcc4..3dadf8168 100644 --- a/doc/_themes/sphinx13/static/sphinx13.css +++ b/doc/_themes/sphinx13/static/sphinx13.css @@ -388,32 +388,29 @@ div.admonition, div.warning { padding: 0; } -div.admonition p, div.warning p { +div.admonition > p, div.warning > p { margin: 0.5em 1em 0.5em 1em; padding: 0; } -div.admonition pre, div.warning pre { +div.admonition > pre, div.warning > pre { margin: 0.4em 1em 0.4em 1em; } -div.admonition p.admonition-title, -div.warning p.admonition-title { - margin-top: 1em; - padding-top: 0.5em; +div.admonition > p.admonition-title, +div.warning > p.admonition-title { + margin-top: 0.5em; font-weight: bold; } div.warning { border: 1px solid #940000; -/* background-color: #FFCCCF;*/ } -div.warning p.admonition-title { -} - -div.admonition ul, div.admonition ol, -div.warning ul, div.warning ol { +div.admonition > ul, +div.admonition > ol, +div.warning > ul, +div.warning > ol { margin: 0.1em 0.5em 0.5em 3em; padding: 0; } diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t index 5bdd803eb..ec30e8432 100644 --- a/sphinx/themes/basic/static/basic.css_t +++ b/sphinx/themes/basic/static/basic.css_t @@ -231,6 +231,16 @@ a.headerlink { visibility: hidden; } +a.brackets:before, +span.brackets > a:before{ + content: "["; +} + +a.brackets:after, +span.brackets > a:after { + content: "]"; +} + h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, @@ -391,6 +401,14 @@ table.citation td { border-bottom: none; } +td > p:first-child { + margin-top: 0px; +} + +td > p:only-child { + margin-bottom: 0px; +} + /* -- figures --------------------------------------------------------------- */ div.figure { @@ -460,11 +478,57 @@ ol.upperroman { list-style: upper-roman; } +li > p:first-child { + margin-top: 0px; +} + +li > p:only-child { + margin-bottom: 0px; +} + +dl.footnote > dt, +dl.citation > dt { + float: left; +} + +dl.footnote > dd, +dl.citation > dd { + margin-bottom: 0em; +} + +dl.footnote > dd:after, +dl.citation > dd:after { + content: ""; + clear: both; +} + +dl.field-list { + display: flex; + flex-wrap: wrap; +} + +dl.field-list > dt { + flex-basis: 20%; + font-weight: bold; + word-break: break-word; +} + +dl.field-list > dt:after { + content: ":"; +} + +dl.field-list > dd { + flex-basis: 70%; + padding-left: 1em; + margin-left: 0em; + margin-bottom: 0em; +} + dl { margin-bottom: 15px; } -dd p { +dd > p:first-child { margin-top: 0px; } From d548bf98f5d6999d548f2989d90f56cbcdb24002 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 18:07:30 +0900 Subject: [PATCH 51/86] autosectionlabel: Rename node_get_depth() to get_node_depth() --- sphinx/ext/autosectionlabel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/ext/autosectionlabel.py b/sphinx/ext/autosectionlabel.py index 76903aab1..a3267baf4 100644 --- a/sphinx/ext/autosectionlabel.py +++ b/sphinx/ext/autosectionlabel.py @@ -30,7 +30,7 @@ if False: from sphinx.application import Sphinx # NOQA -def node_get_depth(node): +def get_node_depth(node): i = 0 cur_node = node while cur_node.parent != node.document: @@ -45,7 +45,7 @@ def register_sections_as_label(app, document): anonlabels = app.env.domaindata['std']['anonlabels'] for node in document.traverse(nodes.section): if (app.config.autosectionlabel_max_depth and - node_get_depth(node) > app.config.autosectionlabel_max_depth): + get_node_depth(node) > app.config.autosectionlabel_max_depth): continue labelid = node['ids'][0] docname = app.env.docname From 4250b197c5d1697b42b1471a605f8079d9444bf5 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 18:07:57 +0900 Subject: [PATCH 52/86] autosectionlabel: Rename confval to autosectionlabel_maxdepth --- CHANGES | 2 +- sphinx/ext/autosectionlabel.py | 6 +++--- tests/test_ext_autosectionlabel.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 9966e415e..698ffa014 100644 --- a/CHANGES +++ b/CHANGES @@ -186,7 +186,7 @@ Features added * #5924: githubpages: create CNAME file for custom domains when :confval:`html_baseurl` set * #4261: autosectionlabel: restrict the labeled sections by new config value; - :confval:`autosectionlabel_max_depth` + :confval:`autosectionlabel_maxdepth` Bugs fixed diff --git a/sphinx/ext/autosectionlabel.py b/sphinx/ext/autosectionlabel.py index a3267baf4..35eaa0f34 100644 --- a/sphinx/ext/autosectionlabel.py +++ b/sphinx/ext/autosectionlabel.py @@ -44,8 +44,8 @@ def register_sections_as_label(app, document): labels = app.env.domaindata['std']['labels'] anonlabels = app.env.domaindata['std']['anonlabels'] for node in document.traverse(nodes.section): - if (app.config.autosectionlabel_max_depth and - get_node_depth(node) > app.config.autosectionlabel_max_depth): + if (app.config.autosectionlabel_maxdepth and + get_node_depth(node) > app.config.autosectionlabel_maxdepth): continue labelid = node['ids'][0] docname = app.env.docname @@ -69,7 +69,7 @@ def register_sections_as_label(app, document): def setup(app): # type: (Sphinx) -> Dict[str, Any] app.add_config_value('autosectionlabel_prefix_document', False, 'env') - app.add_config_value('autosectionlabel_max_depth', None, 'env') + app.add_config_value('autosectionlabel_maxdepth', None, 'env') app.connect('doctree-read', register_sections_as_label) return { diff --git a/tests/test_ext_autosectionlabel.py b/tests/test_ext_autosectionlabel.py index cd90c23e8..6dcd9e57b 100644 --- a/tests/test_ext_autosectionlabel.py +++ b/tests/test_ext_autosectionlabel.py @@ -71,7 +71,7 @@ def test_autosectionlabel_prefix_document_html(app, status, warning): @pytest.mark.sphinx('html', testroot='ext-autosectionlabel', - confoverrides={'autosectionlabel_max_depth': 2}) -def test_autosectionlabel_max_depth(app, status, warning): + confoverrides={'autosectionlabel_maxdepth': 2}) +def test_autosectionlabel_maxdepth(app, status, warning): return test_autosectionlabel_html(app, status, warning, skipped_labels=True) From 9f3feb6315bdba71a64a0b4f5d6130fd81552a21 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 18:15:31 +0900 Subject: [PATCH 53/86] docs: autosectionlabel_maxdepth --- doc/usage/extensions/autosectionlabel.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/usage/extensions/autosectionlabel.rst b/doc/usage/extensions/autosectionlabel.rst index 20b15ca20..caaa5db26 100644 --- a/doc/usage/extensions/autosectionlabel.rst +++ b/doc/usage/extensions/autosectionlabel.rst @@ -38,3 +38,10 @@ Configuration called ``Introduction`` that appears in document ``index.rst``. Useful for avoiding ambiguity when the same section heading appears in different documents. + +.. confval:: autosectionlabel_maxdepth + + If set, autosectionlabel chooses the sections for labeling by its depth. For + example, when set 1 to ``autosectionlabel_maxdepth``, labels are generated + only for top level sections, and deeper sections are not labeled. It + defaults to ``None`` (disabled). From 2d44232e34df4d9ef04a2a103ff628922693718c Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 18:33:42 +0900 Subject: [PATCH 54/86] Fix autosectionlabel_maxdepth is 0 origin --- sphinx/ext/autosectionlabel.py | 2 +- tests/test_ext_autosectionlabel.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/ext/autosectionlabel.py b/sphinx/ext/autosectionlabel.py index 35eaa0f34..4c353049c 100644 --- a/sphinx/ext/autosectionlabel.py +++ b/sphinx/ext/autosectionlabel.py @@ -45,7 +45,7 @@ def register_sections_as_label(app, document): anonlabels = app.env.domaindata['std']['anonlabels'] for node in document.traverse(nodes.section): if (app.config.autosectionlabel_maxdepth and - get_node_depth(node) > app.config.autosectionlabel_maxdepth): + get_node_depth(node) >= app.config.autosectionlabel_maxdepth): continue labelid = node['ids'][0] docname = app.env.docname diff --git a/tests/test_ext_autosectionlabel.py b/tests/test_ext_autosectionlabel.py index 6dcd9e57b..7960e2943 100644 --- a/tests/test_ext_autosectionlabel.py +++ b/tests/test_ext_autosectionlabel.py @@ -71,7 +71,7 @@ def test_autosectionlabel_prefix_document_html(app, status, warning): @pytest.mark.sphinx('html', testroot='ext-autosectionlabel', - confoverrides={'autosectionlabel_maxdepth': 2}) + confoverrides={'autosectionlabel_maxdepth': 3}) def test_autosectionlabel_maxdepth(app, status, warning): return test_autosectionlabel_html(app, status, warning, skipped_labels=True) From ac399b3b4e52f96cae53e1b257df408920092fa4 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 18:34:04 +0900 Subject: [PATCH 55/86] refactor tests for autosectionlabel --- .../index.rst | 8 +++ .../roots/test-ext-autosectionlabel/index.rst | 1 + tests/test_ext_autosectionlabel.py | 54 +++++++++++-------- 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/tests/roots/test-ext-autosectionlabel-prefix-document/index.rst b/tests/roots/test-ext-autosectionlabel-prefix-document/index.rst index 10fa2662d..d767373c2 100644 --- a/tests/roots/test-ext-autosectionlabel-prefix-document/index.rst +++ b/tests/roots/test-ext-autosectionlabel-prefix-document/index.rst @@ -15,6 +15,12 @@ For Windows users For UNIX users -------------- +Linux +^^^^^ + +FreeBSD +^^^^^^^ + This one's got an apostrophe ---------------------------- @@ -26,4 +32,6 @@ References * :ref:`index:Installation` * :ref:`index:For Windows users` * :ref:`index:For UNIX users` +* :ref:`index:Linux` +* :ref:`index:FreeBSD` * :ref:`index:This one's got an apostrophe` diff --git a/tests/roots/test-ext-autosectionlabel/index.rst b/tests/roots/test-ext-autosectionlabel/index.rst index 7d1b39ded..133206ed6 100644 --- a/tests/roots/test-ext-autosectionlabel/index.rst +++ b/tests/roots/test-ext-autosectionlabel/index.rst @@ -27,6 +27,7 @@ This one's got an apostrophe References ========== +* :ref:`test-ext-autosectionlabel` * :ref:`Introduce of Sphinx` * :ref:`Installation` * :ref:`For Windows users` diff --git a/tests/test_ext_autosectionlabel.py b/tests/test_ext_autosectionlabel.py index 7960e2943..5e83cacf6 100644 --- a/tests/test_ext_autosectionlabel.py +++ b/tests/test_ext_autosectionlabel.py @@ -34,26 +34,13 @@ def test_autosectionlabel_html(app, status, warning, skipped_labels=False): 'For UNIX users') assert re.search(html, content, re.S) - if skipped_labels is None: - return + html = ('
  • ' + 'Linux
  • ') + assert re.search(html, content, re.S) - if not skipped_labels: - html = ('
  • ' - 'Linux
  • ') - assert re.search(html, content, re.S) - - html = ('
  • ' - 'FreeBSD
  • ') - assert re.search(html, content, re.S) - else: - html = '
  • Linux
  • ' - assert re.search(html, content, re.S) - - html = '
  • FreeBSD
  • ' - assert re.search(html, content, re.S) - - assert 'WARNING: undefined label: linux' in warning.getvalue() - assert 'WARNING: undefined label: freebsd' in warning.getvalue() + html = ('
  • ' + 'FreeBSD
  • ') + assert re.search(html, content, re.S) # for smart_quotes (refs: #4027) html = ('
  • ' + 'test-ext-autosectionlabel
  • ') + assert re.search(html, content, re.S) + + # depth: 2 + html = ('
  • ' + 'Installation
  • ') + assert re.search(html, content, re.S) + + # depth: 3 + html = ('
  • ' + 'For Windows users
  • ') + assert re.search(html, content, re.S) + + # depth: 4 + html = '
  • Linux
  • ' + assert re.search(html, content, re.S) + + assert 'WARNING: undefined label: linux' in warning.getvalue() From 73b8d6ff3342451e44de94ffca6b3c885ca6b425 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 19:22:57 +0900 Subject: [PATCH 56/86] Closes #5636: autosummary: Add autosummary_mock_imports to mock external libraries --- CHANGES | 2 ++ doc/usage/extensions/autosummary.rst | 5 +++++ sphinx/ext/autosummary/__init__.py | 17 +++++++++++------ .../test-ext-autosummary-mock_imports/conf.py | 7 +++++++ .../test-ext-autosummary-mock_imports/foo.py | 6 ++++++ .../test-ext-autosummary-mock_imports/index.rst | 7 +++++++ tests/test_ext_autosummary.py | 13 +++++++++++++ 7 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 tests/roots/test-ext-autosummary-mock_imports/conf.py create mode 100644 tests/roots/test-ext-autosummary-mock_imports/foo.py create mode 100644 tests/roots/test-ext-autosummary-mock_imports/index.rst diff --git a/CHANGES b/CHANGES index 4c032f04c..f16a62881 100644 --- a/CHANGES +++ b/CHANGES @@ -160,6 +160,8 @@ Features added * #5533: autodoc: :confval:`autodoc_default_options` supports ``member-order`` * #5394: autodoc: Display readable names in type annotations for mocked objects * #5459: autodoc: :confval:`autodoc_default_options` accepts ``True`` as a value +* #5635: autosummary: Add :confval:`autosummary_mock_imports` to mock external + libraries on importing targets * #4018: htmlhelp: Add :confval:`htmlhelp_file_suffix` and :confval:`htmlhelp_link_suffix` * #5559: text: Support complex tables (colspan and rowspan) diff --git a/doc/usage/extensions/autosummary.rst b/doc/usage/extensions/autosummary.rst index 24e0d1855..eeb522950 100644 --- a/doc/usage/extensions/autosummary.rst +++ b/doc/usage/extensions/autosummary.rst @@ -143,6 +143,11 @@ also use this new config value: The new files will be placed in the directories specified in the ``:toctree:`` options of the directives. +.. confval:: autosummary_mock_imports + + This value contains a list of modules to be mocked up. See + :confval:`autodoc_mock_imports` for more details. It defaults to + :confval:`autodoc_mock_imports`. Customizing templates --------------------- diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index a18ce2a6d..4cd1638d6 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -72,7 +72,7 @@ from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.environment.adapters.toctree import TocTree from sphinx.ext.autodoc import get_documenters from sphinx.ext.autodoc.directive import DocumenterBridge, Options -from sphinx.ext.autodoc.importer import import_module +from sphinx.ext.autodoc.importer import import_module, mock from sphinx.locale import __ from sphinx.pycode import ModuleAnalyzer, PycodeError from sphinx.util import import_object, rst, logging @@ -286,7 +286,8 @@ class Autosummary(SphinxDirective): display_name = name.split('.')[-1] try: - real_name, obj, parent, modname = import_by_name(name, prefixes=prefixes) + with mock(self.config.autosummary_mock_imports): + real_name, obj, parent, modname = import_by_name(name, prefixes=prefixes) except ImportError: logger.warning(__('failed to import %s'), name) items.append((name, '', '', name)) @@ -702,10 +703,11 @@ def process_generate_options(app): 'But your source_suffix does not contain .rst. Skipped.')) return - generate_autosummary_docs(genfiles, builder=app.builder, - warn=logger.warning, info=logger.info, - suffix=suffix, base_path=app.srcdir, - app=app) + with mock(app.config.autosummary_mock_imports): + generate_autosummary_docs(genfiles, builder=app.builder, + warn=logger.warning, info=logger.info, + suffix=suffix, base_path=app.srcdir, + app=app) def setup(app): @@ -729,4 +731,7 @@ def setup(app): app.connect('doctree-read', process_autosummary_toc) app.connect('builder-inited', process_generate_options) app.add_config_value('autosummary_generate', [], True, [bool]) + app.add_config_value('autosummary_mock_imports', + lambda config: config.autodoc_mock_imports, 'env') + return {'version': sphinx.__display_version__, 'parallel_read_safe': True} diff --git a/tests/roots/test-ext-autosummary-mock_imports/conf.py b/tests/roots/test-ext-autosummary-mock_imports/conf.py new file mode 100644 index 000000000..1097e3c04 --- /dev/null +++ b/tests/roots/test-ext-autosummary-mock_imports/conf.py @@ -0,0 +1,7 @@ +import os +import sys +sys.path.insert(0, os.path.abspath('.')) + +extensions = ['sphinx.ext.autosummary'] +autosummary_generate = True +autosummary_mock_imports = ['unknown'] diff --git a/tests/roots/test-ext-autosummary-mock_imports/foo.py b/tests/roots/test-ext-autosummary-mock_imports/foo.py new file mode 100644 index 000000000..ab4460ef0 --- /dev/null +++ b/tests/roots/test-ext-autosummary-mock_imports/foo.py @@ -0,0 +1,6 @@ +import unknown + + +class Foo(unknown.Class): + """Foo class""" + pass diff --git a/tests/roots/test-ext-autosummary-mock_imports/index.rst b/tests/roots/test-ext-autosummary-mock_imports/index.rst new file mode 100644 index 000000000..f6044ed29 --- /dev/null +++ b/tests/roots/test-ext-autosummary-mock_imports/index.rst @@ -0,0 +1,7 @@ +test-ext-autosummary-mock_imports +================================= + +.. autosummary:: + :toctree: generated + + foo diff --git a/tests/test_ext_autosummary.py b/tests/test_ext_autosummary.py index 64c7df01c..63026beb5 100644 --- a/tests/test_ext_autosummary.py +++ b/tests/test_ext_autosummary.py @@ -8,6 +8,7 @@ :license: BSD, see LICENSE for details. """ +import sys from io import StringIO import pytest @@ -229,3 +230,15 @@ def test_import_by_name(): assert obj == sphinx.ext.autosummary.Autosummary.get_items assert parent is sphinx.ext.autosummary.Autosummary assert modname == 'sphinx.ext.autosummary' + + +@pytest.mark.sphinx('dummy', testroot='ext-autosummary-mock_imports') +def test_autosummary_mock_imports(app, status, warning): + try: + app.build() + assert warning.getvalue() == '' + + # generated/foo is generated successfully + assert app.env.get_doctree('generated/foo') + finally: + sys.modules.pop('foo', None) # unload foo module From 53653497806e88a672a573d28b38c1706eee402e Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 12 Feb 2019 00:28:03 +0900 Subject: [PATCH 57/86] Closes #4148: quickstart: some questions are removed --- CHANGES | 2 ++ sphinx/cmd/quickstart.py | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/CHANGES b/CHANGES index 4c032f04c..b92d36b5d 100644 --- a/CHANGES +++ b/CHANGES @@ -59,6 +59,8 @@ Incompatible changes from LaTeX preamble now get overwritten. Use ``\sphinxtableofcontentshook`` to insert custom user definitions. See :ref:`latex-macros`. * quickstart: Simplify generated ``conf.py`` +* #4148: quickstart: some questions are removed. They are still able to specify + via command line options * websupport: unbundled from sphinx core. Please use sphinxcontrib-websupport * C++, the visibility of base classes is now always rendered as present in the input. That is, ``private`` is now shown, where it was ellided before. diff --git a/sphinx/cmd/quickstart.py b/sphinx/cmd/quickstart.py index 6639bf7e9..c8b1b4fdd 100644 --- a/sphinx/cmd/quickstart.py +++ b/sphinx/cmd/quickstart.py @@ -516,7 +516,7 @@ Makefile to be used with sphinx-build. group = parser.add_argument_group(__('Structure options')) group.add_argument('--sep', action='store_true', default=None, help=__('if specified, separate source and build dirs')) - group.add_argument('--dot', metavar='DOT', + group.add_argument('--dot', metavar='DOT', default='_', help=__('replacement for dot in _templates etc.')) group = parser.add_argument_group(__('Project basic options')) @@ -530,9 +530,9 @@ Makefile to be used with sphinx-build. help=__('release of project')) group.add_argument('-l', '--language', metavar='LANGUAGE', dest='language', help=__('document language')) - group.add_argument('--suffix', metavar='SUFFIX', + group.add_argument('--suffix', metavar='SUFFIX', default='.rst', help=__('source file suffix')) - group.add_argument('--master', metavar='MASTER', + group.add_argument('--master', metavar='MASTER', default='index', help=__('master document name')) group.add_argument('--epub', action='store_true', default=False, help=__('use epub')) @@ -546,11 +546,11 @@ Makefile to be used with sphinx-build. action='append', help=__('enable arbitrary extensions')) group = parser.add_argument_group(__('Makefile and Batchfile creation')) - group.add_argument('--makefile', action='store_true', dest='makefile', default=None, + group.add_argument('--makefile', action='store_true', dest='makefile', default=True, help=__('create makefile')) group.add_argument('--no-makefile', action='store_false', dest='makefile', help=__('do not create makefile')) - group.add_argument('--batchfile', action='store_true', dest='batchfile', default=None, + group.add_argument('--batchfile', action='store_true', dest='batchfile', default=True, help=__('create batchfile')) group.add_argument('--no-batchfile', action='store_false', dest='batchfile', @@ -592,6 +592,13 @@ def main(argv=sys.argv[1:]): # delete None or False value d = dict((k, v) for k, v in d.items() if v is not None) + # handle use of CSV-style extension values + d.setdefault('extensions', []) + for ext in d['extensions'][:]: + if ',' in ext: + d['extensions'].remove(ext) + d['extensions'].extend(ext.split(',')) + try: if 'quiet' in d: if not set(['project', 'author']).issubset(d): @@ -621,13 +628,6 @@ def main(argv=sys.argv[1:]): print('[Interrupted.]') return 130 # 128 + SIGINT - # handle use of CSV-style extension values - d.setdefault('extensions', []) - for ext in d['extensions'][:]: - if ',' in ext: - d['extensions'].remove(ext) - d['extensions'].extend(ext.split(',')) - for variable in d.get('variables', []): try: name, value = variable.split('=') From b9b6930e2899bf3b1bf23d7f064797be8ed52452 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 14 Feb 2019 00:37:37 +0900 Subject: [PATCH 58/86] Fix mypy violations --- sphinx/domains/cpp.py | 2 +- sphinx/domains/python.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index ff5487598..13f905d07 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -7158,7 +7158,7 @@ class CPPDomain(Domain): # the non-identifier refs are cross-references, which should be processed: # - fix parenthesis due to operator() and add_function_parentheses if typ != "identifier": - title = contnode.pop(0).astext() # type: ignore + title = contnode.pop(0).astext() # If it's operator(), we need to add '()' if explicit function parens # are requested. Then the Sphinx machinery will add another pair. # Also, if it's an 'any' ref that resolves to a function, we need to add diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 8374c2918..2dc51dc33 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -151,7 +151,7 @@ class PyXrefMixin: delims_re = re.compile(delims) sub_targets = re.split(delims, target) - split_contnode = bool(contnode and contnode.astext() == target) # type: ignore + split_contnode = bool(contnode and contnode.astext() == target) results = [] for sub_target in filter(None, sub_targets): From c8d172b340ff72f2121da83c25f051988824db3d Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 14 Feb 2019 00:35:07 +0900 Subject: [PATCH 59/86] Fix typo --- sphinx/writers/texinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index 0f3137677..ede48ec60 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -247,7 +247,7 @@ class TexinfoTranslator(SphinxTranslator): title = self.settings.title # type: str if not title: title_node = self.document.next_node(nodes.title) - title = (title and title_node.astext()) or '' + title = (title_node and title_node.astext()) or '' elements['title'] = self.escape_id(title) or '' # filename if not elements['filename']: From 4adcb408ea95bbd3dce111441c28730f59538eb6 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 14 Feb 2019 01:03:22 +0900 Subject: [PATCH 60/86] Fix #6019: imgconverter: Including multipage PDF fails --- CHANGES | 1 + sphinx/ext/imgconverter.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index ba2f15cb9..669c29062 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,7 @@ Bugs fixed * LaTeX: Remove extraneous space after author names on PDF title page (refs: #6004) * #6046: LaTeX: ``TypeError`` is raised when invalid latex_elements given +* #6019: imgconverter: Including multipage PDF fails Testing -------- diff --git a/sphinx/ext/imgconverter.py b/sphinx/ext/imgconverter.py index 79755d351..b0b379936 100644 --- a/sphinx/ext/imgconverter.py +++ b/sphinx/ext/imgconverter.py @@ -66,9 +66,9 @@ class ImagemagickConverter(ImageConverter): # type: (unicode, unicode) -> bool """Converts the image to expected one.""" try: - if _from.lower().endswith('.gif'): - # when target is GIF format, pick the first frame - _from += '[0]' + # append an index 0 to source filename to pick up the first frame + # (or first page) of image (ex. Animation GIF, PDF) + _from += '[0]' args = ([self.config.image_converter] + self.config.image_converter_args + From 3e7113c4c6b91de842e43cb6e0ba254dfb0fe280 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 14 Feb 2019 02:04:21 +0900 Subject: [PATCH 61/86] Fix test_ext_autosectionlabel conflicts with HTML5 writer --- tests/test_ext_autosectionlabel.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/tests/test_ext_autosectionlabel.py b/tests/test_ext_autosectionlabel.py index 77c7a7e44..440860f7a 100644 --- a/tests/test_ext_autosectionlabel.py +++ b/tests/test_ext_autosectionlabel.py @@ -38,12 +38,12 @@ def test_autosectionlabel_html(app, status, warning, skipped_labels=False): 'For UNIX users

    ') assert re.search(html, content, re.S) - html = ('
  • ' - 'Linux
  • ') + html = ('
  • ' + 'Linux

  • ') assert re.search(html, content, re.S) - html = ('
  • ' - 'FreeBSD
  • ') + html = ('
  • ' + 'FreeBSD

  • ') assert re.search(html, content, re.S) # for smart_quotes (refs: #4027) @@ -62,6 +62,8 @@ def test_autosectionlabel_prefix_document_html(app, status, warning): test_autosectionlabel_html(app, status, warning) +@pytest.mark.skipif(docutils.__version_info__ < (0, 13), + reason='docutils-0.13 or above is required') @pytest.mark.sphinx('html', testroot='ext-autosectionlabel', confoverrides={'autosectionlabel_maxdepth': 3}) def test_autosectionlabel_maxdepth(app, status, warning): @@ -70,22 +72,22 @@ def test_autosectionlabel_maxdepth(app, status, warning): content = (app.outdir / 'index.html').text() # depth: 1 - html = ('
  • ' - 'test-ext-autosectionlabel
  • ') + html = ('
  • ' + 'test-ext-autosectionlabel

  • ') assert re.search(html, content, re.S) # depth: 2 - html = ('
  • ' - 'Installation
  • ') + html = ('
  • ' + 'Installation

  • ') assert re.search(html, content, re.S) # depth: 3 - html = ('
  • ' - 'For Windows users
  • ') + html = ('
  • ' + 'For Windows users

  • ') assert re.search(html, content, re.S) # depth: 4 - html = '
  • Linux
  • ' + html = '
  • Linux

  • ' assert re.search(html, content, re.S) assert 'WARNING: undefined label: linux' in warning.getvalue() From 891178e8ed6cde1b7bf3048ca73b784caf96e010 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 3 Feb 2019 22:10:43 +0900 Subject: [PATCH 62/86] test: Reduce warnings on building test-root Note: all warnings are tested on test-warnings. --- tests/roots/test-root/autodoc.txt | 8 ------ tests/roots/test-root/autodoc_fodder.py | 7 ----- tests/roots/test-root/images.txt | 3 --- tests/roots/test-root/includes.txt | 1 - tests/roots/test-root/subdir/include.inc | 2 +- tests/test_builder.py | 34 +++++++++--------------- tests/test_environment.py | 2 -- tests/test_ext_apidoc.py | 2 -- 8 files changed, 14 insertions(+), 45 deletions(-) delete mode 100644 tests/roots/test-root/autodoc_fodder.py diff --git a/tests/roots/test-root/autodoc.txt b/tests/roots/test-root/autodoc.txt index 39d9dd5a7..959ab2d67 100644 --- a/tests/roots/test-root/autodoc.txt +++ b/tests/roots/test-root/autodoc.txt @@ -3,8 +3,6 @@ Autodoc tests Just testing a few autodoc possibilities... -.. automodule:: util - .. automodule:: autodoc_target :members: @@ -28,12 +26,6 @@ Just testing a few autodoc possibilities... :members: -.. automodule:: autodoc_fodder - :noindex: - - .. autoclass:: MarkupError - - .. currentmodule:: autodoc_target .. autoclass:: InstAttCls diff --git a/tests/roots/test-root/autodoc_fodder.py b/tests/roots/test-root/autodoc_fodder.py deleted file mode 100644 index e5fd74139..000000000 --- a/tests/roots/test-root/autodoc_fodder.py +++ /dev/null @@ -1,7 +0,0 @@ - -class MarkupError(object): - """ - .. note:: This is a docstring with a - small markup error which should have - correct location information. - """ diff --git a/tests/roots/test-root/images.txt b/tests/roots/test-root/images.txt index 55bc6f61c..1dc591a02 100644 --- a/tests/roots/test-root/images.txt +++ b/tests/roots/test-root/images.txt @@ -4,9 +4,6 @@ Sphinx image handling .. first, a simple test with direct filename .. image:: img.png -.. a non-existing image with direct filename -.. image:: foo.png - .. an image with path name (relative to this directory!) .. image:: subdir/img.png :height: 100 diff --git a/tests/roots/test-root/includes.txt b/tests/roots/test-root/includes.txt index e921a966c..5deec9864 100644 --- a/tests/roots/test-root/includes.txt +++ b/tests/roots/test-root/includes.txt @@ -3,7 +3,6 @@ Testing downloadable files Download :download:`img.png` here. Download :download:`this ` there. -Don't download :download:`this `. Test file and literal inclusion =============================== diff --git a/tests/roots/test-root/subdir/include.inc b/tests/roots/test-root/subdir/include.inc index 1211300c2..d89275d1d 100644 --- a/tests/roots/test-root/subdir/include.inc +++ b/tests/roots/test-root/subdir/include.inc @@ -2,4 +2,4 @@ .. Paths in included files are relative to the file that includes them -.. image:: ../root/img.png +.. image:: subdir/img.png diff --git a/tests/test_builder.py b/tests/test_builder.py index 09c64ab86..35197a8ef 100644 --- a/tests/test_builder.py +++ b/tests/test_builder.py @@ -10,11 +10,12 @@ import pytest -@pytest.mark.sphinx('dummy', srcdir="test_builder") +@pytest.mark.sphinx('dummy', srcdir="test_builder", freshenv=True) def test_incremental_reading(app): # first reading updated = app.builder.read() assert set(updated) == app.env.found_docs == set(app.env.all_docs) + assert updated == sorted(updated) # sorted by alphanumeric # test if exclude_patterns works ok assert 'subdir/excluded' not in app.env.found_docs @@ -27,29 +28,20 @@ def test_incremental_reading(app): # second reading updated = app.builder.read() - # "includes" and "images" are in there because they contain references - # to nonexisting downloadable or image files, which are given another - # chance to exist - assert set(updated) == set(['index', 'new', 'includes', 'images']) + assert set(updated) == set(['index', 'new']) assert 'autodoc' not in app.env.all_docs assert 'autodoc' not in app.env.found_docs -@pytest.mark.sphinx('dummy') -def test_env_read_docs(app): - """By default, docnames are read in alphanumeric order""" - def on_env_read_docs_1(app, env, docnames): - pass +@pytest.mark.sphinx('dummy', testroot='warnings', freshenv=True) +def test_incremental_reading_for_missing_files(app): + # first reading + updated = app.builder.read() + assert set(updated) == app.env.found_docs == set(app.env.all_docs) - app.connect('env-before-read-docs', on_env_read_docs_1) + # second reading + updated = app.builder.read() - read_docnames = app.builder.read() - assert len(read_docnames) > 2 and read_docnames == sorted(read_docnames) - - def on_env_read_docs_2(app, env, docnames): - docnames.remove('images') - - app.connect('env-before-read-docs', on_env_read_docs_2) - - read_docnames = app.builder.read() - assert len(read_docnames) == 2 + # "index" is listed up to updated because it contains references + # to nonexisting downloadable or image files + assert set(updated) == set(['index']) diff --git a/tests/test_environment.py b/tests/test_environment.py index 0226e43b8..df0aa20b0 100644 --- a/tests/test_environment.py +++ b/tests/test_environment.py @@ -17,8 +17,6 @@ from sphinx.testing.comparer import PathComparer @pytest.mark.sphinx('dummy') def test_images(app): app.build() - assert ('image file not readable: foo.png' - in app._warning.getvalue()) tree = app.env.get_doctree('images') htmlbuilder = StandaloneHTMLBuilder(app) diff --git a/tests/test_ext_apidoc.py b/tests/test_ext_apidoc.py index 5bcbb3cc4..3f5f367c4 100644 --- a/tests/test_ext_apidoc.py +++ b/tests/test_ext_apidoc.py @@ -49,7 +49,6 @@ def apidoc_params(request): def test_simple(make_app, apidoc): outdir = apidoc.outdir assert (outdir / 'conf.py').isfile() - assert (outdir / 'autodoc_fodder.rst').isfile() assert (outdir / 'index.rst').isfile() app = make_app('text', srcdir=outdir) @@ -273,7 +272,6 @@ def test_excludes_module_should_not_be_skipped(apidoc): def test_multibyte_parameters(make_app, apidoc): outdir = apidoc.outdir assert (outdir / 'conf.py').isfile() - assert (outdir / 'autodoc_fodder.rst').isfile() assert (outdir / 'index.rst').isfile() conf_py = (outdir / 'conf.py').text() From 9afe93d8f6ce0d9188de6ccb582cb72994d6d7c3 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 16:45:57 +0900 Subject: [PATCH 63/86] refactor: test: Remove unused settings from conf.py --- tests/roots/test-build-htmlhelp/make.bat | 64 ----------------------- tests/roots/test-domain-cpp/conf.py | 1 - tests/roots/test-domain-js/conf.py | 1 - tests/roots/test-domain-py/conf.py | 1 - tests/roots/test-footnotes/conf.py | 1 - tests/roots/test-manpage_url/conf.py | 1 - tests/roots/test-maxlistdepth/conf.py | 1 - tests/roots/test-numfig/conf.py | 1 - tests/roots/test-toctree-empty/conf.py | 1 - tests/roots/test-toctree-glob/conf.py | 1 - tests/roots/test-toctree-maxdepth/conf.py | 1 - 11 files changed, 74 deletions(-) delete mode 100644 tests/roots/test-build-htmlhelp/make.bat diff --git a/tests/roots/test-build-htmlhelp/make.bat b/tests/roots/test-build-htmlhelp/make.bat deleted file mode 100644 index 333fd1439..000000000 --- a/tests/roots/test-build-htmlhelp/make.bat +++ /dev/null @@ -1,64 +0,0 @@ -@echo off -setlocal - -pushd %~dp0 - -set this=%~n0 - -if not defined PYTHON set PYTHON=py - -if not defined SPHINXBUILD ( - %PYTHON% -c "import sphinx" > nul 2> nul - if errorlevel 1 ( - echo Installing sphinx with %PYTHON% - %PYTHON% -m pip install sphinx - if errorlevel 1 exit /B - ) - set SPHINXBUILD=%PYTHON% -c "import sphinx.cmd.build, sys; sys.exit(sphinx.cmd.build.main())" -) - -rem Search for HHC in likely places -set HTMLHELP= -where hhc /q && set HTMLHELP=hhc && goto :skiphhcsearch -where /R ..\externals hhc > "%TEMP%\hhc.loc" 2> nul && set /P HTMLHELP= < "%TEMP%\hhc.loc" & del "%TEMP%\hhc.loc" -if not exist "%HTMLHELP%" where /R "%ProgramFiles(x86)%" hhc > "%TEMP%\hhc.loc" 2> nul && set /P HTMLHELP= < "%TEMP%\hhc.loc" & del "%TEMP%\hhc.loc" -if not exist "%HTMLHELP%" where /R "%ProgramFiles%" hhc > "%TEMP%\hhc.loc" 2> nul && set /P HTMLHELP= < "%TEMP%\hhc.loc" & del "%TEMP%\hhc.loc" -if not exist "%HTMLHELP%" ( - echo. - echo.The HTML Help Workshop was not found. Set the HTMLHELP variable - echo.to the path to hhc.exe or download and install it from - echo.http://msdn.microsoft.com/en-us/library/ms669985 - exit /B 1 -) -echo hhc.exe path: %HTMLHELP% - -if "%BUILDDIR%" EQU "" set BUILDDIR=build - -%SPHINXBUILD% >nul 2> nul -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - popd - exit /B 1 -) - -set SPHINXOPTS=-D html_theme_options.body_max_width=none %SPHINXOPTS% - -cmd /S /C "%SPHINXBUILD% %SPHINXOPTS% -bhtmlhelp -dbuild\doctrees . "%BUILDDIR%\htmlhelp" - -"%HTMLHELP%" "%BUILDDIR%\htmlhelp\test.hhp" -rem hhc.exe seems to always exit with code 1, reset to 0 for less than 2 -if not errorlevel 2 cmd /C exit /b 0 - -echo. -if errorlevel 1 ( - echo.Build failed (exit code %ERRORLEVEL%^), check for error messages - echo.above. Any output will be found in %BUILDDIR%\%1 -) else ( - echo.Build succeeded. All output should be in %BUILDDIR%\%1 -) - -popd diff --git a/tests/roots/test-domain-cpp/conf.py b/tests/roots/test-domain-cpp/conf.py index 46bb290c9..a45d22e28 100644 --- a/tests/roots/test-domain-cpp/conf.py +++ b/tests/roots/test-domain-cpp/conf.py @@ -1,2 +1 @@ -html_theme = 'classic' exclude_patterns = ['_build'] diff --git a/tests/roots/test-domain-js/conf.py b/tests/roots/test-domain-js/conf.py index 46bb290c9..a45d22e28 100644 --- a/tests/roots/test-domain-js/conf.py +++ b/tests/roots/test-domain-js/conf.py @@ -1,2 +1 @@ -html_theme = 'classic' exclude_patterns = ['_build'] diff --git a/tests/roots/test-domain-py/conf.py b/tests/roots/test-domain-py/conf.py index 46bb290c9..a45d22e28 100644 --- a/tests/roots/test-domain-py/conf.py +++ b/tests/roots/test-domain-py/conf.py @@ -1,2 +1 @@ -html_theme = 'classic' exclude_patterns = ['_build'] diff --git a/tests/roots/test-footnotes/conf.py b/tests/roots/test-footnotes/conf.py index 46bb290c9..a45d22e28 100644 --- a/tests/roots/test-footnotes/conf.py +++ b/tests/roots/test-footnotes/conf.py @@ -1,2 +1 @@ -html_theme = 'classic' exclude_patterns = ['_build'] diff --git a/tests/roots/test-manpage_url/conf.py b/tests/roots/test-manpage_url/conf.py index 46bb290c9..a45d22e28 100644 --- a/tests/roots/test-manpage_url/conf.py +++ b/tests/roots/test-manpage_url/conf.py @@ -1,2 +1 @@ -html_theme = 'classic' exclude_patterns = ['_build'] diff --git a/tests/roots/test-maxlistdepth/conf.py b/tests/roots/test-maxlistdepth/conf.py index b04872cea..a3b12a228 100644 --- a/tests/roots/test-maxlistdepth/conf.py +++ b/tests/roots/test-maxlistdepth/conf.py @@ -1,4 +1,3 @@ -html_theme = 'classic' exclude_patterns = ['_build'] latex_elements = { diff --git a/tests/roots/test-numfig/conf.py b/tests/roots/test-numfig/conf.py index 46bb290c9..a45d22e28 100644 --- a/tests/roots/test-numfig/conf.py +++ b/tests/roots/test-numfig/conf.py @@ -1,2 +1 @@ -html_theme = 'classic' exclude_patterns = ['_build'] diff --git a/tests/roots/test-toctree-empty/conf.py b/tests/roots/test-toctree-empty/conf.py index 171892c11..bda61da95 100644 --- a/tests/roots/test-toctree-empty/conf.py +++ b/tests/roots/test-toctree-empty/conf.py @@ -1,3 +1,2 @@ -html_theme = 'classic' exclude_patterns = ['_build'] templates_path = ['_templates'] diff --git a/tests/roots/test-toctree-glob/conf.py b/tests/roots/test-toctree-glob/conf.py index 46bb290c9..a45d22e28 100644 --- a/tests/roots/test-toctree-glob/conf.py +++ b/tests/roots/test-toctree-glob/conf.py @@ -1,2 +1 @@ -html_theme = 'classic' exclude_patterns = ['_build'] diff --git a/tests/roots/test-toctree-maxdepth/conf.py b/tests/roots/test-toctree-maxdepth/conf.py index 46bb290c9..a45d22e28 100644 --- a/tests/roots/test-toctree-maxdepth/conf.py +++ b/tests/roots/test-toctree-maxdepth/conf.py @@ -1,2 +1 @@ -html_theme = 'classic' exclude_patterns = ['_build'] From 3b49f9fe3dc980c2c53933d1ea88b7611202790d Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 15 Feb 2019 01:44:12 +0900 Subject: [PATCH 64/86] Separate htmlhelp to sphinxcontrib package --- CHANGES | 1 + doc/extdev/index.rst | 5 + setup.py | 1 + sphinx/application.py | 2 +- sphinx/builders/htmlhelp.py | 372 +--------------------- tests/roots/test-build-htmlhelp/conf.py | 1 - tests/roots/test-build-htmlhelp/index.rst | 19 -- tests/roots/test-htmlhelp-hhc/bar.rst | 2 - tests/roots/test-htmlhelp-hhc/baz.rst | 2 - tests/roots/test-htmlhelp-hhc/conf.py | 1 - tests/roots/test-htmlhelp-hhc/foo.rst | 6 - tests/roots/test-htmlhelp-hhc/index.rst | 15 - tests/test_build.py | 2 +- tests/test_build_htmlhelp.py | 131 -------- 14 files changed, 26 insertions(+), 534 deletions(-) delete mode 100644 tests/roots/test-build-htmlhelp/conf.py delete mode 100644 tests/roots/test-build-htmlhelp/index.rst delete mode 100644 tests/roots/test-htmlhelp-hhc/bar.rst delete mode 100644 tests/roots/test-htmlhelp-hhc/baz.rst delete mode 100644 tests/roots/test-htmlhelp-hhc/conf.py delete mode 100644 tests/roots/test-htmlhelp-hhc/foo.rst delete mode 100644 tests/roots/test-htmlhelp-hhc/index.rst delete mode 100644 tests/test_build_htmlhelp.py diff --git a/CHANGES b/CHANGES index fd40e17dc..20585e834 100644 --- a/CHANGES +++ b/CHANGES @@ -23,6 +23,7 @@ Dependencies - sphinxcontrib.applehelp - sphinxcontrib.devhelp + - sphinxcontrib.htmlhelp - sphinxcontrib.jsmath - sphinxcontrib.qthelp diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst index 4cc868c77..7203ec6fa 100644 --- a/doc/extdev/index.rst +++ b/doc/extdev/index.rst @@ -295,6 +295,11 @@ The following is a list of deprecated interfaces. - 4.0 - ``sphinx.builders.singlehtml.SingleFileHTMLBuilder`` + * - ``sphinx.builders.htmlhelp`` + - 2.0 + - 4.0 + - ``sphinxcontrib.htmlhelp`` + * - ``sphinx.builders.htmlhelp.HTMLHelpBuilder.open_file()`` - 2.0 - 4.0 diff --git a/setup.py b/setup.py index 5b2c38e8c..cdcbbc4f7 100644 --- a/setup.py +++ b/setup.py @@ -18,6 +18,7 @@ install_requires = [ 'sphinxcontrib-applehelp', 'sphinxcontrib-devhelp', 'sphinxcontrib-jsmath', + 'sphinxcontrib-htmlhelp', 'sphinxcontrib-qthelp', 'Jinja2>=2.3', 'Pygments>=2.0', diff --git a/sphinx/application.py b/sphinx/application.py index dfeeea6dc..296104e21 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -67,7 +67,6 @@ builtin_extensions = ( 'sphinx.builders.dummy', 'sphinx.builders.gettext', 'sphinx.builders.html', - 'sphinx.builders.htmlhelp', 'sphinx.builders.latex', 'sphinx.builders.linkcheck', 'sphinx.builders.manpage', @@ -107,6 +106,7 @@ builtin_extensions = ( # 1st party extensions 'sphinxcontrib.applehelp', 'sphinxcontrib.devhelp', + 'sphinxcontrib.htmlhelp', 'sphinxcontrib.qthelp', # Strictly, alabaster theme is not a builtin extension, # but it is loaded automatically to use it as default theme. diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py index ce30affba..2e7e8f083 100644 --- a/sphinx/builders/htmlhelp.py +++ b/sphinx/builders/htmlhelp.py @@ -9,374 +9,36 @@ :license: BSD, see LICENSE for details. """ -import html -import os import warnings -from os import path -from docutils import nodes +from sphinxcontrib.htmlhelp import ( + chm_locales, chm_htmlescape, HTMLHelpBuilder, default_htmlhelp_basename +) + +from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias -from sphinx import addnodes -from sphinx import package_dir -from sphinx.builders.html import StandaloneHTMLBuilder -from sphinx.deprecation import RemovedInSphinx40Warning -from sphinx.environment.adapters.indexentries import IndexEntries -from sphinx.locale import __ -from sphinx.util import logging -from sphinx.util import progress_message -from sphinx.util.fileutil import copy_asset_file -from sphinx.util.nodes import NodeMatcher -from sphinx.util.osutil import make_filename_from_project, relpath -from sphinx.util.template import SphinxRenderer if False: # For type annotation - from typing import Any, Dict, IO, List, Match, Tuple # NOQA + from typing import Any, Dict # NOQA from sphinx.application import Sphinx # NOQA - from sphinx.config import Config # NOQA -logger = logging.getLogger(__name__) - -template_dir = path.join(package_dir, 'templates', 'htmlhelp') - - -# Project file (*.hhp) template. 'outname' is the file basename (like -# the pythlp in pythlp.hhp); 'version' is the doc version number (like -# the 2.2 in Python 2.2). -# The magical numbers in the long line under [WINDOWS] set most of the -# user-visible features (visible buttons, tabs, etc). -# About 0x10384e: This defines the buttons in the help viewer. The -# following defns are taken from htmlhelp.h. Not all possibilities -# actually work, and not all those that work are available from the Help -# Workshop GUI. In particular, the Zoom/Font button works and is not -# available from the GUI. The ones we're using are marked with 'x': -# -# 0x000002 Hide/Show x -# 0x000004 Back x -# 0x000008 Forward x -# 0x000010 Stop -# 0x000020 Refresh -# 0x000040 Home x -# 0x000080 Forward -# 0x000100 Back -# 0x000200 Notes -# 0x000400 Contents -# 0x000800 Locate x -# 0x001000 Options x -# 0x002000 Print x -# 0x004000 Index -# 0x008000 Search -# 0x010000 History -# 0x020000 Favorites -# 0x040000 Jump 1 -# 0x080000 Jump 2 -# 0x100000 Zoom/Font x -# 0x200000 TOC Next -# 0x400000 TOC Prev - -object_sitemap = '''\ - - - - -''' - -# The following list includes only languages supported by Sphinx. See -# https://docs.microsoft.com/en-us/previous-versions/windows/embedded/ms930130(v=msdn.10) -# for more. -chm_locales = { - # lang: LCID, encoding - 'ca': (0x403, 'cp1252'), - 'cs': (0x405, 'cp1250'), - 'da': (0x406, 'cp1252'), - 'de': (0x407, 'cp1252'), - 'en': (0x409, 'cp1252'), - 'es': (0x40a, 'cp1252'), - 'et': (0x425, 'cp1257'), - 'fa': (0x429, 'cp1256'), - 'fi': (0x40b, 'cp1252'), - 'fr': (0x40c, 'cp1252'), - 'hr': (0x41a, 'cp1250'), - 'hu': (0x40e, 'cp1250'), - 'it': (0x410, 'cp1252'), - 'ja': (0x411, 'cp932'), - 'ko': (0x412, 'cp949'), - 'lt': (0x427, 'cp1257'), - 'lv': (0x426, 'cp1257'), - 'nl': (0x413, 'cp1252'), - 'no_NB': (0x414, 'cp1252'), - 'pl': (0x415, 'cp1250'), - 'pt_BR': (0x416, 'cp1252'), - 'ru': (0x419, 'cp1251'), - 'sk': (0x41b, 'cp1250'), - 'sl': (0x424, 'cp1250'), - 'sv': (0x41d, 'cp1252'), - 'tr': (0x41f, 'cp1254'), - 'uk_UA': (0x422, 'cp1251'), - 'zh_CN': (0x804, 'cp936'), - 'zh_TW': (0x404, 'cp950'), -} - - -def chm_htmlescape(s, quote=True): - # type: (str, bool) -> str - """ - chm_htmlescape() is a wrapper of html.escape(). - .hhc/.hhk files don't recognize hex escaping, we need convert - hex escaping to decimal escaping. for example: ``'`` -> ``'`` - html.escape() may generates a hex escaping ``'`` for single - quote ``'``, this wrapper fixes this. - """ - s = html.escape(s, quote) - s = s.replace(''', ''') # re-escape as decimal - return s - - -class ToCTreeVisitor(nodes.NodeVisitor): - def __init__(self, document): - # type: (nodes.document) -> None - super().__init__(document) - self.body = [] # type: List[str] - self.depth = 0 - - def append(self, text): - # type: (str) -> None - indent = ' ' * (self.depth - 1) - self.body.append(indent + text) - - def astext(self): - # type: () -> str - return '\n'.join(self.body) - - def unknown_visit(self, node): - # type: (nodes.Node) -> None - pass - - def unknown_departure(self, node): - # type: (nodes.Node) -> None - pass - - def visit_bullet_list(self, node): - # type: (nodes.Element) -> None - if self.depth > 0: - self.append('
      ') - - self.depth += 1 - - def depart_bullet_list(self, node): - # type: (nodes.Element) -> None - self.depth -= 1 - if self.depth > 0: - self.append('
    ') - - def visit_list_item(self, node): - # type: (nodes.Element) -> None - self.append('
  • ') - self.depth += 1 - - def depart_list_item(self, node): - # type: (nodes.Element) -> None - self.depth -= 1 - self.append('
  • ') - - def visit_reference(self, node): - # type: (nodes.Element) -> None - title = chm_htmlescape(node.astext(), True) - self.append('') - self.append(' ' % title) - self.append(' ' % node['refuri']) - self.append('') - raise nodes.SkipNode - - -class HTMLHelpBuilder(StandaloneHTMLBuilder): - """ - Builder that also outputs Windows HTML help project, contents and - index files. Adapted from the original Doc/tools/prechm.py. - """ - name = 'htmlhelp' - epilog = __('You can now run HTML Help Workshop with the .htp file in ' - '%(outdir)s.') - - # don't copy the reST source - copysource = False - supported_image_types = ['image/png', 'image/gif', 'image/jpeg'] - - # don't add links - add_permalinks = False - # don't add sidebar etc. - embedded = True - - # don't generate search index or include search page - search = False - - lcid = 0x409 - encoding = 'cp1252' - - def init(self): - # type: () -> None - # the output files for HTML help is .html by default - self.out_suffix = '.html' - self.link_suffix = '.html' - super().init() - # determine the correct locale setting - locale = chm_locales.get(self.config.language) - if locale is not None: - self.lcid, self.encoding = locale - - def open_file(self, outdir, basename, mode='w'): - # type: (str, str, str) -> IO - # open a file with the correct encoding for the selected language - warnings.warn('HTMLHelpBuilder.open_file() is deprecated.', - RemovedInSphinx40Warning) - return open(path.join(outdir, basename), mode, encoding=self.encoding, - errors='xmlcharrefreplace') - - def update_page_context(self, pagename, templatename, ctx, event_arg): - # type: (str, str, Dict, str) -> None - ctx['encoding'] = self.encoding - - def handle_finish(self): - # type: () -> None - self.copy_stopword_list() - self.build_project_file() - self.build_toc_file() - self.build_hhx(self.outdir, self.config.htmlhelp_basename) - - def write_doc(self, docname, doctree): - # type: (str, nodes.document) -> None - for node in doctree.traverse(nodes.reference): - # add ``target=_blank`` attributes to external links - if node.get('internal') is None and 'refuri' in node: - node['target'] = '_blank' - - super().write_doc(docname, doctree) - - def render(self, name, context): - # type: (str, Dict) -> str - template = SphinxRenderer(template_dir) - return template.render(name, context) - - @progress_message(__('copying stopword list')) - def copy_stopword_list(self): - # type: () -> None - """Copy a stopword list (.stp) to outdir. - - The stopword list contains a list of words the full text search facility - shouldn't index. Note that this list must be pretty small. Different - versions of the MS docs claim the file has a maximum size of 256 or 512 - bytes (including \r\n at the end of each line). Note that "and", "or", - "not" and "near" are operators in the search language, so no point - indexing them even if we wanted to. - """ - template = path.join(template_dir, 'project.stp') - filename = path.join(self.outdir, self.config.htmlhelp_basename + '.stp') - copy_asset_file(template, filename) - - @progress_message(__('writing project file')) - def build_project_file(self): - # type: () -> None - """Create a project file (.hhp) on outdir.""" - # scan project files - project_files = [] # type: List[str] - for root, dirs, files in os.walk(self.outdir): - dirs.sort() - files.sort() - in_staticdir = root.startswith(path.join(self.outdir, '_static')) - for fn in sorted(files): - if (in_staticdir and not fn.endswith('.js')) or fn.endswith('.html'): - fn = relpath(path.join(root, fn), self.outdir) - project_files.append(fn.replace(os.sep, '\\')) - - filename = path.join(self.outdir, self.config.htmlhelp_basename + '.hhp') - with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f: - context = { - 'outname': self.config.htmlhelp_basename, - 'title': self.config.html_title, - 'version': self.config.version, - 'project': self.config.project, - 'lcid': self.lcid, - 'master_doc': self.config.master_doc + self.out_suffix, - 'files': project_files, - } - body = self.render('project.hhp', context) - f.write(body) - - @progress_message(__('writing TOC file')) - def build_toc_file(self): - # type: () -> None - """Create a ToC file (.hhp) on outdir.""" - filename = path.join(self.outdir, self.config.htmlhelp_basename + '.hhc') - with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f: - toctree = self.env.get_and_resolve_doctree(self.config.master_doc, self, - prune_toctrees=False) - visitor = ToCTreeVisitor(toctree) - matcher = NodeMatcher(addnodes.compact_paragraph, toctree=True) - for node in toctree.traverse(matcher): # type: addnodes.compact_paragraph - node.walkabout(visitor) - - context = { - 'body': visitor.astext(), - 'suffix': self.out_suffix, - 'short_title': self.config.html_short_title, - 'master_doc': self.config.master_doc, - 'domain_indices': self.domain_indices, - } - f.write(self.render('project.hhc', context)) - - def build_hhx(self, outdir, outname): - # type: (str, str) -> None - logger.info(__('writing index file...')) - index = IndexEntries(self.env).create_index(self) - filename = path.join(outdir, outname + '.hhk') - with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f: - f.write('
      \n') - - def write_index(title, refs, subitems): - # type: (str, List[Tuple[str, str]], List[Tuple[str, List[Tuple[str, str]]]]) -> None # NOQA - def write_param(name, value): - # type: (str, str) -> None - item = ' \n' % (name, value) - f.write(item) - title = chm_htmlescape(title, True) - f.write('
    • \n') - write_param('Keyword', title) - if len(refs) == 0: - write_param('See Also', title) - elif len(refs) == 1: - write_param('Local', refs[0][1]) - else: - for i, ref in enumerate(refs): - # XXX: better title? - write_param('Name', '[%d] %s' % (i, ref[1])) - write_param('Local', ref[1]) - f.write('\n') - if subitems: - f.write('
        ') - for subitem in subitems: - write_index(subitem[0], subitem[1], []) - f.write('
      ') - for (key, group) in index: - for title, (refs, subitems, key_) in group: - write_index(title, refs, subitems) - f.write('
    \n') - - -def default_htmlhelp_basename(config): - # type: (Config) -> str - """Better default htmlhelp_basename setting.""" - return make_filename_from_project(config.project) + 'doc' +deprecated_alias('sphinx.builders.devhelp', + { + 'chm_locales': chm_locales, + 'chm_htmlescape': chm_htmlescape, + 'HTMLHelpBuilder': HTMLHelpBuilder, + 'default_htmlhelp_basename': default_htmlhelp_basename, + }, + RemovedInSphinx40Warning) def setup(app): # type: (Sphinx) -> Dict[str, Any] - app.setup_extension('sphinx.builders.html') - app.add_builder(HTMLHelpBuilder) - - app.add_config_value('htmlhelp_basename', default_htmlhelp_basename, None) - app.add_config_value('htmlhelp_file_suffix', None, 'html', [str]) - app.add_config_value('htmlhelp_link_suffix', None, 'html', [str]) + warnings.warn('sphinx.builders.htmlhelp has been moved to sphinxcontrib-htmlhelp.', + RemovedInSphinx40Warning) + app.setup_extension('sphinxcontrib.htmlhelp') return { 'version': 'builtin', diff --git a/tests/roots/test-build-htmlhelp/conf.py b/tests/roots/test-build-htmlhelp/conf.py deleted file mode 100644 index 9b6a55417..000000000 --- a/tests/roots/test-build-htmlhelp/conf.py +++ /dev/null @@ -1 +0,0 @@ -project = 'test' diff --git a/tests/roots/test-build-htmlhelp/index.rst b/tests/roots/test-build-htmlhelp/index.rst deleted file mode 100644 index 2d9e563d6..000000000 --- a/tests/roots/test-build-htmlhelp/index.rst +++ /dev/null @@ -1,19 +0,0 @@ -Index markup ------------- - -.. index:: - single: entry - pair: entry; pair - double: entry; double - triple: index; entry; triple - keyword: with - see: from; to - seealso: fromalso; toalso - -.. index:: - !Main, !Other - !single: entry; pair - -.. index:: triple-quoted string, Unicode Consortium, raw string - single: """; string literal - single: '''; string literal diff --git a/tests/roots/test-htmlhelp-hhc/bar.rst b/tests/roots/test-htmlhelp-hhc/bar.rst deleted file mode 100644 index 0d1785bcf..000000000 --- a/tests/roots/test-htmlhelp-hhc/bar.rst +++ /dev/null @@ -1,2 +0,0 @@ -bar ---- diff --git a/tests/roots/test-htmlhelp-hhc/baz.rst b/tests/roots/test-htmlhelp-hhc/baz.rst deleted file mode 100644 index 69fe26493..000000000 --- a/tests/roots/test-htmlhelp-hhc/baz.rst +++ /dev/null @@ -1,2 +0,0 @@ -baz ---- diff --git a/tests/roots/test-htmlhelp-hhc/conf.py b/tests/roots/test-htmlhelp-hhc/conf.py deleted file mode 100644 index 20447e040..000000000 --- a/tests/roots/test-htmlhelp-hhc/conf.py +++ /dev/null @@ -1 +0,0 @@ -html_short_title = "Sphinx's documentation" diff --git a/tests/roots/test-htmlhelp-hhc/foo.rst b/tests/roots/test-htmlhelp-hhc/foo.rst deleted file mode 100644 index 6e06b7e69..000000000 --- a/tests/roots/test-htmlhelp-hhc/foo.rst +++ /dev/null @@ -1,6 +0,0 @@ -foo ---- - -.. toctree:: - - bar diff --git a/tests/roots/test-htmlhelp-hhc/index.rst b/tests/roots/test-htmlhelp-hhc/index.rst deleted file mode 100644 index 9b43b4129..000000000 --- a/tests/roots/test-htmlhelp-hhc/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -test-htmlhelp-domain_indices ----------------------------- - -section -~~~~~~~ - -.. py:module:: sphinx - -subsection -^^^^^^^^^^ - -.. toctree:: - - foo - baz diff --git a/tests/test_build.py b/tests/test_build.py index 61fb2fed6..b25254529 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -60,7 +60,7 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir): "buildername", [ # note: no 'html' - if it's ok with dirhtml it's ok with html - 'dirhtml', 'singlehtml', 'pickle', 'json', 'text', 'htmlhelp', + 'dirhtml', 'singlehtml', 'pickle', 'json', 'text', 'changes', 'xml', 'pseudoxml', 'linkcheck', ], ) diff --git a/tests/test_build_htmlhelp.py b/tests/test_build_htmlhelp.py deleted file mode 100644 index 4ad244a4e..000000000 --- a/tests/test_build_htmlhelp.py +++ /dev/null @@ -1,131 +0,0 @@ -""" - test_build_htmlhelp - ~~~~~~~~~~~~~~~~~~~ - - Test the HTML Help builder and check output against XPath. - - :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -import re - -import pytest -from html5lib import HTMLParser - -from sphinx.builders.htmlhelp import chm_htmlescape, default_htmlhelp_basename -from sphinx.config import Config - - -@pytest.mark.sphinx('htmlhelp', testroot='basic') -def test_build_htmlhelp(app, status, warning): - app.build() - - hhp = (app.outdir / 'pythondoc.hhp').text() - assert 'Compiled file=pythondoc.chm' in hhp - assert 'Contents file=pythondoc.hhc' in hhp - assert 'Default Window=pythondoc' in hhp - assert 'Default topic=index.html' in hhp - assert 'Full text search stop list file=pythondoc.stp' in hhp - assert 'Index file=pythondoc.hhk' in hhp - assert 'Language=0x409' in hhp - assert 'Title=Python documentation' in hhp - assert ('pythondoc="Python documentation","pythondoc.hhc",' - '"pythondoc.hhk","index.html","index.html",,,,,' - '0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0' in hhp) - - files = ['genindex.html', 'index.html', '_static\\alabaster.css', '_static\\basic.css', - '_static\\custom.css', '_static\\file.png', '_static\\minus.png', - '_static\\plus.png', '_static\\pygments.css'] - assert '[FILES]\n%s' % '\n'.join(files) in hhp - - -@pytest.mark.sphinx('htmlhelp', testroot='basic') -def test_default_htmlhelp_file_suffix(app, warning): - assert app.builder.out_suffix == '.html' - - -@pytest.mark.sphinx('htmlhelp', testroot='basic', - confoverrides={'htmlhelp_file_suffix': '.htm'}) -def test_htmlhelp_file_suffix(app, warning): - assert app.builder.out_suffix == '.htm' - - -def test_default_htmlhelp_basename(): - config = Config({'project': 'Sphinx Documentation'}) - config.init_values() - assert default_htmlhelp_basename(config) == 'sphinxdoc' - - -@pytest.mark.sphinx('htmlhelp', testroot='build-htmlhelp') -def test_chm(app): - app.build() - - # check .hhk file - outname = app.builder.config.htmlhelp_basename - hhk_path = str(app.outdir / outname + '.hhk') - - with open(hhk_path, 'rb') as f: - data = f.read() - m = re.search(br'&#[xX][0-9a-fA-F]+;', data) - assert m is None, 'Hex escaping exists in .hhk file: ' + str(m.group(0)) - - -@pytest.mark.sphinx('htmlhelp', testroot='htmlhelp-hhc') -def test_htmlhelp_hhc(app): - app.build() - - def assert_sitemap(node, name, filename): - assert node.tag == 'object' - assert len(node) == 2 - assert node[0].tag == 'param' - assert node[0].attrib == {'name': 'Name', 'value': name} - assert node[1].tag == 'param' - assert node[1].attrib == {'name': 'Local', 'value': filename} - - # .hhc file - hhc = (app.outdir / 'pythondoc.hhc').text() - tree = HTMLParser(namespaceHTMLElements=False).parse(hhc) - items = tree.find('.//body/ul') - assert len(items) == 4 - - # index - assert items[0].tag == 'li' - assert len(items[0]) == 1 - assert_sitemap(items[0][0], "Sphinx's documentation", 'index.html') - - # py-modindex - assert items[1].tag == 'li' - assert len(items[1]) == 1 - assert_sitemap(items[1][0], 'Python Module Index', 'py-modindex.html') - - # toctree - assert items[2].tag == 'li' - assert len(items[2]) == 2 - assert_sitemap(items[2][0], 'foo', 'foo.html') - - assert items[2][1].tag == 'ul' - assert len(items[2][1]) == 1 - assert items[2][1][0].tag == 'li' - assert_sitemap(items[2][1][0][0], 'bar', 'bar.html') - - assert items[3].tag == 'li' - assert len(items[3]) == 1 - assert_sitemap(items[3][0], 'baz', 'baz.html') - - # single quotes should be escaped as decimal (') - assert "Sphinx's documentation" in hhc - - -def test_chm_htmlescape(): - assert chm_htmlescape('Hello world') == 'Hello world' - assert chm_htmlescape(u'Unicode 文字') == u'Unicode 文字' - assert chm_htmlescape('E') == '&#x45' - - assert chm_htmlescape(' "world"') == '<Hello> "world"' - assert chm_htmlescape(' "world"', True) == '<Hello> "world"' - assert chm_htmlescape(' "world"', False) == '<Hello> "world"' - - assert chm_htmlescape("Hello 'world'") == "Hello 'world'" - assert chm_htmlescape("Hello 'world'", True) == "Hello 'world'" - assert chm_htmlescape("Hello 'world'", False) == "Hello 'world'" From 686486498c228cc83fdd37a42861d9fac145d33e Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 15 Feb 2019 01:58:09 +0900 Subject: [PATCH 65/86] Closes #1148: autodoc: Add autodecorator directive for decorators --- CHANGES | 1 + doc/usage/extensions/autodoc.rst | 4 +++- sphinx/ext/autodoc/__init__.py | 18 +++++++++++++++ .../test-ext-autodoc/target/decorator.py | 16 +++++++++++++ tests/test_autodoc.py | 23 +++++++++++++++++++ 5 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 tests/roots/test-ext-autodoc/target/decorator.py diff --git a/CHANGES b/CHANGES index fd40e17dc..49c8b667f 100644 --- a/CHANGES +++ b/CHANGES @@ -166,6 +166,7 @@ Features added * #5533: autodoc: :confval:`autodoc_default_options` supports ``member-order`` * #5394: autodoc: Display readable names in type annotations for mocked objects * #5459: autodoc: :confval:`autodoc_default_options` accepts ``True`` as a value +* #1148: autodoc: Add :rst:dir:`autodecorator` directive for decorators * #5635: autosummary: Add :confval:`autosummary_mock_imports` to mock external libraries on importing targets * #4018: htmlhelp: Add :confval:`htmlhelp_file_suffix` and diff --git a/doc/usage/extensions/autodoc.rst b/doc/usage/extensions/autodoc.rst index 45789e0a2..cb41cb07d 100644 --- a/doc/usage/extensions/autodoc.rst +++ b/doc/usage/extensions/autodoc.rst @@ -237,6 +237,7 @@ inserting them into the page source under a suitable :rst:dir:`py:module`, .. rst:directive:: autofunction + autodecorator autodata automethod autoattribute @@ -293,10 +294,11 @@ inserting them into the page source under a suitable :rst:dir:`py:module`, docstrings. .. versionchanged:: 1.1 Comment docs are now allowed on the same line after an assignment. - .. versionchanged:: 1.2 :rst:dir:`autodata` and :rst:dir:`autoattribute` have an ``annotation`` option. + .. versionchanged:: 2.0 + :rst:dir:`autodecorator` added. .. note:: diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 9e65e595d..f76faf322 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1032,6 +1032,23 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ pass +class DecoratorDocumenter(FunctionDocumenter): + """ + Specialized Documenter subclass for decorator functions. + """ + objtype = 'decorator' + + # must be lower than FunctionDocumenter + priority = -1 + + def format_args(self): + args = super().format_args() + if ',' in args: + return args + else: + return None + + class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: ignore """ Specialized Documenter subclass for classes. @@ -1461,6 +1478,7 @@ def setup(app): app.add_autodocumenter(ExceptionDocumenter) app.add_autodocumenter(DataDocumenter) app.add_autodocumenter(FunctionDocumenter) + app.add_autodocumenter(DecoratorDocumenter) app.add_autodocumenter(MethodDocumenter) app.add_autodocumenter(AttributeDocumenter) app.add_autodocumenter(InstanceAttributeDocumenter) diff --git a/tests/roots/test-ext-autodoc/target/decorator.py b/tests/roots/test-ext-autodoc/target/decorator.py new file mode 100644 index 000000000..4ccfedf28 --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/decorator.py @@ -0,0 +1,16 @@ +def deco1(func): + """docstring for deco1""" + def wrapper(): + return func() + + return wrapper + + +def deco2(condition, message): + """docstring for deco2""" + def decorator(func): + def wrapper(): + return func() + + return wrapper + return decorator diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 7758f442d..943ba5a7a 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -602,6 +602,29 @@ def test_generate(): 'Class.meth', more_content=add_content) +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_decorator(app): + actual = do_autodoc(app, 'decorator', 'target.decorator.deco1') + assert list(actual) == [ + '', + '.. py:decorator:: deco1', + ' :module: target.decorator', + '', + ' docstring for deco1', + ' ' + ] + + actual = do_autodoc(app, 'decorator', 'target.decorator.deco2') + assert list(actual) == [ + '', + '.. py:decorator:: deco2(condition, message)', + ' :module: target.decorator', + '', + ' docstring for deco2', + ' ' + ] + + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_exception(app): actual = do_autodoc(app, 'exception', 'target.CustomEx') From a6d345f95da246ec7589d34248001df646b5d445 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 14 Feb 2019 22:36:34 +0900 Subject: [PATCH 66/86] Replace :index: role by class based implementation --- CHANGES | 1 + doc/extdev/index.rst | 5 +++++ sphinx/roles.py | 30 ++++++++++++++++++++++++++++-- sphinx/util/docutils.py | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 20585e834..9488aedff 100644 --- a/CHANGES +++ b/CHANGES @@ -116,6 +116,7 @@ Deprecated * ``sphinx.io.SphinxRSTFileInput`` * ``sphinx.registry.SphinxComponentRegistry.add_source_input()`` * ``sphinx.roles.abbr_role()`` +* ``sphinx.roles.index_role()`` * ``sphinx.testing.util.remove_unicode_literal()`` * ``sphinx.util.attrdict`` * ``sphinx.util.force_decode()`` diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst index 7203ec6fa..c3061852a 100644 --- a/doc/extdev/index.rst +++ b/doc/extdev/index.rst @@ -380,6 +380,11 @@ The following is a list of deprecated interfaces. - 4.0 - ``sphinx.roles.Abbreviation`` + * - ``sphinx.roles.index_role()`` + - 2.0 + - 4.0 + - ``sphinx.roles.Index`` + * - ``sphinx.testing.util.remove_unicode_literal()`` - 2.0 - 4.0 diff --git a/sphinx/roles.py b/sphinx/roles.py index e9a8786ca..d2e64c40c 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -18,7 +18,7 @@ from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.errors import SphinxError from sphinx.locale import _ from sphinx.util import ws_re -from sphinx.util.docutils import SphinxRole +from sphinx.util.docutils import ReferenceRole, SphinxRole from sphinx.util.nodes import split_explicit_title, process_index_entry, \ set_role_source_info @@ -371,6 +371,8 @@ class Abbreviation(SphinxRole): def index_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): # type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA + warnings.warn('index_role() is deprecated. Please use Index class instead.', + RemovedInSphinx40Warning, stacklevel=2) # create new reference target env = inliner.document.settings.env targetid = 'index-%s' % env.new_serialno('index') @@ -398,6 +400,30 @@ def index_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): return [indexnode, targetnode, textnode], [] +class Index(ReferenceRole): + def run(self): + # type: () -> Tuple[List[nodes.Node], List[nodes.system_message]] + target_id = 'index-%s' % self.env.new_serialno('index') + if self.has_explicit_title: + # if an explicit target is given, process it as a full entry + title = self.title + entries = process_index_entry(self.target, target_id) + else: + # otherwise we just create a single entry + if self.target.startswith('!'): + title = self.title[1:] + entries = [('single', self.target[1:], target_id, 'main', None)] + else: + title = self.title + entries = [('single', self.target, target_id, '', None)] + + index = addnodes.index(entries=entries) + target = nodes.target('', '', ids=[target_id]) + text = nodes.Text(title, title) + self.set_source_info(index) + return [index, target, text], [] + + specific_docroles = { # links to download references 'download': XRefRole(nodeclass=addnodes.download_reference), @@ -411,7 +437,7 @@ specific_docroles = { 'file': emph_literal_role, 'samp': emph_literal_role, 'abbr': Abbreviation(), - 'index': index_role, + 'index': Index(), } # type: Dict[str, RoleFunction] diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index ba5a9d0ea..71e8db268 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -421,6 +421,39 @@ class SphinxRole: """Reference to the :class:`.Config` object.""" return self.env.config + def set_source_info(self, node, lineno=None): + # type: (nodes.Node, int) -> None + if lineno is None: + lineno = self.lineno + + source_info = self.inliner.reporter.get_source_and_line(lineno) # type: ignore + node.source, node.line = source_info + + +class ReferenceRole(SphinxRole): + """A base class for reference roles. + + The reference roles can accpet ``link title `` style as a text for + the role. The parsed result: link title and target will be stored to + ``self.title`` and ``self.target``. + """ + # \x00 means the "<" was backslash-escaped + explicit_title_re = re.compile(r'^(.+?)\s*(?$', re.DOTALL) + + def __call__(self, typ, rawtext, text, lineno, inliner, options={}, content=[]): + # type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA + matched = self.explicit_title_re.match(text) + if matched: + self.has_explicit_title = True + self.title = unescape(matched.group(1)) + self.target = unescape(matched.group(2)) + else: + self.has_explicit_title = False + self.title = unescape(text) + self.target = unescape(text) + + return super().__call__(typ, rawtext, text, lineno, inliner, options, content) + class SphinxTranslator(nodes.NodeVisitor): """A base class for Sphinx translators. From 9cb40436ed51068e3543e9332a7056a3212e119b Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 14 Feb 2019 22:44:32 +0900 Subject: [PATCH 67/86] Replace :pep: and :rfc: roles by class based implementation --- CHANGES | 1 + doc/extdev/index.rst | 5 +++ sphinx/roles.py | 80 +++++++++++++++++++++++++++++++++++++++-- sphinx/util/docutils.py | 13 +++++-- 4 files changed, 95 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 9488aedff..cb4499c4e 100644 --- a/CHANGES +++ b/CHANGES @@ -117,6 +117,7 @@ Deprecated * ``sphinx.registry.SphinxComponentRegistry.add_source_input()`` * ``sphinx.roles.abbr_role()`` * ``sphinx.roles.index_role()`` +* ``sphinx.roles.indexmarkup_role()`` * ``sphinx.testing.util.remove_unicode_literal()`` * ``sphinx.util.attrdict`` * ``sphinx.util.force_decode()`` diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst index c3061852a..a7f84d2ea 100644 --- a/doc/extdev/index.rst +++ b/doc/extdev/index.rst @@ -385,6 +385,11 @@ The following is a list of deprecated interfaces. - 4.0 - ``sphinx.roles.Index`` + * - ``sphinx.roles.indexmarkup_role()`` + - 2.0 + - 4.0 + - ``sphinx.roles.PEP`` or ``sphinx.roles.RFC`` + * - ``sphinx.testing.util.remove_unicode_literal()`` - 2.0 - 4.0 diff --git a/sphinx/roles.py b/sphinx/roles.py index d2e64c40c..c36ed2613 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -182,6 +182,8 @@ class AnyXRefRole(XRefRole): def indexmarkup_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): # type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA """Role for PEP/RFC references that generate an index entry.""" + warnings.warn('indexmarkup_role() is deprecated. Please use PEP or RFC class instead.', + RemovedInSphinx40Warning, stacklevel=2) env = inliner.document.settings.env if not typ: assert env.temp_data['default_role'] @@ -245,6 +247,80 @@ def indexmarkup_role(typ, rawtext, text, lineno, inliner, options={}, content=[] raise ValueError('unknown role type: %s' % typ) +class PEP(ReferenceRole): + def run(self): + # type: () -> Tuple[List[nodes.Node], List[nodes.system_message]] + target_id = 'index-%s' % self.env.new_serialno('index') + entries = [('single', _('Python Enhancement Proposals; PEP %s') % self.target, + target_id, '', None)] + + index = addnodes.index(entries=entries) + target = nodes.target('', '', ids=[target_id]) + self.inliner.document.note_explicit_target(target) + + try: + refuri = self.build_uri() + reference = nodes.reference('', '', internal=False, refuri=refuri, classes=['pep']) + if self.has_explicit_title: + reference += nodes.strong(self.title, self.title) + else: + title = "PEP " + self.title + reference += nodes.strong(title, title) + except ValueError: + msg = self.inliner.reporter.error('invalid PEP number %s' % self.target, + line=self.lineno) + prb = self.inliner.problematic(self.rawtext, self.rawtext, msg) + return [prb], [msg] + + return [index, target, reference], [] + + def build_uri(self): + # type: () -> str + base_url = self.inliner.document.settings.pep_base_url + target, anchor = self.target.split('#', 1) + if anchor: + return base_url + 'pep-%04d#%s' % (int(target), anchor) + else: + return base_url + 'pep-%04d' % int(target) + + +class RFC(ReferenceRole): + def run(self): + # type: () -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA + target_id = 'index-%s' % self.env.new_serialno('index') + entries = [('single', 'RFC; RFC %s' % self.target, target_id, '', None)] + + index = addnodes.index(entries=entries) + target = nodes.target('', '', ids=[target_id]) + self.inliner.document.note_explicit_target(target) + + try: + refuri = self.build_uri() + reference = nodes.reference('', '', internal=False, refuri=refuri, classes=['rfc']) + if self.has_explicit_title: + reference += nodes.strong(self.title, self.title) + else: + title = "RFC " + self.title + reference += nodes.strong(title, title) + except ValueError: + msg = self.inliner.reporter.error('invalid RFC number %s' % self.target, + line=self.lineno) + prb = self.inliner.problematic(self.rawtext, self.rawtext, msg) + return [prb], [msg] + + return [index, target, reference], [] + + def build_uri(self): + # type: () -> str + target, anchor = self.target.split('#', 1) + base_url = (self.inliner.document.settings.rfc_base_url + + self.inliner.rfc_url % int(target)) + if anchor: + return base_url + '#' + anchor + else: + return base_url + + _amp_re = re.compile(r'(? Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA - self.type = typ self.rawtext = rawtext self.text = unescape(text) self.lineno = lineno @@ -403,6 +402,16 @@ class SphinxRole: self.options = options self.content = content + # guess role type + if typ: + self.type = typ.lower() + else: + self.type = self.env.temp_data.get('default_role') + if not self.type: + self.type = self.env.config.default_role + if not self.type: + raise SphinxError('cannot determine default role!') + return self.run() def run(self): From cb49a6b09148342dca7eea2573d9c9e9e62974c2 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 16 Feb 2019 01:34:24 +0900 Subject: [PATCH 68/86] Fix :pep: and :rfc: roles are broken --- sphinx/roles.py | 19 +++++++++---------- tests/test_markup.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/sphinx/roles.py b/sphinx/roles.py index c36ed2613..c77feeb62 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -277,11 +277,11 @@ class PEP(ReferenceRole): def build_uri(self): # type: () -> str base_url = self.inliner.document.settings.pep_base_url - target, anchor = self.target.split('#', 1) - if anchor: - return base_url + 'pep-%04d#%s' % (int(target), anchor) + ret = self.target.split('#', 1) + if len(ret) == 2: + return base_url + 'pep-%04d#%s' % (int(ret[0]), ret[1]) else: - return base_url + 'pep-%04d' % int(target) + return base_url + 'pep-%04d' % int(ret[0]) class RFC(ReferenceRole): @@ -312,13 +312,12 @@ class RFC(ReferenceRole): def build_uri(self): # type: () -> str - target, anchor = self.target.split('#', 1) - base_url = (self.inliner.document.settings.rfc_base_url + - self.inliner.rfc_url % int(target)) - if anchor: - return base_url + '#' + anchor + base_url = self.inliner.document.settings.rfc_base_url + ret = self.target.split('#', 1) + if len(ret) == 2: + return base_url + self.inliner.rfc_url % int(ret[0]) + '#' + ret[1] else: - return base_url + return base_url + self.inliner.rfc_url % int(ret[0]) _amp_re = re.compile(r'(?PEP 8

    '), + ('\\index{Python Enhancement Proposals@\\spxentry{Python Enhancement Proposals}' + '!PEP 8@\\spxentry{PEP 8}}\\sphinxhref{http://www.python.org/dev/peps/pep-0008}' + '{\\sphinxstylestrong{PEP 8}}') + ), + ( + # pep role with anchor + 'verify', + ':pep:`8#id1`', + ('

    ' + 'PEP 8#id1

    '), + ('\\index{Python Enhancement Proposals@\\spxentry{Python Enhancement Proposals}' + '!PEP 8\\#id1@\\spxentry{PEP 8\\#id1}}\\sphinxhref' + '{http://www.python.org/dev/peps/pep-0008\\#id1}' + '{\\sphinxstylestrong{PEP 8\\#id1}}') + ), + ( + # rfc role + 'verify', + ':rfc:`2324`', + ('

    RFC 2324

    '), + ('\\index{RFC@\\spxentry{RFC}!RFC 2324@\\spxentry{RFC 2324}}' + '\\sphinxhref{http://tools.ietf.org/html/rfc2324.html}' + '{\\sphinxstylestrong{RFC 2324}}') + ), + ( + # rfc role with anchor + 'verify', + ':rfc:`2324#id1`', + ('

    ' + 'RFC 2324#id1

    '), + ('\\index{RFC@\\spxentry{RFC}!RFC 2324\\#id1@\\spxentry{RFC 2324\\#id1}}' + '\\sphinxhref{http://tools.ietf.org/html/rfc2324.html\\#id1}' + '{\\sphinxstylestrong{RFC 2324\\#id1}}') + ), ( # correct interpretation of code with whitespace 'verify_re', From c892a5abeb0c9a28650cc6e01bcc851f85d68a41 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 16 Feb 2019 01:00:18 +0900 Subject: [PATCH 69/86] refactor: Rename the first argument of roles to `name` In docutils' document, it is called as name. So our base class should call it as "name" also. --- sphinx/util/docutils.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index 56f8d124a..31c959a0e 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -393,7 +393,7 @@ class SphinxRole: This class is strongly coupled with Sphinx. """ - def __call__(self, typ, rawtext, text, lineno, inliner, options={}, content=[]): + def __call__(self, name, rawtext, text, lineno, inliner, options={}, content=[]): # type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA self.rawtext = rawtext self.text = unescape(text) @@ -403,13 +403,13 @@ class SphinxRole: self.content = content # guess role type - if typ: - self.type = typ.lower() + if name: + self.name = name.lower() else: - self.type = self.env.temp_data.get('default_role') - if not self.type: - self.type = self.env.config.default_role - if not self.type: + self.name = self.env.temp_data.get('default_role') + if not self.name: + self.name = self.env.config.default_role + if not self.name: raise SphinxError('cannot determine default role!') return self.run() @@ -449,7 +449,7 @@ class ReferenceRole(SphinxRole): # \x00 means the "<" was backslash-escaped explicit_title_re = re.compile(r'^(.+?)\s*(?$', re.DOTALL) - def __call__(self, typ, rawtext, text, lineno, inliner, options={}, content=[]): + def __call__(self, name, rawtext, text, lineno, inliner, options={}, content=[]): # type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA matched = self.explicit_title_re.match(text) if matched: @@ -461,7 +461,7 @@ class ReferenceRole(SphinxRole): self.title = unescape(text) self.target = unescape(text) - return super().__call__(typ, rawtext, text, lineno, inliner, options, content) + return super().__call__(name, rawtext, text, lineno, inliner, options, content) class SphinxTranslator(nodes.NodeVisitor): From 065ece6f4c223bdacd91e85c6b7a47e51093be6d Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 16 Feb 2019 01:10:51 +0900 Subject: [PATCH 70/86] docs: Add SphinxRole and ReferenceRole --- doc/extdev/utils.rst | 6 ++++++ sphinx/util/docutils.py | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/doc/extdev/utils.rst b/doc/extdev/utils.rst index ebf05629e..3aac51ed9 100644 --- a/doc/extdev/utils.rst +++ b/doc/extdev/utils.rst @@ -18,5 +18,11 @@ components (e.g. :class:`.Config`, :class:`.BuildEnvironment` and so on) easily. .. autoclass:: sphinx.util.docutils.SphinxDirective :members: +.. autoclass:: sphinx.util.docutils.SphinxRole + :members: + +.. autoclass:: sphinx.util.docutils.ReferenceRole + :members: + .. autoclass:: sphinx.transforms.post_transforms.images.ImageConverter :members: diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index 31c959a0e..393f97abf 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -392,6 +392,15 @@ class SphinxRole: .. note:: The subclasses of this class might not work with docutils. This class is strongly coupled with Sphinx. """ + name = None #: The role name actually used in the document. + rawtext = None #: A string containing the entire interpreted text input. + text = None #: The interpreted text content. + lineno = None #: The line number where the interpreted text begins. + inliner = None #: The ``docutils.parsers.rst.states.Inliner`` object. + options = None #: A dictionary of directive options for customization + #: (from the "role" directive). + content = None #: A list of strings, the directive content for customization + #: (from the "role" directive). def __call__(self, name, rawtext, text, lineno, inliner, options={}, content=[]): # type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA @@ -443,9 +452,13 @@ class ReferenceRole(SphinxRole): """A base class for reference roles. The reference roles can accpet ``link title `` style as a text for - the role. The parsed result: link title and target will be stored to + the role. The parsed result; link title and target will be stored to ``self.title`` and ``self.target``. """ + has_explicit_title = None #: A boolean indicates the role has explicit title or not. + title = None #: The link title for the interpreted text. + target = None #: The link target for the interpreted text. + # \x00 means the "<" was backslash-escaped explicit_title_re = re.compile(r'^(.+?)\s*(?$', re.DOTALL) From 1660269d01de3ba70485b89cbdff1fb0a83f8b77 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 16 Feb 2019 01:36:31 +0900 Subject: [PATCH 71/86] Fix a test --- tests/roots/test-html_entity/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/roots/test-html_entity/index.rst b/tests/roots/test-html_entity/index.rst index e4cbd99c3..11f4f4c84 100644 --- a/tests/roots/test-html_entity/index.rst +++ b/tests/roots/test-html_entity/index.rst @@ -7,7 +7,7 @@ Empty cell ---------- .. list-table:: - :header-rows: 1 + - * un * * trois From ea4d8ca08dbe1b2db5347bae8456c8babb2381c1 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 14 Feb 2019 22:53:10 +0900 Subject: [PATCH 72/86] Replace :guilabel: and :menuselection: roles by class based implementation --- CHANGES | 1 + doc/extdev/index.rst | 5 +++++ sphinx/roles.py | 33 +++++++++++++++++++++++++++++++-- tests/test_markup.py | 7 +++++++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index cb4499c4e..33bce4e1a 100644 --- a/CHANGES +++ b/CHANGES @@ -116,6 +116,7 @@ Deprecated * ``sphinx.io.SphinxRSTFileInput`` * ``sphinx.registry.SphinxComponentRegistry.add_source_input()`` * ``sphinx.roles.abbr_role()`` +* ``sphinx.roles.menusel_role()`` * ``sphinx.roles.index_role()`` * ``sphinx.roles.indexmarkup_role()`` * ``sphinx.testing.util.remove_unicode_literal()`` diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst index a7f84d2ea..2066e95cc 100644 --- a/doc/extdev/index.rst +++ b/doc/extdev/index.rst @@ -380,6 +380,11 @@ The following is a list of deprecated interfaces. - 4.0 - ``sphinx.roles.Abbreviation`` + * - ``sphinx.roles.menusel_role()`` + - 2.0 + - 4.0 + - ``sphinx.roles.GUILabel`` or ``sphinx.roles.MenuSelection`` + * - ``sphinx.roles.index_role()`` - 2.0 - 4.0 diff --git a/sphinx/roles.py b/sphinx/roles.py index c77feeb62..db3feddc3 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -325,6 +325,9 @@ _amp_re = re.compile(r'(? Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA + warnings.warn('menusel_role() is deprecated. ' + 'Please use MenuSelection or GUILabel class instead.', + RemovedInSphinx40Warning, stacklevel=2) env = inliner.document.settings.env if not typ: assert env.temp_data['default_role'] @@ -357,6 +360,32 @@ def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): return [node], [] +class GUILabel(SphinxRole): + amp_re = re.compile(r'(? Tuple[List[nodes.Node], List[nodes.system_message]] + node = nodes.inline(rawtext=self.rawtext, classes=[self.name]) + spans = self.amp_re.split(self.text) + node += nodes.Text(spans.pop(0)) + for span in spans: + span = span.replace('&&', '&') + + letter = nodes.Text(span[0]) + accelerator = nodes.inline('', '', letter, classes=['accelerator']) + node += accelerator + node += nodes.Text(span[1:]) + + return [node], [] + + +class MenuSelection(GUILabel): + def run(self): + # type: () -> Tuple[List[nodes.Node], List[nodes.system_message]] + self.text = self.text.replace('-->', '\N{TRIANGULAR BULLET}') # type: ignore + return super().run() + + _litvar_re = re.compile('{([^}]+)}') parens_re = re.compile(r'(\\*{|\\*})') @@ -507,8 +536,8 @@ specific_docroles = { 'pep': PEP(), 'rfc': RFC(), - 'guilabel': menusel_role, - 'menuselection': menusel_role, + 'guilabel': GUILabel(), + 'menuselection': MenuSelection(), 'file': emph_literal_role, 'samp': emph_literal_role, 'abbr': Abbreviation(), diff --git a/tests/test_markup.py b/tests/test_markup.py index 1aa544bf6..9d995ff98 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -225,6 +225,13 @@ def get_verifier(verify, verify_re): '-&- Bar

    '), r'\sphinxguilabel{\sphinxaccelerator{F}oo -\&- \sphinxaccelerator{B}ar}', ), + ( + # no ampersands in guilabel + 'verify', + ':guilabel:`Foo`', + '

    Foo

    ', + r'\sphinxguilabel{Foo}', + ), ( # non-interpolation of dashes in option role 'verify_re', From 3ec032fa39553570820c8d2ba62416561fa98d1a Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 14 Feb 2019 22:53:10 +0900 Subject: [PATCH 73/86] Replace :file: and :samp: roles by class based implementation --- CHANGES | 1 + doc/extdev/index.rst | 5 ++++ sphinx/roles.py | 59 ++++++++++++++++++++++++++++++++++++++++++-- tests/test_markup.py | 48 +++++++++++++++++++++-------------- 4 files changed, 93 insertions(+), 20 deletions(-) diff --git a/CHANGES b/CHANGES index 33bce4e1a..a1dd7b565 100644 --- a/CHANGES +++ b/CHANGES @@ -116,6 +116,7 @@ Deprecated * ``sphinx.io.SphinxRSTFileInput`` * ``sphinx.registry.SphinxComponentRegistry.add_source_input()`` * ``sphinx.roles.abbr_role()`` +* ``sphinx.roles.emph_literal_role()`` * ``sphinx.roles.menusel_role()`` * ``sphinx.roles.index_role()`` * ``sphinx.roles.indexmarkup_role()`` diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst index 2066e95cc..24596935c 100644 --- a/doc/extdev/index.rst +++ b/doc/extdev/index.rst @@ -380,6 +380,11 @@ The following is a list of deprecated interfaces. - 4.0 - ``sphinx.roles.Abbreviation`` + * - ``sphinx.roles.emph_literal_role()`` + - 2.0 + - 4.0 + - ``sphinx.roles.EmphasizedLiteral`` + * - ``sphinx.roles.menusel_role()`` - 2.0 - 4.0 diff --git a/sphinx/roles.py b/sphinx/roles.py index db3feddc3..b7f2a99d6 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -393,6 +393,9 @@ parens_re = re.compile(r'(\\*{|\\*})') def emph_literal_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): # type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA + warnings.warn('emph_literal_role() is deprecated. ' + 'Please use EmphasizedLiteral class instead.', + RemovedInSphinx40Warning, stacklevel=2) env = inliner.document.settings.env if not typ: assert env.temp_data['default_role'] @@ -440,6 +443,58 @@ def emph_literal_role(typ, rawtext, text, lineno, inliner, return [retnode], [] +class EmphasizedLiteral(SphinxRole): + parens_re = re.compile(r'(\\\\|\\{|\\}|{|})') + + def run(self): + # type: () -> Tuple[List[nodes.Node], List[nodes.system_message]] + children = self.parse(self.text) + node = nodes.literal(self.rawtext, '', *children, + role=self.name.lower(), classes=[self.name]) + + return [node], [] + + def parse(self, text): + # type: (str) -> List[nodes.Node] + result = [] # type: List[nodes.Node] + + stack = [''] + for part in self.parens_re.split(text): + if part == '\\\\': # escaped backslash + stack[-1] += '\\' + elif part == '{': + if len(stack) >= 2 and stack[-2] == "{": # nested + stack[-1] += "{" + else: + # start emphasis + stack.append('{') + stack.append('') + elif part == '}': + if len(stack) == 3 and stack[1] == "{" and len(stack[2]) > 0: + # emphasized word found + if stack[0]: + result.append(nodes.Text(stack[0], stack[0])) + result.append(nodes.emphasis(stack[2], stack[2])) + stack = [''] + else: + # emphasized word not found; the rparen is not a special symbol + stack.append('}') + stack = [''.join(stack)] + elif part == '\\{': # escaped left-brace + stack[-1] += '{' + elif part == '\\}': # escaped right-brace + stack[-1] += '}' + else: # others (containing escaped braces) + stack[-1] += part + + if ''.join(stack): + # remaining is treated as Text + text = ''.join(stack) + result.append(nodes.Text(text, text)) + + return result + + _abbr_re = re.compile(r'\((.*)\)$', re.S) @@ -538,8 +593,8 @@ specific_docroles = { 'rfc': RFC(), 'guilabel': GUILabel(), 'menuselection': MenuSelection(), - 'file': emph_literal_role, - 'samp': emph_literal_role, + 'file': EmphasizedLiteral(), + 'samp': EmphasizedLiteral(), 'abbr': Abbreviation(), 'index': Index(), } # type: Dict[str, RoleFunction] diff --git a/tests/test_markup.py b/tests/test_markup.py index 9d995ff98..19928158e 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -184,24 +184,6 @@ def get_verifier(verify, verify_re): 'code   sample

    '), r'\\sphinxcode{\\sphinxupquote{code sample}}', ), - ( - # correct interpretation of code with whitespace - 'verify_re', - ':samp:`code sample`', - ('

    ' - 'code   sample

    '), - r'\\sphinxcode{\\sphinxupquote{code sample}}', - ), - ( - # interpolation of braces in samp and file roles (HTML only) - 'verify', - ':samp:`a{b}c`', - ('

    ' - 'a' - 'b' - 'c

    '), - '\\sphinxcode{\\sphinxupquote{a\\sphinxstyleemphasis{b}c}}', - ), ( # interpolation of arrows in menuselection 'verify', @@ -314,6 +296,36 @@ def test_inline(get_verifier, type, rst, html_expected, latex_expected): verifier(rst, html_expected, latex_expected) +def test_samp_role(parse): + # no braces + text = ':samp:`a{b}c`' + doctree = parse(text) + assert_node(doctree[0], [nodes.paragraph, nodes.literal, ("a", + [nodes.emphasis, "b"], + "c")]) + # nested braces + text = ':samp:`a{{b}}c`' + doctree = parse(text) + assert_node(doctree[0], [nodes.paragraph, nodes.literal, ("a", + [nodes.emphasis, "{b"], + "}c")]) + + # half-opened braces + text = ':samp:`a{bc`' + doctree = parse(text) + assert_node(doctree[0], [nodes.paragraph, nodes.literal, "a{bc"]) + + # escaped braces + text = ':samp:`a\\\\{b}c`' + doctree = parse(text) + assert_node(doctree[0], [nodes.paragraph, nodes.literal, "a{b}c"]) + + # no braces (whitespaces are keeped as is) + text = ':samp:`code sample`' + doctree = parse(text) + assert_node(doctree[0], [nodes.paragraph, nodes.literal, "code sample"]) + + @pytest.mark.sphinx('dummy', testroot='prolog') def test_rst_prolog(app, status, warning): app.builder.build_all() From 6748a6de2f5117cbaf4a80bc0ad5f320217397f9 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 16 Feb 2019 15:22:48 +0900 Subject: [PATCH 74/86] Add testcase for download role --- tests/test_markup.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_markup.py b/tests/test_markup.py index 19928158e..cf503cd14 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -326,6 +326,26 @@ def test_samp_role(parse): assert_node(doctree[0], [nodes.paragraph, nodes.literal, "code sample"]) +def test_download_role(parse): + # implicit + text = ':download:`sphinx.rst`' + doctree = parse(text) + assert_node(doctree[0], [nodes.paragraph, addnodes.download_reference, + nodes.literal, "sphinx.rst"]) + assert_node(doctree[0][0], refdoc='dummy', refdomain='', reftype='download', + refexplicit=False, reftarget='sphinx.rst', refwarn=False) + assert_node(doctree[0][0][0], classes=['xref', 'download']) + + # explicit + text = ':download:`reftitle `' + doctree = parse(text) + assert_node(doctree[0], [nodes.paragraph, addnodes.download_reference, + nodes.literal, "reftitle"]) + assert_node(doctree[0][0], refdoc='dummy', refdomain='', reftype='download', + refexplicit=True, reftarget='sphinx.rst', refwarn=False) + assert_node(doctree[0][0][0], classes=['xref', 'download']) + + @pytest.mark.sphinx('dummy', testroot='prolog') def test_rst_prolog(app, status, warning): app.builder.build_all() From 61f1477942649de0ce50c10cf1e0bfde454bc382 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 16 Feb 2019 15:23:07 +0900 Subject: [PATCH 75/86] Add testcase for XRefRole class --- tests/test_markup.py | 67 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/tests/test_markup.py b/tests/test_markup.py index cf503cd14..30b874466 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -18,7 +18,8 @@ from docutils.transforms.universal import SmartQuotes from sphinx import addnodes from sphinx.builders.latex import LaTeXBuilder -from sphinx.testing.util import assert_node +from sphinx.roles import XRefRole +from sphinx.testing.util import Struct, assert_node from sphinx.util import texescape from sphinx.util.docutils import sphinx_domains from sphinx.writers.html import HTMLWriter, HTMLTranslator @@ -43,10 +44,26 @@ def settings(app): @pytest.fixture -def parse(settings): - def parse_(rst): - document = utils.new_document(b'test data', settings) +def new_document(settings): + def create(): + document = utils.new_document('test data', settings) document['file'] = 'dummy' + return document + + return create + + +@pytest.fixture +def inliner(new_document): + document = new_document() + document.reporter.get_source_and_line = lambda line=1: ('dummy.rst', line) + return Struct(document=document, reporter=document.reporter) + + +@pytest.fixture +def parse(new_document): + def parse_(rst): + document = new_document() parser = RstParser() parser.parse(rst, document) SmartQuotes(document, startnode=None).apply() @@ -346,6 +363,48 @@ def test_download_role(parse): assert_node(doctree[0][0][0], classes=['xref', 'download']) +def test_XRefRole(inliner): + role = XRefRole() + + # implicit + doctrees, errors = role('ref', 'rawtext', 'text', 5, inliner, {}, []) + assert len(doctrees) == 1 + assert_node(doctrees[0], [addnodes.pending_xref, nodes.literal, 'text']) + assert_node(doctrees[0], refdoc='dummy', refdomain='', reftype='ref', reftarget='text', + refexplicit=False, refwarn=False) + assert errors == [] + + # explicit + doctrees, errors = role('ref', 'rawtext', 'title ', 5, inliner, {}, []) + assert_node(doctrees[0], [addnodes.pending_xref, nodes.literal, 'title']) + assert_node(doctrees[0], refdoc='dummy', refdomain='', reftype='ref', reftarget='target', + refexplicit=True, refwarn=False) + + # bang + doctrees, errors = role('ref', 'rawtext', '!title ', 5, inliner, {}, []) + assert_node(doctrees[0], [nodes.literal, 'title ']) + + # refdomain + doctrees, errors = role('test:doc', 'rawtext', 'text', 5, inliner, {}, []) + assert_node(doctrees[0], [addnodes.pending_xref, nodes.literal, 'text']) + assert_node(doctrees[0], refdoc='dummy', refdomain='test', reftype='doc', reftarget='text', + refexplicit=False, refwarn=False) + + # fix_parens + role = XRefRole(fix_parens=True) + doctrees, errors = role('ref', 'rawtext', 'text()', 5, inliner, {}, []) + assert_node(doctrees[0], [addnodes.pending_xref, nodes.literal, 'text()']) + assert_node(doctrees[0], refdoc='dummy', refdomain='', reftype='ref', reftarget='text', + refexplicit=False, refwarn=False) + + # lowercase + role = XRefRole(lowercase=True) + doctrees, errors = role('ref', 'rawtext', 'TEXT', 5, inliner, {}, []) + assert_node(doctrees[0], [addnodes.pending_xref, nodes.literal, 'TEXT']) + assert_node(doctrees[0], refdoc='dummy', refdomain='', reftype='ref', reftarget='text', + refexplicit=False, refwarn=False) + + @pytest.mark.sphinx('dummy', testroot='prolog') def test_rst_prolog(app, status, warning): app.builder.build_all() From c23835ef0600e3ab30bf285580df960c34ab625f Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 16 Feb 2019 21:20:30 +0900 Subject: [PATCH 76/86] refactor XRefRole using ReferenceRole class --- sphinx/roles.py | 106 ++++++++++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 44 deletions(-) diff --git a/sphinx/roles.py b/sphinx/roles.py index b7f2a99d6..1a2daa36a 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -15,7 +15,6 @@ from docutils import nodes, utils from sphinx import addnodes from sphinx.deprecation import RemovedInSphinx40Warning -from sphinx.errors import SphinxError from sphinx.locale import _ from sphinx.util import ws_re from sphinx.util.docutils import ReferenceRole, SphinxRole @@ -47,7 +46,7 @@ generic_docroles = { # -- generic cross-reference role ---------------------------------------------- -class XRefRole: +class XRefRole(ReferenceRole): """ A generic cross-referencing role. To create a callable that can be used as a role function, create an instance of this class. @@ -85,8 +84,12 @@ class XRefRole: if innernodeclass is not None: self.innernodeclass = innernodeclass + super().__init__() + def _fix_parens(self, env, has_explicit_title, title, target): # type: (BuildEnvironment, bool, str, str) -> Tuple[str, str] + warnings.warn('XRefRole._fix_parens() is deprecated.', + RemovedInSphinx40Warning, stacklevel=2) if not has_explicit_title: if title.endswith('()'): # remove parentheses @@ -99,55 +102,70 @@ class XRefRole: target = target[:-2] return title, target - def __call__(self, typ, rawtext, text, lineno, inliner, - options={}, content=[]): - # type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA - env = inliner.document.settings.env - if not typ: - typ = env.temp_data.get('default_role') - if not typ: - typ = env.config.default_role - if not typ: - raise SphinxError('cannot determine default role!') + def update_title_and_target(self, title, target): + # type: (str, str) -> Tuple[str, str] + if not self.has_explicit_title: + if title.endswith('()'): + # remove parentheses + title = title[:-2] + if self.config.add_function_parentheses: + # add them back to all occurrences if configured + title += '()' + # remove parentheses from the target too + if target.endswith('()'): + target = target[:-2] + return title, target + + def run(self): + # type: () -> Tuple[List[nodes.Node], List[nodes.system_message]] + if ':' not in self.name: + self.refdomain, self.reftype = '', self.name + self.classes = ['xref', self.reftype] else: - typ = typ.lower() - if ':' not in typ: - domain, role = '', typ - classes = ['xref', role] + self.refdomain, self.reftype = self.name.split(':', 1) + self.classes = ['xref', self.refdomain, '%s-%s' % (self.refdomain, self.reftype)] + + if self.text.startswith('!'): + # if the first character is a bang, don't cross-reference at all + return self.create_non_xref_node() else: - domain, role = typ.split(':', 1) - classes = ['xref', domain, '%s-%s' % (domain, role)] - # if the first character is a bang, don't cross-reference at all - if text[0:1] == '!': - text = utils.unescape(text)[1:] - if self.fix_parens: - text, tgt = self._fix_parens(env, False, text, "") - innernode = self.innernodeclass(rawtext, text, classes=classes) - return self.result_nodes(inliner.document, env, innernode, is_ref=False) - # split title and target in role content - has_explicit_title, title, target = split_explicit_title(text) - title = utils.unescape(title) - target = utils.unescape(target) - # fix-up title and target + return self.create_xref_node() + + def create_non_xref_node(self): + # type: () -> Tuple[List[nodes.Node], List[nodes.system_message]] + text = utils.unescape(self.text[1:]) + if self.fix_parens: + self.has_explicit_title = False # treat as implicit + text, target = self.update_title_and_target(text, "") + + node = self.innernodeclass(self.rawtext, text, classes=self.classes) + return self.result_nodes(self.inliner.document, self.env, node, is_ref=False) + + def create_xref_node(self): + # type: () -> Tuple[List[nodes.Node], List[nodes.system_message]] + target = self.target + title = self.title if self.lowercase: target = target.lower() if self.fix_parens: - title, target = self._fix_parens( - env, has_explicit_title, title, target) + title, target = self.update_title_and_target(title, target) + # create the reference node - refnode = self.nodeclass(rawtext, reftype=role, refdomain=domain, - refexplicit=has_explicit_title) - # we may need the line number for warnings - set_role_source_info(inliner, lineno, refnode) - title, target = self.process_link(env, refnode, has_explicit_title, title, target) - # now that the target and title are finally determined, set them + options = {'refdoc': self.env.docname, + 'refdomain': self.refdomain, + 'reftype': self.reftype, + 'refexplicit': self.has_explicit_title, + 'refwarn': self.warn_dangling} + refnode = self.nodeclass(self.rawtext, **options) + self.set_source_info(refnode) + + # determine the target and title for the class + title, target = self.process_link(self.env, refnode, self.has_explicit_title, + title, target) refnode['reftarget'] = target - refnode += self.innernodeclass(rawtext, title, classes=classes) - # we also need the source document - refnode['refdoc'] = env.docname - refnode['refwarn'] = self.warn_dangling - # result_nodes allow further modification of return values - return self.result_nodes(inliner.document, env, refnode, is_ref=True) + refnode += self.innernodeclass(self.rawtext, title, classes=self.classes) + + return self.result_nodes(self.inliner.document, self.env, refnode, is_ref=True) # methods that can be overwritten From 68f9339d7f05198ec22f76c21e69c5684f6362a9 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 17 Feb 2019 01:15:44 +0900 Subject: [PATCH 77/86] conf.py: Remove html_sidebars from template --- sphinx/templates/quickstart/conf.py_t | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/sphinx/templates/quickstart/conf.py_t b/sphinx/templates/quickstart/conf.py_t index d5773881f..87a9b87ef 100644 --- a/sphinx/templates/quickstart/conf.py_t +++ b/sphinx/templates/quickstart/conf.py_t @@ -101,16 +101,6 @@ html_theme = 'alabaster' # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['{{ dot }}static'] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -# html_sidebars = {} {%- if extensions %} From 963d8bc4fcb09a9ded2caa986f791c535bb005e1 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 17 Feb 2019 01:16:01 +0900 Subject: [PATCH 78/86] conf.py: Remove html_options from template --- sphinx/templates/quickstart/conf.py_t | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sphinx/templates/quickstart/conf.py_t b/sphinx/templates/quickstart/conf.py_t index 87a9b87ef..f9f96b221 100644 --- a/sphinx/templates/quickstart/conf.py_t +++ b/sphinx/templates/quickstart/conf.py_t @@ -91,12 +91,6 @@ exclude_patterns = [{{ exclude_patterns }}] # html_theme = 'alabaster' -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". From 08cd23a0bbc060a6135cd5acc1411693159df8b5 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 17 Feb 2019 18:22:12 +0900 Subject: [PATCH 79/86] Fix #6047: autodoc: ``autofunction`` emits a warning for method objects --- CHANGES | 1 + sphinx/ext/autodoc/__init__.py | 1 + tests/roots/test-ext-autodoc/target/callable.py | 5 +++++ tests/test_autodoc.py | 13 +++++++++++++ 4 files changed, 20 insertions(+) diff --git a/CHANGES b/CHANGES index ea1301c7c..65f39e538 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,7 @@ Bugs fixed * #6026: LaTeX: A cross reference to definition list does not work * #6046: LaTeX: ``TypeError`` is raised when invalid latex_elements given * #6019: imgconverter: Including multipage PDF fails +* #6047: autodoc: ``autofunction`` emits a warning for method objects Testing -------- diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index df3319695..1b1635faa 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1056,6 +1056,7 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ return None try: if (not isfunction(self.object) and + not inspect.ismethod(self.object) and not isbuiltin(self.object) and not inspect.isclass(self.object) and hasattr(self.object, '__call__')): diff --git a/tests/roots/test-ext-autodoc/target/callable.py b/tests/roots/test-ext-autodoc/target/callable.py index f3358eafd..6fcd5053e 100644 --- a/tests/roots/test-ext-autodoc/target/callable.py +++ b/tests/roots/test-ext-autodoc/target/callable.py @@ -4,5 +4,10 @@ class Callable(): def __call__(self, arg1, arg2, **kwargs): pass + def method(self, arg1, arg2): + """docstring of Callable.method().""" + pass + function = Callable() +method = function.method diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 1be35eb5b..7b1384bf7 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -1359,6 +1359,19 @@ def test_autofunction_for_callable(app): ] +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autofunction_for_method(app): + actual = do_autodoc(app, 'function', 'target.callable.method') + assert list(actual) == [ + '', + '.. py:function:: method(arg1, arg2)', + ' :module: target.callable', + '', + ' docstring of Callable.method().', + ' ' + ] + + @pytest.mark.sphinx('html', testroot='root') def test_mocked_module_imports(app): options = {"members": 'TestAutodoc,decoratedFunction'} From 90bf81bd379793c1144a2ecd955b4da4cff0fe37 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 17 Feb 2019 19:52:08 +0900 Subject: [PATCH 80/86] test: Use get_doctree() to load doctree files --- tests/test_build.py | 5 ++--- tests/test_ext_autodoc.py | 4 +--- tests/test_intl.py | 15 ++++++--------- tests/test_markup.py | 19 +++++++++---------- 4 files changed, 18 insertions(+), 25 deletions(-) diff --git a/tests/test_build.py b/tests/test_build.py index b25254529..7a1214154 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -8,7 +8,6 @@ :license: BSD, see LICENSE for details. """ -import pickle import sys from textwrap import dedent @@ -110,7 +109,7 @@ def test_image_glob(app, status, warning): app.builder.build_all() # index.rst - doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes()) + doctree = app.env.get_doctree('index') assert isinstance(doctree[0][1], nodes.image) assert doctree[0][1]['candidates'] == {'*': 'rimg.png'} @@ -135,7 +134,7 @@ def test_image_glob(app, status, warning): assert doctree[0][4][0]['uri'] == 'img.*' # subdir/index.rst - doctree = pickle.loads((app.doctreedir / 'subdir/index.doctree').bytes()) + doctree = app.env.get_doctree('subdir/index') assert isinstance(doctree[0][1], nodes.image) sub = path('subdir') diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index 7a366bd29..eeee9e3a3 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -8,8 +8,6 @@ :license: BSD, see LICENSE for details. """ -import pickle - import pytest from sphinx import addnodes @@ -19,7 +17,7 @@ from sphinx import addnodes def test_autodoc(app, status, warning): app.builder.build_all() - content = pickle.loads((app.doctreedir / 'index.doctree').bytes()) + content = app.env.get_doctree('index') assert isinstance(content[3], addnodes.desc) assert content[3][0].astext() == 'autodoc_dummy_module.test' assert content[3][1].astext() == 'Dummy function using dummy.*' diff --git a/tests/test_intl.py b/tests/test_intl.py index 786c31386..367409fd9 100644 --- a/tests/test_intl.py +++ b/tests/test_intl.py @@ -10,7 +10,6 @@ """ import os -import pickle import re import pytest @@ -1188,9 +1187,9 @@ def test_text_references(app, warning): @pytest.mark.xfail(os.name != 'posix', reason="Not working on windows") def test_image_glob_intl(app): app.build() - # index.rst - doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes()) + # index.rst + doctree = app.env.get_doctree('index') assert_node(doctree[0][1], nodes.image, uri='rimg.xx.png', candidates={'*': 'rimg.xx.png'}) @@ -1210,8 +1209,7 @@ def test_image_glob_intl(app): 'image/png': 'img.png'}) # subdir/index.rst - doctree = pickle.loads((app.doctreedir / 'subdir/index.doctree').bytes()) - + doctree = app.env.get_doctree('subdir/index') assert_node(doctree[0][1], nodes.image, uri='subdir/rimg.xx.png', candidates={'*': 'subdir/rimg.xx.png'}) @@ -1236,9 +1234,9 @@ def test_image_glob_intl(app): @pytest.mark.xfail(os.name != 'posix', reason="Not working on windows") def test_image_glob_intl_using_figure_language_filename(app): app.build() - # index.rst - doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes()) + # index.rst + doctree = app.env.get_doctree('index') assert_node(doctree[0][1], nodes.image, uri='rimg.png.xx', candidates={'*': 'rimg.png.xx'}) @@ -1258,8 +1256,7 @@ def test_image_glob_intl_using_figure_language_filename(app): 'image/png': 'img.png'}) # subdir/index.rst - doctree = pickle.loads((app.doctreedir / 'subdir/index.doctree').bytes()) - + doctree = app.env.get_doctree('subdir/index') assert_node(doctree[0][1], nodes.image, uri='subdir/rimg.png', candidates={'*': 'subdir/rimg.png'}) diff --git a/tests/test_markup.py b/tests/test_markup.py index 30b874466..006d19caa 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -8,7 +8,6 @@ :license: BSD, see LICENSE for details. """ -import pickle import re import pytest @@ -408,8 +407,8 @@ def test_XRefRole(inliner): @pytest.mark.sphinx('dummy', testroot='prolog') def test_rst_prolog(app, status, warning): app.builder.build_all() - rst = pickle.loads((app.doctreedir / 'restructuredtext.doctree').bytes()) - md = pickle.loads((app.doctreedir / 'markdown.doctree').bytes()) + rst = app.env.get_doctree('restructuredtext') + md = app.env.get_doctree('markdown') # rst_prolog assert_node(rst[0], nodes.paragraph) @@ -432,7 +431,7 @@ def test_rst_prolog(app, status, warning): @pytest.mark.sphinx('dummy', testroot='keep_warnings') def test_keep_warnings_is_True(app, status, warning): app.builder.build_all() - doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes()) + doctree = app.env.get_doctree('index') assert_node(doctree[0], nodes.section) assert len(doctree[0]) == 2 assert_node(doctree[0][1], nodes.system_message) @@ -442,7 +441,7 @@ def test_keep_warnings_is_True(app, status, warning): confoverrides={'keep_warnings': False}) def test_keep_warnings_is_False(app, status, warning): app.builder.build_all() - doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes()) + doctree = app.env.get_doctree('index') assert_node(doctree[0], nodes.section) assert len(doctree[0]) == 1 @@ -450,7 +449,7 @@ def test_keep_warnings_is_False(app, status, warning): @pytest.mark.sphinx('dummy', testroot='refonly_bullet_list') def test_compact_refonly_bullet_list(app, status, warning): app.builder.build_all() - doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes()) + doctree = app.env.get_doctree('index') assert_node(doctree[0], nodes.section) assert len(doctree[0]) == 5 @@ -470,7 +469,7 @@ def test_default_role1(app, status, warning): app.builder.build_all() # default-role: pep - doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes()) + doctree = app.env.get_doctree('index') assert_node(doctree[0], nodes.section) assert_node(doctree[0][1], nodes.paragraph) assert_node(doctree[0][1][0], addnodes.index) @@ -478,7 +477,7 @@ def test_default_role1(app, status, warning): assert_node(doctree[0][1][2], nodes.reference, classes=["pep"]) # no default-role - doctree = pickle.loads((app.doctreedir / 'foo.doctree').bytes()) + doctree = app.env.get_doctree('foo') assert_node(doctree[0], nodes.section) assert_node(doctree[0][1], nodes.paragraph) assert_node(doctree[0][1][0], nodes.title_reference) @@ -491,7 +490,7 @@ def test_default_role2(app, status, warning): app.builder.build_all() # default-role directive is stronger than configratuion - doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes()) + doctree = app.env.get_doctree('index') assert_node(doctree[0], nodes.section) assert_node(doctree[0][1], nodes.paragraph) assert_node(doctree[0][1][0], addnodes.index) @@ -499,7 +498,7 @@ def test_default_role2(app, status, warning): assert_node(doctree[0][1][2], nodes.reference, classes=["pep"]) # default_role changes the default behavior - doctree = pickle.loads((app.doctreedir / 'foo.doctree').bytes()) + doctree = app.env.get_doctree('foo') assert_node(doctree[0], nodes.section) assert_node(doctree[0][1], nodes.paragraph) assert_node(doctree[0][1][0], nodes.inline, classes=["guilabel"]) From 1ca210aab1b6218916960d2cde44d7efceff2548 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 17 Feb 2019 21:55:28 +0900 Subject: [PATCH 81/86] Separate json and pickle to sphinxcontrib package --- CHANGES | 2 + doc/extdev/index.rst | 25 ++++++ doc/usage/builders/index.rst | 2 +- setup.py | 1 + sphinx/application.py | 1 + sphinx/builders/html.py | 154 +++-------------------------------- sphinx/util/jsonimpl.py | 7 ++ tests/test_build.py | 6 +- 8 files changed, 48 insertions(+), 150 deletions(-) diff --git a/CHANGES b/CHANGES index f402b28b1..0ea8b7c43 100644 --- a/CHANGES +++ b/CHANGES @@ -25,6 +25,7 @@ Dependencies - sphinxcontrib.devhelp - sphinxcontrib.htmlhelp - sphinxcontrib.jsmath + - sphinxcontrib.serializinghtml - sphinxcontrib.qthelp Incompatible changes @@ -128,6 +129,7 @@ Deprecated * ``sphinx.util.force_decode()`` * ``sphinx.util.get_matching_docs()`` * ``sphinx.util.inspect.Parameter`` +* ``sphinx.util.jsonimpl`` * ``sphinx.util.osutil.EEXIST`` * ``sphinx.util.osutil.EINVAL`` * ``sphinx.util.osutil.ENOENT`` diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst index 24596935c..7b4bbc692 100644 --- a/doc/extdev/index.rst +++ b/doc/extdev/index.rst @@ -290,11 +290,31 @@ The following is a list of deprecated interfaces. - 4.0 - ``sphinx.builders.epub3.validate_config_values()`` + * - ``sphinx.builders.html.JSONHTMLBuilder`` + - 2.0 + - 4.0 + - ``sphinx.builders.serializinghtml.JSONHTMLBuilder`` + + * - ``sphinx.builders.html.PickleHTMLBuilder`` + - 2.0 + - 4.0 + - ``sphinx.builders.serializinghtml.PickleHTMLBuilder`` + + * - ``sphinx.builders.html.SerializingHTMLBuilder`` + - 2.0 + - 4.0 + - ``sphinx.builders.serializinghtml.SerializingHTMLBuilder`` + * - ``sphinx.builders.html.SingleFileHTMLBuilder`` - 2.0 - 4.0 - ``sphinx.builders.singlehtml.SingleFileHTMLBuilder`` + * - ``sphinx.builders.html.WebHTMLBuilder`` + - 2.0 + - 4.0 + - ``sphinx.builders.serializinghtml.PickleHTMLBuilder`` + * - ``sphinx.builders.htmlhelp`` - 2.0 - 4.0 @@ -425,6 +445,11 @@ The following is a list of deprecated interfaces. - 3.0 - N/A + * - ``sphinx.util.jsonimpl`` + - 2.0 + - 4.0 + - ``sphinxcontrib.serializinghtml.jsonimpl`` + * - ``sphinx.util.osutil.EEXIST`` - 2.0 - 4.0 diff --git a/doc/usage/builders/index.rst b/doc/usage/builders/index.rst index b0a10483b..3b74a02e1 100644 --- a/doc/usage/builders/index.rst +++ b/doc/usage/builders/index.rst @@ -299,7 +299,7 @@ name is ``rinoh``. Refer to the `rinohtype manual`_ for details. .. versionadded:: 1.1 -.. currentmodule:: sphinx.builders.html +.. currentmodule:: sphinxcontrib.serializinghtml .. class:: SerializingHTMLBuilder This builder uses a module that implements the Python serialization API diff --git a/setup.py b/setup.py index cdcbbc4f7..79c466321 100644 --- a/setup.py +++ b/setup.py @@ -19,6 +19,7 @@ install_requires = [ 'sphinxcontrib-devhelp', 'sphinxcontrib-jsmath', 'sphinxcontrib-htmlhelp', + 'sphinxcontrib-serializinghtml', 'sphinxcontrib-qthelp', 'Jinja2>=2.3', 'Pygments>=2.0', diff --git a/sphinx/application.py b/sphinx/application.py index 296104e21..6d553333b 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -107,6 +107,7 @@ builtin_extensions = ( 'sphinxcontrib.applehelp', 'sphinxcontrib.devhelp', 'sphinxcontrib.htmlhelp', + 'sphinxcontrib.serializinghtml', 'sphinxcontrib.qthelp', # Strictly, alabaster theme is not a builtin extension, # but it is loaded automatically to use it as default theme. diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 8368b07db..bb11bfb4a 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -9,11 +9,9 @@ """ import html -import pickle import posixpath import re import sys -import types import warnings from hashlib import md5 from os import path @@ -25,7 +23,6 @@ from docutils.io import DocTreeInput, StringOutput from docutils.utils import relative_path from sphinx import package_dir, __display_version__ -from sphinx.application import ENV_PICKLE_FILENAME from sphinx.builders import Builder from sphinx.deprecation import ( RemovedInSphinx30Warning, RemovedInSphinx40Warning, deprecated_alias @@ -38,15 +35,14 @@ from sphinx.highlighting import PygmentsBridge from sphinx.locale import _, __ from sphinx.search import js_index from sphinx.theming import HTMLThemeFactory -from sphinx.util import jsonimpl, logging, status_iterator +from sphinx.util import logging, status_iterator from sphinx.util.console import bold # type: ignore from sphinx.util.docutils import is_html5_writer_available, new_document from sphinx.util.fileutil import copy_asset from sphinx.util.i18n import format_date from sphinx.util.inventory import InventoryFile from sphinx.util.matching import patmatch, Matcher, DOTFILES -from sphinx.util.osutil import SEP, os_path, relative_uri, ensuredir, \ - movefile, copyfile +from sphinx.util.osutil import os_path, relative_uri, ensuredir, movefile, copyfile from sphinx.writers.html import HTMLWriter, HTMLTranslator if False: @@ -66,8 +62,6 @@ else: #: the filename for the inventory of objects INVENTORY_FILENAME = 'objects.inv' -#: the filename for the "last build" file (for serializing builders) -LAST_BUILD_FILENAME = 'last_build' logger = logging.getLogger(__name__) return_codes_re = re.compile('[\r\n]+') @@ -1157,140 +1151,6 @@ class StandaloneHTMLBuilder(Builder): logger.info(__('done')) -class SerializingHTMLBuilder(StandaloneHTMLBuilder): - """ - An abstract builder that serializes the generated HTML. - """ - #: the serializing implementation to use. Set this to a module that - #: implements a `dump`, `load`, `dumps` and `loads` functions - #: (pickle, simplejson etc.) - implementation = None # type: Any - implementation_dumps_unicode = False - #: additional arguments for dump() - additional_dump_args = () # type: Tuple - - #: the filename for the global context file - globalcontext_filename = None # type: str - - supported_image_types = ['image/svg+xml', 'image/png', - 'image/gif', 'image/jpeg'] - - def init(self): - # type: () -> None - self.build_info = BuildInfo(self.config, self.tags) - self.imagedir = '_images' - self.current_docname = None - self.theme = None # no theme necessary - self.templates = None # no template bridge necessary - self.init_templates() - self.init_highlighter() - self.init_css_files() - self.init_js_files() - self.use_index = self.get_builder_config('use_index', 'html') - - def get_target_uri(self, docname, typ=None): - # type: (str, str) -> str - if docname == 'index': - return '' - if docname.endswith(SEP + 'index'): - return docname[:-5] # up to sep - return docname + SEP - - def dump_context(self, context, filename): - # type: (Dict, str) -> None - if self.implementation_dumps_unicode: - with open(filename, 'w', encoding='utf-8') as ft: - self.implementation.dump(context, ft, *self.additional_dump_args) - else: - with open(filename, 'wb') as fb: - self.implementation.dump(context, fb, *self.additional_dump_args) - - def handle_page(self, pagename, ctx, templatename='page.html', - outfilename=None, event_arg=None): - # type: (str, Dict, str, str, Any) -> None - ctx['current_page_name'] = pagename - self.add_sidebars(pagename, ctx) - - if not outfilename: - outfilename = path.join(self.outdir, - os_path(pagename) + self.out_suffix) - - # we're not taking the return value here, since no template is - # actually rendered - self.app.emit('html-page-context', pagename, templatename, ctx, event_arg) - - # make context object serializable - for key in list(ctx): - if isinstance(ctx[key], types.FunctionType): - del ctx[key] - - ensuredir(path.dirname(outfilename)) - self.dump_context(ctx, outfilename) - - # if there is a source file, copy the source file for the - # "show source" link - if ctx.get('sourcename'): - source_name = path.join(self.outdir, '_sources', - os_path(ctx['sourcename'])) - ensuredir(path.dirname(source_name)) - copyfile(self.env.doc2path(pagename), source_name) - - def handle_finish(self): - # type: () -> None - # dump the global context - outfilename = path.join(self.outdir, self.globalcontext_filename) - self.dump_context(self.globalcontext, outfilename) - - # super here to dump the search index - super().handle_finish() - - # copy the environment file from the doctree dir to the output dir - # as needed by the web app - copyfile(path.join(self.doctreedir, ENV_PICKLE_FILENAME), - path.join(self.outdir, ENV_PICKLE_FILENAME)) - - # touch 'last build' file, used by the web application to determine - # when to reload its environment and clear the cache - open(path.join(self.outdir, LAST_BUILD_FILENAME), 'w').close() - - -class PickleHTMLBuilder(SerializingHTMLBuilder): - """ - A Builder that dumps the generated HTML into pickle files. - """ - name = 'pickle' - epilog = __('You can now process the pickle files in %(outdir)s.') - - implementation = pickle - implementation_dumps_unicode = False - additional_dump_args = (pickle.HIGHEST_PROTOCOL,) - indexer_format = pickle - indexer_dumps_unicode = False - out_suffix = '.fpickle' - globalcontext_filename = 'globalcontext.pickle' - searchindex_filename = 'searchindex.pickle' - - -# compatibility alias -WebHTMLBuilder = PickleHTMLBuilder - - -class JSONHTMLBuilder(SerializingHTMLBuilder): - """ - A builder that dumps the generated HTML into JSON files. - """ - name = 'json' - epilog = __('You can now process the JSON files in %(outdir)s.') - - implementation = jsonimpl - implementation_dumps_unicode = True - indexer_format = jsonimpl - indexer_dumps_unicode = True - out_suffix = '.fjson' - globalcontext_filename = 'globalcontext.json' - searchindex_filename = 'searchindex.json' - - def convert_html_css_files(app, config): # type: (Sphinx, Config) -> None """This converts string styled html_css_files to tuple styled one.""" @@ -1374,11 +1234,19 @@ def validate_math_renderer(app): # for compatibility from sphinx.builders.dirhtml import DirectoryHTMLBuilder # NOQA from sphinx.builders.singlehtml import SingleFileHTMLBuilder # NOQA +from sphinxcontrib.serializinghtml import ( # NOQA + LAST_BUILD_FILENAME, JSONHTMLBuilder, PickleHTMLBuilder, SerializingHTMLBuilder +) deprecated_alias('sphinx.builders.html', { + 'LAST_BUILD_FILENAME': LAST_BUILD_FILENAME, 'DirectoryHTMLBuilder': DirectoryHTMLBuilder, + 'JSONHTMLBuilder': JSONHTMLBuilder, + 'PickleHTMLBuilder': PickleHTMLBuilder, + 'SerializingHTMLBuilder': SerializingHTMLBuilder, 'SingleFileHTMLBuilder': SingleFileHTMLBuilder, + 'WebHTMLBuilder': PickleHTMLBuilder, }, RemovedInSphinx40Warning) @@ -1387,8 +1255,6 @@ def setup(app): # type: (Sphinx) -> Dict[str, Any] # builders app.add_builder(StandaloneHTMLBuilder) - app.add_builder(PickleHTMLBuilder) - app.add_builder(JSONHTMLBuilder) # config values app.add_config_value('html_theme', 'alabaster', 'html') diff --git a/sphinx/util/jsonimpl.py b/sphinx/util/jsonimpl.py index ecb0b791a..4fb8e1f5d 100644 --- a/sphinx/util/jsonimpl.py +++ b/sphinx/util/jsonimpl.py @@ -9,13 +9,20 @@ """ import json +import warnings from collections import UserString +from sphinx.deprecation import RemovedInSphinx40Warning + if False: # For type annotation from typing import Any, IO # NOQA +warnings.warn('sphinx.util.jsonimpl is deprecated', + RemovedInSphinx40Warning, stacklevel=2) + + class SphinxJSONEncoder(json.JSONEncoder): """JSONEncoder subclass that forces translation proxies.""" def default(self, obj): diff --git a/tests/test_build.py b/tests/test_build.py index 7a1214154..8072906a2 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -57,11 +57,7 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir): # (html, epub, latex, texinfo and manpage) @pytest.mark.parametrize( "buildername", - [ - # note: no 'html' - if it's ok with dirhtml it's ok with html - 'dirhtml', 'singlehtml', 'pickle', 'json', 'text', - 'changes', 'xml', 'pseudoxml', 'linkcheck', - ], + ['dirhtml', 'singlehtml', 'text', 'changes', 'xml', 'pseudoxml', 'linkcheck'], ) @mock.patch('sphinx.builders.linkcheck.requests.head', side_effect=request_session_head) From d4ace4cd63421e5ed7f046fda026d570b38253f0 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 17 Feb 2019 22:43:58 +0900 Subject: [PATCH 82/86] Fix wrong indentation --- sphinx/writers/latex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 04731411f..9ee51f8aa 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -28,7 +28,7 @@ from sphinx.deprecation import ( from sphinx.domains.std import StandardDomain from sphinx.errors import SphinxError from sphinx.locale import admonitionlabels, _, __ - from sphinx.util import split_into, logging +from sphinx.util import split_into, logging from sphinx.util.docutils import SphinxTranslator from sphinx.util.nodes import clean_astext, get_prev_node from sphinx.util.template import LaTeXRenderer From 60a1016aa44e723a5b5cb9be60f53d617be7e2e2 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 18 Feb 2019 01:53:35 +0900 Subject: [PATCH 83/86] Bump to 2.0.0 beta1 --- CHANGES | 4 +- sphinx/__init__.py | 6 +- sphinx/locale/sphinx.pot | 1764 ++++++++++++++++++-------------------- 3 files changed, 831 insertions(+), 943 deletions(-) diff --git a/CHANGES b/CHANGES index ede14bcb3..36a08b8d0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,5 @@ -Release 2.0.0 (in development) -============================== +Release 2.0.0 beta1 (in development) +==================================== Dependencies ------------ diff --git a/sphinx/__init__.py b/sphinx/__init__.py index bfa5297d5..acc5dfef2 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -32,8 +32,8 @@ if 'PYTHONWARNINGS' not in os.environ: warnings.filterwarnings('ignore', "'U' mode is deprecated", DeprecationWarning, module='docutils.io') -__version__ = '2.0.0+' -__released__ = '2.0.0' # used when Sphinx builds its own docs +__version__ = '2.0.0b1' +__released__ = '2.0.0b1' # used when Sphinx builds its own docs #: Version info for better programmatic use. #: @@ -43,7 +43,7 @@ __released__ = '2.0.0' # used when Sphinx builds its own docs #: #: .. versionadded:: 1.2 #: Before version 1.2, check the string ``sphinx.__version__``. -version_info = (2, 0, 0, 'beta', 0) +version_info = (2, 0, 0, 'beta', 1) package_dir = path.abspath(path.dirname(__file__)) diff --git a/sphinx/locale/sphinx.pot b/sphinx/locale/sphinx.pot index 6e58ba551..38074c074 100644 --- a/sphinx/locale/sphinx.pot +++ b/sphinx/locale/sphinx.pot @@ -1,14 +1,14 @@ # Translations template for Sphinx. -# Copyright (C) 2018 ORGANIZATION +# Copyright (C) 2019 ORGANIZATION # This file is distributed under the same license as the Sphinx project. -# FIRST AUTHOR , 2018. +# FIRST AUTHOR , 2019. # #, fuzzy msgid "" msgstr "" -"Project-Id-Version: Sphinx 1.8.0\n" +"Project-Id-Version: Sphinx 2.0.0\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2018-08-21 01:14+0900\n" +"POT-Creation-Date: 2019-02-18 01:52+0900\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,21 +17,21 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.6.0\n" -#: sphinx/application.py:152 +#: sphinx/application.py:153 #, python-format msgid "config directory doesn't contain a conf.py file (%s)" msgstr "" -#: sphinx/application.py:156 +#: sphinx/application.py:157 #, python-format msgid "Cannot find source directory (%s)" msgstr "" -#: sphinx/application.py:160 +#: sphinx/application.py:161 msgid "Source directory and destination directory cannot be identical" msgstr "" -#: sphinx/application.py:191 +#: sphinx/application.py:192 #, python-format msgid "Running Sphinx v%s" msgstr "" @@ -44,95 +44,83 @@ msgid "" msgstr "" #: sphinx/application.py:234 -msgid "making output directory..." +msgid "making output directory" msgstr "" -#: sphinx/application.py:244 +#: sphinx/application.py:239 sphinx/registry.py:470 +#, python-format +msgid "while setting up extension %s:" +msgstr "" + +#: sphinx/application.py:245 msgid "" "'setup' as currently defined in conf.py isn't a Python callable. Please " "modify its definition to make it a callable function. This is needed for " "conf.py to behave as a Sphinx extension." msgstr "" -#: sphinx/application.py:256 -#, python-format -msgid "primary_domain %r not found, ignored." -msgstr "" - -#: sphinx/application.py:271 +#: sphinx/application.py:269 #, python-format msgid "loading translations [%s]... " msgstr "" -#: sphinx/application.py:287 sphinx/application.py:304 -#: sphinx/builders/__init__.py:380 sphinx/builders/__init__.py:387 -#: sphinx/builders/__init__.py:588 sphinx/builders/applehelp.py:134 -#: sphinx/builders/applehelp.py:178 sphinx/builders/applehelp.py:188 -#: sphinx/builders/applehelp.py:201 sphinx/builders/applehelp.py:241 -#: sphinx/builders/applehelp.py:274 sphinx/builders/html.py:956 -#: sphinx/builders/html.py:1220 sphinx/builders/html.py:1238 -#: sphinx/builders/html.py:1412 sphinx/builders/html.py:1422 -#: sphinx/builders/latex/__init__.py:339 sphinx/builders/texinfo.py:188 +#: sphinx/application.py:285 sphinx/builders/html.py:852 +#: sphinx/builders/html.py:870 sphinx/builders/html.py:1133 +#: sphinx/builders/html.py:1151 sphinx/util/__init__.py:702 msgid "done" msgstr "" -#: sphinx/application.py:289 +#: sphinx/application.py:287 msgid "not available for built-in messages" msgstr "" -#: sphinx/application.py:300 -msgid "loading pickled environment... " +#: sphinx/application.py:298 +msgid "loading pickled environment" msgstr "" -#: sphinx/application.py:306 +#: sphinx/application.py:303 #, python-format msgid "failed: %s" msgstr "" -#: sphinx/application.py:316 +#: sphinx/application.py:313 msgid "No builder selected, using default: html" msgstr "" -#: sphinx/application.py:347 +#: sphinx/application.py:344 msgid "succeeded" msgstr "" -#: sphinx/application.py:347 +#: sphinx/application.py:344 msgid "finished with problems" msgstr "" -#: sphinx/application.py:349 +#: sphinx/application.py:346 #, python-format msgid "build %s, %s warning." msgstr "" -#: sphinx/application.py:353 +#: sphinx/application.py:350 #, python-format msgid "build %s." msgstr "" -#: sphinx/application.py:626 +#: sphinx/application.py:557 #, python-format -msgid "" -"while setting up extension %s: node class %r is already registered, its " -"visitors will be overridden" +msgid "node class %r is already registered, its visitors will be overridden" msgstr "" -#: sphinx/application.py:724 +#: sphinx/application.py:654 #, python-format -msgid "" -"while setting up extension %s: directive %r is already registered, it " -"will be overridden" +msgid "directive %r is already registered, it will be overridden" msgstr "" -#: sphinx/application.py:749 sphinx/application.py:770 +#: sphinx/application.py:677 sphinx/application.py:696 #, python-format -msgid "" -"while setting up extension %s: role %r is already registered, it will be " -"overridden" +msgid "role %r is already registered, it will be overridden" msgstr "" -#: sphinx/application.py:1268 +#: sphinx/application.py:1182 #, python-format msgid "" "the %s extension does not declare if it is safe for parallel reading, " @@ -140,7 +128,7 @@ msgid "" "explicit" msgstr "" -#: sphinx/application.py:1274 +#: sphinx/application.py:1188 #, python-format msgid "" "the %s extension does not declare if it is safe for parallel writing, " @@ -148,61 +136,55 @@ msgid "" "explicit" msgstr "" -#: sphinx/application.py:1285 +#: sphinx/application.py:1199 #, python-format msgid "doing serial %s" msgstr "" -#: sphinx/config.py:212 +#: sphinx/config.py:220 #, python-format msgid "" "cannot override dictionary config setting %r, ignoring (use %r to set " "individual elements)" msgstr "" -#: sphinx/config.py:221 +#: sphinx/config.py:229 #, python-format msgid "invalid number %r for config value %r, ignoring" msgstr "" -#: sphinx/config.py:226 +#: sphinx/config.py:234 #, python-format msgid "cannot override config setting %r with unsupported type, ignoring" msgstr "" -#: sphinx/config.py:256 +#: sphinx/config.py:264 #, python-format msgid "unknown config value %r in override, ignoring" msgstr "" -#: sphinx/config.py:274 +#: sphinx/config.py:282 #, python-format msgid "No such config value: %s" msgstr "" -#: sphinx/config.py:304 +#: sphinx/config.py:312 #, python-format msgid "Config value %r already present" msgstr "" -#: sphinx/config.py:355 +#: sphinx/config.py:363 #, python-format -msgid "There is a syntax error in your configuration file: %s" +msgid "There is a syntax error in your configuration file: %s\n" msgstr "" -#: sphinx/config.py:357 -msgid "" -"\n" -"Did you change the syntax from 2.x to 3.x?" -msgstr "" - -#: sphinx/config.py:360 +#: sphinx/config.py:366 msgid "" "The configuration file (or one of the modules it imports) called " "sys.exit()" msgstr "" -#: sphinx/config.py:364 +#: sphinx/config.py:370 #, python-format msgid "" "There is a programmable error in your configuration file:\n" @@ -210,52 +192,52 @@ msgid "" "%s" msgstr "" -#: sphinx/config.py:391 +#: sphinx/config.py:397 #, python-format msgid "" -"The config value `source_suffix' expected to a string, list of strings or" -" dictionary. But `%r' is given." +"The config value `source_suffix' expects a string, list of strings, or " +"dictionary. But `%r' is given." msgstr "" -#: sphinx/config.py:399 +#: sphinx/config.py:405 #, python-format msgid "Section %s" msgstr "" -#: sphinx/config.py:400 +#: sphinx/config.py:406 #, python-format msgid "Fig. %s" msgstr "" -#: sphinx/config.py:401 +#: sphinx/config.py:407 #, python-format msgid "Table %s" msgstr "" -#: sphinx/config.py:402 +#: sphinx/config.py:408 #, python-format msgid "Listing %s" msgstr "" -#: sphinx/config.py:441 +#: sphinx/config.py:447 msgid "" "The config value `{name}` has to be a one of {candidates}, but " "`{current}` is given." msgstr "" -#: sphinx/config.py:459 +#: sphinx/config.py:465 msgid "" -"The config value `{name}' has type `{current.__name__}', expected to " +"The config value `{name}' has type `{current.__name__}'; expected " "{permitted}." msgstr "" -#: sphinx/config.py:465 +#: sphinx/config.py:478 msgid "" "The config value `{name}' has type `{current.__name__}', defaults to " "`{default.__name__}'." msgstr "" -#: sphinx/config.py:481 +#: sphinx/config.py:497 #, python-format msgid "" "the config value %r is set to a string with non-ASCII characters; this " @@ -263,787 +245,675 @@ msgid "" "%r." msgstr "" -#: sphinx/events.py:58 +#: sphinx/config.py:506 +#, python-format +msgid "primary_domain %r not found, ignored." +msgstr "" + +#: sphinx/config.py:518 +msgid "" +"Since v2.0, Sphinx uses \"index\" as master_doc by default. Please add " +"\"master_doc = 'contents'\" to your conf.py." +msgstr "" + +#: sphinx/events.py:54 #, python-format msgid "Event %r already present" msgstr "" -#: sphinx/events.py:64 +#: sphinx/events.py:60 #, python-format msgid "Unknown event name: %s" msgstr "" -#: sphinx/extension.py:55 +#: sphinx/extension.py:52 #, python-format msgid "" "The %s extension is required by needs_extensions settings, but it is not " "loaded." msgstr "" -#: sphinx/extension.py:60 +#: sphinx/extension.py:57 #, python-format msgid "" "This project needs the extension %s at least in version %s and therefore " "cannot be built with the loaded version (%s)." msgstr "" -#: sphinx/highlighting.py:144 +#: sphinx/highlighting.py:142 #, python-format msgid "Pygments lexer name %r is not known" msgstr "" -#: sphinx/highlighting.py:165 +#: sphinx/highlighting.py:163 #, python-format msgid "Could not lex literal_block as \"%s\". Highlighting skipped." msgstr "" -#: sphinx/io.py:209 -#, python-format -msgid "undecodable source characters, replacing with \"?\": %r" +#: sphinx/project.py:59 +msgid "document not readable. Ignored." msgstr "" -#: sphinx/registry.py:132 +#: sphinx/registry.py:131 #, python-format msgid "Builder class %s has no \"name\" attribute" msgstr "" -#: sphinx/registry.py:134 +#: sphinx/registry.py:133 #, python-format msgid "Builder %r already exists (in module %s)" msgstr "" -#: sphinx/registry.py:148 +#: sphinx/registry.py:147 #, python-format msgid "Builder name %s not registered or available through entry point" msgstr "" -#: sphinx/registry.py:156 +#: sphinx/registry.py:155 #, python-format msgid "Builder name %s not registered" msgstr "" -#: sphinx/registry.py:164 +#: sphinx/registry.py:163 #, python-format msgid "domain %s already registered" msgstr "" -#: sphinx/registry.py:198 sphinx/registry.py:213 sphinx/registry.py:224 +#: sphinx/registry.py:197 sphinx/registry.py:212 sphinx/registry.py:223 #, python-format msgid "domain %s not yet registered" msgstr "" -#: sphinx/registry.py:202 +#: sphinx/registry.py:201 #, python-format -msgid "The %r directive is already registered to %d domain" +msgid "The %r directive is already registered to domain %s" msgstr "" -#: sphinx/registry.py:216 +#: sphinx/registry.py:215 #, python-format -msgid "The %r role is already registered to %d domain" +msgid "The %r role is already registered to domain %s" msgstr "" -#: sphinx/registry.py:227 +#: sphinx/registry.py:226 #, python-format -msgid "The %r index is already registered to %d domain" +msgid "The %r index is already registered to domain %s" msgstr "" -#: sphinx/registry.py:251 +#: sphinx/registry.py:250 #, python-format msgid "The %r object_type is already registered" msgstr "" -#: sphinx/registry.py:271 +#: sphinx/registry.py:270 #, python-format msgid "The %r crossref_type is already registered" msgstr "" -#: sphinx/registry.py:279 +#: sphinx/registry.py:278 #, python-format msgid "source_suffix %r is already registered" msgstr "" -#: sphinx/registry.py:309 +#: sphinx/registry.py:308 #, python-format msgid "source_parser for %r is already registered" msgstr "" -#: sphinx/registry.py:325 +#: sphinx/registry.py:324 #, python-format msgid "Source parser for %s not registered" msgstr "" -#: sphinx/registry.py:343 +#: sphinx/registry.py:344 #, python-format msgid "source_input for %r is already registered" msgstr "" -#: sphinx/registry.py:356 +#: sphinx/registry.py:363 #, python-format -msgid "source_input for %s not registered" +msgid "Translator for %r already exists" msgstr "" -#: sphinx/registry.py:362 -#, python-format -msgid "Translatoro for %r already exists" -msgstr "" - -#: sphinx/registry.py:374 +#: sphinx/registry.py:375 #, python-format msgid "kwargs for add_node() must be a (visit, depart) function tuple: %r=%r" msgstr "" -#: sphinx/registry.py:444 +#: sphinx/registry.py:445 #, python-format msgid "enumerable_node %r already registered" msgstr "" -#: sphinx/registry.py:452 +#: sphinx/registry.py:453 #, python-format msgid "math renderer %s is already registred" msgstr "" -#: sphinx/registry.py:463 +#: sphinx/registry.py:464 #, python-format msgid "" "the extension %r was already merged with Sphinx since version %s; this " "extension is ignored." msgstr "" -#: sphinx/registry.py:474 +#: sphinx/registry.py:475 msgid "Original exception:\n" msgstr "" -#: sphinx/registry.py:475 +#: sphinx/registry.py:476 #, python-format msgid "Could not import extension %s" msgstr "" -#: sphinx/registry.py:478 +#: sphinx/registry.py:479 #, python-format msgid "" "extension %r has no setup() function; is it really a Sphinx extension " "module?" msgstr "" -#: sphinx/registry.py:487 +#: sphinx/registry.py:488 #, python-format msgid "" "The %s extension used by this project needs at least Sphinx v%s; it " "therefore cannot be built with this version." msgstr "" -#: sphinx/registry.py:495 +#: sphinx/registry.py:496 #, python-format msgid "" "extension %r returned an unsupported object from its setup() function; it" " should return None or a metadata dictionary" msgstr "" -#: sphinx/roles.py:202 +#: sphinx/roles.py:221 sphinx/roles.py:272 #, python-format msgid "Python Enhancement Proposals; PEP %s" msgstr "" -#: sphinx/theming.py:83 +#: sphinx/theming.py:79 #, python-format msgid "theme %r doesn't have \"theme\" setting" msgstr "" -#: sphinx/theming.py:85 +#: sphinx/theming.py:81 #, python-format msgid "theme %r doesn't have \"inherit\" setting" msgstr "" -#: sphinx/theming.py:91 +#: sphinx/theming.py:87 #, python-format msgid "no theme named %r found, inherited by %r" msgstr "" -#: sphinx/theming.py:116 +#: sphinx/theming.py:112 #, python-format msgid "setting %s.%s occurs in none of the searched theme configs" msgstr "" -#: sphinx/theming.py:136 +#: sphinx/theming.py:132 #, python-format msgid "unsupported theme option %r given" msgstr "" -#: sphinx/theming.py:238 -#, python-format -msgid "Theme extension %r does not respond correctly." -msgstr "" - -#: sphinx/theming.py:265 +#: sphinx/theming.py:242 #, python-format msgid "file %r on theme path is not a valid zipfile or contains no theme" msgstr "" -#: sphinx/theming.py:281 +#: sphinx/theming.py:258 msgid "" "sphinx_rtd_theme is no longer a hard dependency since version 1.4.0. " "Please install it manually.(pip install sphinx_rtd_theme)" msgstr "" -#: sphinx/theming.py:285 +#: sphinx/theming.py:262 #, python-format msgid "no theme named %r found (missing theme.conf?)" msgstr "" -#: sphinx/builders/__init__.py:226 +#: sphinx/builders/__init__.py:205 #, python-format msgid "a suitable image for %s builder not found: %s (%s)" msgstr "" -#: sphinx/builders/__init__.py:230 +#: sphinx/builders/__init__.py:209 #, python-format msgid "a suitable image for %s builder not found: %s" msgstr "" -#: sphinx/builders/__init__.py:252 +#: sphinx/builders/__init__.py:231 msgid "building [mo]: " msgstr "" -#: sphinx/builders/__init__.py:253 sphinx/builders/__init__.py:601 -#: sphinx/builders/__init__.py:629 +#: sphinx/builders/__init__.py:232 sphinx/builders/__init__.py:574 +#: sphinx/builders/__init__.py:602 msgid "writing output... " msgstr "" -#: sphinx/builders/__init__.py:267 +#: sphinx/builders/__init__.py:245 #, python-format msgid "all of %d po files" msgstr "" -#: sphinx/builders/__init__.py:289 +#: sphinx/builders/__init__.py:266 #, python-format msgid "targets for %d po files that are specified" msgstr "" -#: sphinx/builders/__init__.py:300 +#: sphinx/builders/__init__.py:276 #, python-format msgid "targets for %d po files that are out of date" msgstr "" -#: sphinx/builders/__init__.py:308 +#: sphinx/builders/__init__.py:284 msgid "all source files" msgstr "" -#: sphinx/builders/__init__.py:322 +#: sphinx/builders/__init__.py:298 #, python-format msgid "file %r given on command line is not under the source directory, ignoring" msgstr "" -#: sphinx/builders/__init__.py:327 +#: sphinx/builders/__init__.py:303 #, python-format msgid "file %r given on command line does not exist, ignoring" msgstr "" -#: sphinx/builders/__init__.py:338 +#: sphinx/builders/__init__.py:314 #, python-format msgid "%d source files given on command line" msgstr "" -#: sphinx/builders/__init__.py:349 +#: sphinx/builders/__init__.py:325 #, python-format msgid "targets for %d source files that are out of date" msgstr "" -#: sphinx/builders/__init__.py:359 +#: sphinx/builders/__init__.py:335 #, python-format msgid "building [%s]" msgstr "" -#: sphinx/builders/__init__.py:366 +#: sphinx/builders/__init__.py:342 msgid "looking for now-outdated files... " msgstr "" -#: sphinx/builders/__init__.py:371 +#: sphinx/builders/__init__.py:347 #, python-format msgid "%d found" msgstr "" -#: sphinx/builders/__init__.py:373 +#: sphinx/builders/__init__.py:349 msgid "none found" msgstr "" -#: sphinx/builders/__init__.py:377 -msgid "pickling environment... " +#: sphinx/builders/__init__.py:354 +msgid "pickling environment" msgstr "" -#: sphinx/builders/__init__.py:385 -msgid "checking consistency... " +#: sphinx/builders/__init__.py:360 +msgid "checking consistency" msgstr "" -#: sphinx/builders/__init__.py:390 +#: sphinx/builders/__init__.py:364 msgid "no targets are out of date." msgstr "" -#: sphinx/builders/__init__.py:577 +#: sphinx/builders/__init__.py:404 +msgid "updating environment: " +msgstr "" + +#: sphinx/builders/__init__.py:423 +#, python-format +msgid "%s added, %s changed, %s removed" +msgstr "" + +#: sphinx/builders/__init__.py:462 sphinx/builders/__init__.py:492 +msgid "reading sources... " +msgstr "" + +#: sphinx/builders/__init__.py:497 sphinx/builders/__init__.py:612 +msgid "waiting for workers..." +msgstr "" + +#: sphinx/builders/__init__.py:551 #, python-format msgid "docnames to write: %s" msgstr "" -#: sphinx/builders/__init__.py:586 sphinx/builders/html.py:1410 -msgid "preparing documents... " +#: sphinx/builders/__init__.py:560 sphinx/builders/singlehtml.py:166 +msgid "preparing documents" msgstr "" -#: sphinx/builders/__init__.py:639 -msgid "waiting for workers..." +#: sphinx/builders/_epub_base.py:219 +#, python-format +msgid "duplicated ToC entry found: %s" msgstr "" -#: sphinx/builders/_epub_base.py:415 +#: sphinx/builders/_epub_base.py:415 sphinx/builders/html.py:761 +#: sphinx/builders/latex/__init__.py:413 sphinx/builders/texinfo.py:190 +msgid "copying images... " +msgstr "" + +#: sphinx/builders/_epub_base.py:422 #, python-format msgid "cannot read image file %r: copying it instead" msgstr "" -#: sphinx/builders/_epub_base.py:421 sphinx/builders/html.py:853 -#: sphinx/builders/latex/__init__.py:353 sphinx/builders/texinfo.py:260 +#: sphinx/builders/_epub_base.py:428 sphinx/builders/html.py:769 +#: sphinx/builders/latex/__init__.py:421 sphinx/builders/texinfo.py:198 #, python-format msgid "cannot copy image file %r: %s" msgstr "" -#: sphinx/builders/_epub_base.py:437 +#: sphinx/builders/_epub_base.py:445 #, python-format msgid "cannot write image file %r: %s" msgstr "" -#: sphinx/builders/_epub_base.py:448 -msgid "PIL not found - copying image files" +#: sphinx/builders/_epub_base.py:456 +msgid "Pillow not found - copying image files" msgstr "" -#: sphinx/builders/_epub_base.py:478 sphinx/builders/_epub_base.py:485 -#: sphinx/builders/_epub_base.py:515 sphinx/builders/_epub_base.py:694 -#: sphinx/builders/_epub_base.py:719 sphinx/builders/epub3.py:211 +#: sphinx/builders/_epub_base.py:491 sphinx/builders/_epub_base.py:504 +#: sphinx/builders/_epub_base.py:540 sphinx/builders/_epub_base.py:725 +#: sphinx/builders/_epub_base.py:758 sphinx/builders/epub3.py:184 #, python-format msgid "writing %s file..." msgstr "" -#: sphinx/builders/_epub_base.py:541 +#: sphinx/builders/_epub_base.py:566 #, python-format msgid "unknown mimetype for %s, ignoring" msgstr "" -#: sphinx/builders/applehelp.py:64 -msgid "Help indexer failed" -msgstr "" - -#: sphinx/builders/applehelp.py:68 -msgid "Code signing failed" -msgstr "" - -#: sphinx/builders/applehelp.py:77 -#, python-format -msgid "" -"The help book is in %(outdir)s.\n" -"Note that won't be able to view it unless you put it in " -"~/Library/Documentation/Help or install it in your application bundle." -msgstr "" - -#: sphinx/builders/applehelp.py:104 -msgid "You must set applehelp_bundle_id before building Apple Help output" -msgstr "" - -#: sphinx/builders/applehelp.py:128 -msgid "copying localized files... " -msgstr "" - -#: sphinx/builders/applehelp.py:175 -msgid "writing Info.plist... " -msgstr "" - -#: sphinx/builders/applehelp.py:182 -msgid "copying icon... " -msgstr "" - -#: sphinx/builders/applehelp.py:190 -#, python-format -msgid "cannot copy icon file %r: %s" -msgstr "" - -#: sphinx/builders/applehelp.py:195 -msgid "building access page..." -msgstr "" - -#: sphinx/builders/applehelp.py:204 -msgid "generating help index... " -msgstr "" - -#: sphinx/builders/applehelp.py:226 sphinx/builders/applehelp.py:260 -msgid "skipping" -msgstr "" - -#: sphinx/builders/applehelp.py:228 -#, python-format -msgid "" -"you will need to index this help book with:\n" -" %s" -msgstr "" - -#: sphinx/builders/applehelp.py:243 sphinx/builders/applehelp.py:276 -#, python-format -msgid "Command not found: %s" -msgstr "" - -#: sphinx/builders/applehelp.py:247 -msgid "signing help book... " -msgstr "" - -#: sphinx/builders/applehelp.py:261 -#, python-format -msgid "" -"you will need to sign this help book with:\n" -" %s" -msgstr "" - -#: sphinx/builders/changes.py:43 +#: sphinx/builders/changes.py:39 #, python-format msgid "The overview file is in %(outdir)s." msgstr "" -#: sphinx/builders/changes.py:70 +#: sphinx/builders/changes.py:68 #, python-format msgid "no changes in version %s." msgstr "" -#: sphinx/builders/changes.py:89 +#: sphinx/builders/changes.py:70 +msgid "writing summary file..." +msgstr "" + +#: sphinx/builders/changes.py:87 msgid "Builtins" msgstr "" -#: sphinx/builders/changes.py:91 +#: sphinx/builders/changes.py:89 msgid "Module level" msgstr "" -#: sphinx/builders/changes.py:136 +#: sphinx/builders/changes.py:134 msgid "copying source files..." msgstr "" -#: sphinx/builders/changes.py:143 +#: sphinx/builders/changes.py:141 #, python-format msgid "could not read %r for changelog creation" msgstr "" -#: sphinx/builders/devhelp.py:47 -#, python-format -msgid "" -"To view the help file:\n" -"$ mkdir -p $HOME/.local/share/devhelp/books\n" -"$ ln -s $PWD/%(outdir)s $HOME/.local/share/devhelp/books/%(project)s\n" -"$ devhelp" -msgstr "" - -#: sphinx/builders/devhelp.py:73 -msgid "dumping devhelp index..." -msgstr "" - -#: sphinx/builders/dummy.py:25 +#: sphinx/builders/dummy.py:24 msgid "The dummy builder generates no files." msgstr "" -#: sphinx/builders/epub3.py:70 +#: sphinx/builders/epub3.py:69 #, python-format msgid "The ePub file is in %(outdir)s." msgstr "" -#: sphinx/builders/epub3.py:95 +#: sphinx/builders/epub3.py:212 msgid "" "conf value \"epub_language\" (or \"language\") should not be empty for " "EPUB3" msgstr "" -#: sphinx/builders/epub3.py:99 +#: sphinx/builders/epub3.py:216 msgid "conf value \"epub_uid\" should be XML NAME for EPUB3" msgstr "" -#: sphinx/builders/epub3.py:102 +#: sphinx/builders/epub3.py:219 msgid "" "conf value \"epub_title\" (or \"html_title\") should not be empty for " "EPUB3" msgstr "" -#: sphinx/builders/epub3.py:106 +#: sphinx/builders/epub3.py:223 msgid "conf value \"epub_author\" should not be empty for EPUB3" msgstr "" -#: sphinx/builders/epub3.py:109 +#: sphinx/builders/epub3.py:226 msgid "conf value \"epub_contributor\" should not be empty for EPUB3" msgstr "" -#: sphinx/builders/epub3.py:112 +#: sphinx/builders/epub3.py:229 msgid "conf value \"epub_description\" should not be empty for EPUB3" msgstr "" -#: sphinx/builders/epub3.py:115 +#: sphinx/builders/epub3.py:232 msgid "conf value \"epub_publisher\" should not be empty for EPUB3" msgstr "" -#: sphinx/builders/epub3.py:118 +#: sphinx/builders/epub3.py:235 msgid "" "conf value \"epub_copyright\" (or \"copyright\")should not be empty for " "EPUB3" msgstr "" -#: sphinx/builders/epub3.py:122 +#: sphinx/builders/epub3.py:239 msgid "conf value \"epub_identifier\" should not be empty for EPUB3" msgstr "" -#: sphinx/builders/epub3.py:125 +#: sphinx/builders/epub3.py:242 msgid "conf value \"version\" should not be empty for EPUB3" msgstr "" -#: sphinx/builders/epub3.py:244 sphinx/builders/html.py:1599 +#: sphinx/builders/epub3.py:257 sphinx/builders/html.py:1166 #, python-format msgid "invalid css_file: %r, ignored" msgstr "" -#: sphinx/builders/gettext.py:219 +#: sphinx/builders/gettext.py:215 #, python-format msgid "The message catalogs are in %(outdir)s." msgstr "" -#: sphinx/builders/gettext.py:243 +#: sphinx/builders/gettext.py:239 #, python-format msgid "building [%s]: " msgstr "" -#: sphinx/builders/gettext.py:244 +#: sphinx/builders/gettext.py:240 #, python-format msgid "targets for %d template files" msgstr "" -#: sphinx/builders/gettext.py:248 +#: sphinx/builders/gettext.py:244 msgid "reading templates... " msgstr "" -#: sphinx/builders/gettext.py:272 +#: sphinx/builders/gettext.py:271 msgid "writing message catalogs... " msgstr "" -#: sphinx/builders/html.py:240 +#: sphinx/builders/html.py:182 #, python-format msgid "build info file is broken: %r" msgstr "" -#: sphinx/builders/html.py:279 +#: sphinx/builders/html.py:217 #, python-format msgid "The HTML pages are in %(outdir)s." msgstr "" -#: sphinx/builders/html.py:472 +#: sphinx/builders/html.py:399 #, python-format msgid "Failed to read build info file: %r" msgstr "" -#: sphinx/builders/html.py:572 sphinx/transforms/__init__.py:121 -#: sphinx/writers/latex.py:537 sphinx/writers/manpage.py:110 -#: sphinx/writers/texinfo.py:240 +#: sphinx/builders/html.py:488 sphinx/builders/latex/__init__.py:204 +#: sphinx/transforms/__init__.py:121 sphinx/writers/manpage.py:118 +#: sphinx/writers/texinfo.py:243 #, python-format msgid "%b %d, %Y" msgstr "" -#: sphinx/builders/html.py:584 +#: sphinx/builders/html.py:500 msgid "html_use_opensearch config value must now be a string" msgstr "" -#: sphinx/builders/html.py:590 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:506 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "" -#: sphinx/builders/html.py:590 +#: sphinx/builders/html.py:506 msgid "index" msgstr "" -#: sphinx/builders/html.py:654 +#: sphinx/builders/html.py:570 msgid "next" msgstr "" -#: sphinx/builders/html.py:663 +#: sphinx/builders/html.py:579 msgid "previous" msgstr "" -#: sphinx/builders/html.py:761 +#: sphinx/builders/html.py:677 msgid "generating indices..." msgstr "" -#: sphinx/builders/html.py:779 +#: sphinx/builders/html.py:695 msgid "writing additional pages..." msgstr "" -#: sphinx/builders/html.py:845 sphinx/builders/latex/__init__.py:345 -#: sphinx/builders/texinfo.py:252 -msgid "copying images... " -msgstr "" - -#: sphinx/builders/html.py:864 +#: sphinx/builders/html.py:780 msgid "copying downloadable files... " msgstr "" -#: sphinx/builders/html.py:872 +#: sphinx/builders/html.py:788 #, python-format msgid "cannot copy downloadable file %r: %s" msgstr "" -#: sphinx/builders/html.py:879 +#: sphinx/builders/html.py:795 msgid "copying static files... " msgstr "" -#: sphinx/builders/html.py:914 +#: sphinx/builders/html.py:830 #, python-format msgid "html_static_path entry %r does not exist" msgstr "" -#: sphinx/builders/html.py:923 sphinx/builders/latex/__init__.py:336 +#: sphinx/builders/html.py:839 sphinx/builders/latex/__init__.py:398 #, python-format msgid "logo file %r does not exist" msgstr "" -#: sphinx/builders/html.py:931 +#: sphinx/builders/html.py:847 #, python-format msgid "favicon file %r does not exist" msgstr "" -#: sphinx/builders/html.py:940 +#: sphinx/builders/html.py:854 #, python-format msgid "cannot copy static file %r" msgstr "" -#: sphinx/builders/html.py:946 +#: sphinx/builders/html.py:860 msgid "copying extra files... " msgstr "" -#: sphinx/builders/html.py:952 +#: sphinx/builders/html.py:866 #, python-format msgid "html_extra_path entry %r does not exist" msgstr "" -#: sphinx/builders/html.py:958 +#: sphinx/builders/html.py:872 #, python-format msgid "cannot copy extra file %r" msgstr "" -#: sphinx/builders/html.py:966 +#: sphinx/builders/html.py:880 #, python-format msgid "Failed to write build info file: %r" msgstr "" -#: sphinx/builders/html.py:1013 +#: sphinx/builders/html.py:927 msgid "" "search index couldn't be loaded, but not all documents will be built: the" " index will be incomplete." msgstr "" -#: sphinx/builders/html.py:1076 +#: sphinx/builders/html.py:996 #, python-format msgid "page %s matches two patterns in html_sidebars: %r and %r" msgstr "" -#: sphinx/builders/html.py:1182 +#: sphinx/builders/html.py:1094 #, python-format msgid "" "a Unicode error occurred when rendering the page %s. Please make sure all" " config values that contain non-ASCII content are Unicode strings." msgstr "" -#: sphinx/builders/html.py:1187 +#: sphinx/builders/html.py:1099 #, python-format msgid "" "An error happened in rendering the page %s.\n" "Reason: %r" msgstr "" -#: sphinx/builders/html.py:1198 sphinx/builders/texinfo.py:245 -#: sphinx/builders/text.py:88 sphinx/builders/xml.py:101 +#: sphinx/builders/html.py:1111 sphinx/builders/text.py:85 +#: sphinx/builders/xml.py:99 #, python-format msgid "error writing file %s: %s" msgstr "" -#: sphinx/builders/html.py:1218 +#: sphinx/builders/html.py:1131 msgid "dumping object inventory... " msgstr "" -#: sphinx/builders/html.py:1225 +#: sphinx/builders/html.py:1138 #, python-format msgid "dumping search index in %s ... " msgstr "" -#: sphinx/builders/html.py:1280 -#, python-format -msgid "The HTML page is in %(outdir)s." -msgstr "" - -#: sphinx/builders/html.py:1414 -msgid "assembling single document... " -msgstr "" - -#: sphinx/builders/html.py:1419 sphinx/builders/latex/__init__.py:213 -#: sphinx/builders/manpage.py:72 sphinx/builders/texinfo.py:171 -msgid "writing... " -msgstr "" - -#: sphinx/builders/html.py:1427 -msgid "writing additional files..." -msgstr "" - -#: sphinx/builders/html.py:1551 -#, python-format -msgid "You can now process the pickle files in %(outdir)s." -msgstr "" - -#: sphinx/builders/html.py:1572 -#, python-format -msgid "You can now process the JSON files in %(outdir)s." -msgstr "" - -#: sphinx/builders/html.py:1617 +#: sphinx/builders/html.py:1184 #, python-format msgid "invalid js_file: %r, ignored" msgstr "" -#: sphinx/builders/html.py:1661 +#: sphinx/builders/html.py:1228 msgid "Many math_renderers are registered. But no math_renderer is selected." msgstr "" -#: sphinx/builders/html.py:1664 +#: sphinx/builders/html.py:1231 #, python-format msgid "Unknown math_renderer %r is given." msgstr "" -#: sphinx/builders/html.py:1681 +#: sphinx/builders/html.py:1264 #, python-format msgid "%s %s documentation" msgstr "" -#: sphinx/builders/htmlhelp.py:178 -#, python-format -msgid "You can now run HTML Help Workshop with the .htp file in %(outdir)s." -msgstr "" - -#: sphinx/builders/htmlhelp.py:232 -msgid "dumping stopword list..." -msgstr "" - -#: sphinx/builders/htmlhelp.py:237 sphinx/builders/qthelp.py:99 -msgid "writing project file..." -msgstr "" - -#: sphinx/builders/htmlhelp.py:260 -msgid "writing TOC file..." -msgstr "" - -#: sphinx/builders/htmlhelp.py:302 -msgid "writing index file..." -msgstr "" - -#: sphinx/builders/linkcheck.py:95 +#: sphinx/builders/linkcheck.py:80 #, python-format msgid "Look for any errors in the above output or in %(outdir)s/output.txt" msgstr "" -#: sphinx/builders/linkcheck.py:159 +#: sphinx/builders/linkcheck.py:144 #, python-format msgid "Anchor '%s' not found" msgstr "" -#: sphinx/builders/linkcheck.py:257 +#: sphinx/builders/linkcheck.py:242 #, python-format msgid "broken link: %s (%s)" msgstr "" @@ -1057,189 +927,210 @@ msgstr "" msgid "no \"man_pages\" config value found; no manual pages will be written" msgstr "" -#: sphinx/builders/qthelp.py:59 +#: sphinx/builders/latex/__init__.py:270 sphinx/builders/manpage.py:64 +#: sphinx/builders/singlehtml.py:174 sphinx/builders/texinfo.py:120 +msgid "writing" +msgstr "" + +#: sphinx/builders/manpage.py:76 #, python-format -msgid "" -"You can now run \"qcollectiongenerator\" with the .qhcp project file in " -"%(outdir)s, like this:\n" -"$ qcollectiongenerator %(outdir)s/%(project)s.qhcp\n" -"To view the help file:\n" -"$ assistant -collectionFile %(outdir)s/%(project)s.qhc" +msgid "\"man_pages\" config value references unknown document %s" msgstr "" -#: sphinx/builders/qthelp.py:161 -msgid "writing collection project file..." +#: sphinx/builders/singlehtml.py:37 +#, python-format +msgid "The HTML page is in %(outdir)s." msgstr "" -#: sphinx/builders/texinfo.py:101 +#: sphinx/builders/singlehtml.py:169 +msgid "assembling single document" +msgstr "" + +#: sphinx/builders/singlehtml.py:188 +msgid "writing additional files" +msgstr "" + +#: sphinx/builders/texinfo.py:50 #, python-format msgid "The Texinfo files are in %(outdir)s." msgstr "" -#: sphinx/builders/texinfo.py:103 +#: sphinx/builders/texinfo.py:52 msgid "" "\n" "Run 'make' in that directory to run these through makeinfo\n" "(use 'make info' here to do that automatically)." msgstr "" -#: sphinx/builders/texinfo.py:136 +#: sphinx/builders/texinfo.py:85 msgid "no \"texinfo_documents\" config value found; no documents will be written" msgstr "" -#: sphinx/builders/texinfo.py:144 +#: sphinx/builders/texinfo.py:93 #, python-format msgid "\"texinfo_documents\" config value references unknown document %s" msgstr "" -#: sphinx/builders/latex/__init__.py:198 sphinx/builders/texinfo.py:167 +#: sphinx/builders/latex/__init__.py:253 sphinx/builders/texinfo.py:116 #, python-format -msgid "processing %s..." +msgid "processing %s" msgstr "" -#: sphinx/builders/latex/__init__.py:260 sphinx/builders/texinfo.py:215 +#: sphinx/builders/latex/__init__.py:322 sphinx/builders/texinfo.py:163 msgid "resolving references..." msgstr "" -#: sphinx/builders/latex/__init__.py:270 sphinx/builders/texinfo.py:224 +#: sphinx/builders/latex/__init__.py:332 sphinx/builders/texinfo.py:172 msgid " (in " msgstr "" -#: sphinx/builders/texinfo.py:237 -msgid "copying Texinfo support files... " +#: sphinx/builders/texinfo.py:204 +msgid "copying Texinfo support files" msgstr "" -#: sphinx/builders/texinfo.py:246 -msgid " done" +#: sphinx/builders/texinfo.py:208 +#, python-format +msgid "error writing file Makefile: %s" msgstr "" -#: sphinx/builders/text.py:35 +#: sphinx/builders/text.py:33 #, python-format msgid "The text files are in %(outdir)s." msgstr "" -#: sphinx/builders/xml.py:39 +#: sphinx/builders/xml.py:38 #, python-format msgid "The XML files are in %(outdir)s." msgstr "" -#: sphinx/builders/xml.py:114 +#: sphinx/builders/xml.py:112 #, python-format msgid "The pseudo-XML files are in %(outdir)s." msgstr "" -#: sphinx/builders/latex/__init__.py:117 +#: sphinx/builders/latex/__init__.py:121 #, python-format msgid "The LaTeX files are in %(outdir)s." msgstr "" -#: sphinx/builders/latex/__init__.py:119 +#: sphinx/builders/latex/__init__.py:123 msgid "" "\n" "Run 'make' in that directory to run these through (pdf)latex\n" "(use `make latexpdf' here to do that automatically)." msgstr "" -#: sphinx/builders/latex/__init__.py:154 +#: sphinx/builders/latex/__init__.py:163 msgid "no \"latex_documents\" config value found; no documents will be written" msgstr "" -#: sphinx/builders/latex/__init__.py:162 +#: sphinx/builders/latex/__init__.py:171 #, python-format msgid "\"latex_documents\" config value references unknown document %s" msgstr "" -#: sphinx/builders/latex/__init__.py:312 +#: sphinx/builders/latex/__init__.py:211 sphinx/domains/std.py:501 +#: sphinx/templates/latex/latex.tex_t:79 +#: sphinx/themes/basic/genindex-single.html:30 +#: sphinx/themes/basic/genindex-single.html:55 +#: sphinx/themes/basic/genindex-split.html:11 +#: sphinx/themes/basic/genindex-split.html:14 +#: sphinx/themes/basic/genindex.html:30 sphinx/themes/basic/genindex.html:33 +#: sphinx/themes/basic/genindex.html:66 sphinx/themes/basic/layout.html:150 +#: sphinx/writers/texinfo.py:522 +msgid "Index" +msgstr "" + +#: sphinx/builders/latex/__init__.py:214 sphinx/templates/latex/latex.tex_t:64 +msgid "Release" +msgstr "" + +#: sphinx/builders/latex/__init__.py:222 sphinx/writers/latex.py:549 +#, python-format +msgid "no Babel option known for language %r" +msgstr "" + +#: sphinx/builders/latex/__init__.py:361 +msgid "copying TeX support files" +msgstr "" + +#: sphinx/builders/latex/__init__.py:382 msgid "copying TeX support files..." msgstr "" -#: sphinx/builders/latex/__init__.py:327 -msgid "copying additional files..." +#: sphinx/builders/latex/__init__.py:402 +msgid "copying additional files" msgstr "" -#: sphinx/builders/latex/__init__.py:364 -#, python-format -msgid "" -"Invalid latex_documents.title found (might contain non-ASCII chars. " -"Please use u\"...\" notation instead): %r" -msgstr "" - -#: sphinx/builders/latex/__init__.py:372 -#, python-format -msgid "" -"Invalid latex_documents.author found (might contain non-ASCII chars. " -"Please use u\"...\" notation instead): %r" -msgstr "" - -#: sphinx/builders/latex/__init__.py:378 +#: sphinx/builders/latex/__init__.py:445 #, python-format msgid "Unknown configure key: latex_elements[%r]. ignored." msgstr "" -#: sphinx/cmd/build.py:42 +#: sphinx/cmd/build.py:38 msgid "Exception occurred while building, starting debugger:" msgstr "" -#: sphinx/cmd/build.py:52 +#: sphinx/cmd/build.py:48 msgid "interrupted!" msgstr "" -#: sphinx/cmd/build.py:54 +#: sphinx/cmd/build.py:50 msgid "reST markup error:" msgstr "" -#: sphinx/cmd/build.py:60 +#: sphinx/cmd/build.py:56 msgid "Encoding error:" msgstr "" -#: sphinx/cmd/build.py:63 sphinx/cmd/build.py:78 +#: sphinx/cmd/build.py:59 sphinx/cmd/build.py:74 #, python-format msgid "" "The full traceback has been saved in %s, if you want to report the issue " "to the developers." msgstr "" -#: sphinx/cmd/build.py:67 +#: sphinx/cmd/build.py:63 msgid "Recursion error:" msgstr "" -#: sphinx/cmd/build.py:70 +#: sphinx/cmd/build.py:66 msgid "" "This can happen with very large or deeply nested source files. You can " "carefully increase the default Python recursion limit of 1000 in conf.py " "with e.g.:" msgstr "" -#: sphinx/cmd/build.py:73 +#: sphinx/cmd/build.py:69 msgid " import sys; sys.setrecursionlimit(1500)" msgstr "" -#: sphinx/cmd/build.py:75 +#: sphinx/cmd/build.py:71 msgid "Exception occurred:" msgstr "" -#: sphinx/cmd/build.py:81 +#: sphinx/cmd/build.py:77 msgid "" "Please also report this if it was a user error, so that a better error " "message can be provided next time." msgstr "" -#: sphinx/cmd/build.py:84 +#: sphinx/cmd/build.py:80 msgid "" "A bug report can be filed in the tracker at . Thanks!" msgstr "" -#: sphinx/cmd/build.py:101 +#: sphinx/cmd/build.py:97 msgid "job number should be a positive number" msgstr "" -#: sphinx/cmd/build.py:110 sphinx/cmd/quickstart.py:528 -#: sphinx/ext/apidoc.py:303 sphinx/ext/autosummary/generate.py:369 +#: sphinx/cmd/build.py:106 sphinx/cmd/quickstart.py:498 +#: sphinx/ext/apidoc.py:298 sphinx/ext/autosummary/generate.py:365 msgid "For more information, visit ." msgstr "" -#: sphinx/cmd/build.py:111 +#: sphinx/cmd/build.py:107 msgid "" "\n" "Generate documentation from source files.\n" @@ -1262,253 +1153,247 @@ msgid "" "files can be built by specifying individual filenames.\n" msgstr "" -#: sphinx/cmd/build.py:132 +#: sphinx/cmd/build.py:128 msgid "path to documentation source files" msgstr "" -#: sphinx/cmd/build.py:134 +#: sphinx/cmd/build.py:130 msgid "path to output directory" msgstr "" -#: sphinx/cmd/build.py:136 +#: sphinx/cmd/build.py:132 msgid "a list of specific files to rebuild. Ignored if -a is specified" msgstr "" -#: sphinx/cmd/build.py:139 +#: sphinx/cmd/build.py:135 msgid "general options" msgstr "" -#: sphinx/cmd/build.py:142 +#: sphinx/cmd/build.py:138 msgid "builder to use (default: html)" msgstr "" -#: sphinx/cmd/build.py:144 +#: sphinx/cmd/build.py:140 msgid "write all files (default: only write new and changed files)" msgstr "" -#: sphinx/cmd/build.py:147 +#: sphinx/cmd/build.py:143 msgid "don't use a saved environment, always read all files" msgstr "" -#: sphinx/cmd/build.py:150 +#: sphinx/cmd/build.py:146 msgid "" "path for the cached environment and doctree files (default: " "OUTPUTDIR/.doctrees)" msgstr "" -#: sphinx/cmd/build.py:153 +#: sphinx/cmd/build.py:149 msgid "" "build in parallel with N processes where possible (special value \"auto\"" " will set N to cpu-count)" msgstr "" -#: sphinx/cmd/build.py:157 +#: sphinx/cmd/build.py:153 msgid "" "path where configuration file (conf.py) is located (default: same as " "SOURCEDIR)" msgstr "" -#: sphinx/cmd/build.py:160 +#: sphinx/cmd/build.py:156 msgid "use no config file at all, only -D options" msgstr "" -#: sphinx/cmd/build.py:163 +#: sphinx/cmd/build.py:159 msgid "override a setting in configuration file" msgstr "" -#: sphinx/cmd/build.py:166 +#: sphinx/cmd/build.py:162 msgid "pass a value into HTML templates" msgstr "" -#: sphinx/cmd/build.py:169 +#: sphinx/cmd/build.py:165 msgid "define tag: include \"only\" blocks with TAG" msgstr "" -#: sphinx/cmd/build.py:171 +#: sphinx/cmd/build.py:167 msgid "nit-picky mode, warn about all missing references" msgstr "" -#: sphinx/cmd/build.py:174 +#: sphinx/cmd/build.py:170 msgid "console output options" msgstr "" -#: sphinx/cmd/build.py:176 +#: sphinx/cmd/build.py:172 msgid "increase verbosity (can be repeated)" msgstr "" -#: sphinx/cmd/build.py:178 +#: sphinx/cmd/build.py:174 msgid "no output on stdout, just warnings on stderr" msgstr "" -#: sphinx/cmd/build.py:180 +#: sphinx/cmd/build.py:176 msgid "no output at all, not even warnings" msgstr "" -#: sphinx/cmd/build.py:183 +#: sphinx/cmd/build.py:179 msgid "do emit colored output (default: auto-detect)" msgstr "" -#: sphinx/cmd/build.py:186 +#: sphinx/cmd/build.py:182 msgid "do not emit colored output (default: auto-detect)" msgstr "" -#: sphinx/cmd/build.py:189 +#: sphinx/cmd/build.py:185 msgid "write warnings (and errors) to given file" msgstr "" -#: sphinx/cmd/build.py:191 +#: sphinx/cmd/build.py:187 msgid "turn warnings into errors" msgstr "" -#: sphinx/cmd/build.py:193 +#: sphinx/cmd/build.py:189 msgid "With -W, Keep going when getting warnings" msgstr "" -#: sphinx/cmd/build.py:195 +#: sphinx/cmd/build.py:191 msgid "show full traceback on exception" msgstr "" -#: sphinx/cmd/build.py:197 +#: sphinx/cmd/build.py:193 msgid "run Pdb on exception" msgstr "" -#: sphinx/cmd/build.py:231 +#: sphinx/cmd/build.py:227 #, python-format msgid "cannot find files %r" msgstr "" -#: sphinx/cmd/build.py:241 +#: sphinx/cmd/build.py:230 msgid "cannot combine -a option and filenames" msgstr "" -#: sphinx/cmd/build.py:260 +#: sphinx/cmd/build.py:249 #, python-format msgid "cannot open warning file %r: %s" msgstr "" -#: sphinx/cmd/build.py:270 +#: sphinx/cmd/build.py:259 msgid "-D option argument must be in the form name=value" msgstr "" -#: sphinx/cmd/build.py:282 +#: sphinx/cmd/build.py:266 msgid "-A option argument must be in the form name=value" msgstr "" -#: sphinx/cmd/quickstart.py:56 +#: sphinx/cmd/quickstart.py:52 msgid "automatically insert docstrings from modules" msgstr "" -#: sphinx/cmd/quickstart.py:57 +#: sphinx/cmd/quickstart.py:53 msgid "automatically test code snippets in doctest blocks" msgstr "" -#: sphinx/cmd/quickstart.py:58 +#: sphinx/cmd/quickstart.py:54 msgid "link between Sphinx documentation of different projects" msgstr "" -#: sphinx/cmd/quickstart.py:59 +#: sphinx/cmd/quickstart.py:55 msgid "write \"todo\" entries that can be shown or hidden on build" msgstr "" -#: sphinx/cmd/quickstart.py:60 +#: sphinx/cmd/quickstart.py:56 msgid "checks for documentation coverage" msgstr "" -#: sphinx/cmd/quickstart.py:61 +#: sphinx/cmd/quickstart.py:57 msgid "include math, rendered as PNG or SVG images" msgstr "" -#: sphinx/cmd/quickstart.py:62 +#: sphinx/cmd/quickstart.py:58 msgid "include math, rendered in the browser by MathJax" msgstr "" -#: sphinx/cmd/quickstart.py:63 +#: sphinx/cmd/quickstart.py:59 msgid "conditional inclusion of content based on config values" msgstr "" -#: sphinx/cmd/quickstart.py:65 +#: sphinx/cmd/quickstart.py:61 msgid "include links to the source code of documented Python objects" msgstr "" -#: sphinx/cmd/quickstart.py:67 +#: sphinx/cmd/quickstart.py:63 msgid "create .nojekyll file to publish the document on GitHub pages" msgstr "" -#: sphinx/cmd/quickstart.py:111 +#: sphinx/cmd/quickstart.py:107 msgid "Please enter a valid path name." msgstr "" -#: sphinx/cmd/quickstart.py:123 +#: sphinx/cmd/quickstart.py:119 msgid "Please enter some text." msgstr "" -#: sphinx/cmd/quickstart.py:132 +#: sphinx/cmd/quickstart.py:128 #, python-format msgid "Please enter one of %s." msgstr "" -#: sphinx/cmd/quickstart.py:140 +#: sphinx/cmd/quickstart.py:136 msgid "Please enter either 'y' or 'n'." msgstr "" -#: sphinx/cmd/quickstart.py:147 +#: sphinx/cmd/quickstart.py:143 msgid "Please enter a file suffix, e.g. '.rst' or '.txt'." msgstr "" -#: sphinx/cmd/quickstart.py:170 +#: sphinx/cmd/quickstart.py:169 msgid "" "* Note: non-ASCII characters entered and terminal encoding unknown -- " "assuming UTF-8 or Latin-1." msgstr "" -#: sphinx/cmd/quickstart.py:193 -msgid "" -"* Note: non-ASCII default value provided and terminal encoding unknown --" -" assuming UTF-8 or Latin-1." -msgstr "" - -#: sphinx/cmd/quickstart.py:259 +#: sphinx/cmd/quickstart.py:248 #, python-format msgid "Welcome to the Sphinx %s quickstart utility." msgstr "" -#: sphinx/cmd/quickstart.py:260 +#: sphinx/cmd/quickstart.py:249 msgid "" "\n" "Please enter values for the following settings (just press Enter to\n" "accept a default value, if one is given in brackets)." msgstr "" -#: sphinx/cmd/quickstart.py:265 +#: sphinx/cmd/quickstart.py:254 #, python-format msgid "" "\n" "Selected root path: %s" msgstr "" -#: sphinx/cmd/quickstart.py:268 +#: sphinx/cmd/quickstart.py:257 msgid "" "\n" "Enter the root path for documentation." msgstr "" -#: sphinx/cmd/quickstart.py:270 +#: sphinx/cmd/quickstart.py:259 msgid "Root path for the documentation" msgstr "" -#: sphinx/cmd/quickstart.py:275 +#: sphinx/cmd/quickstart.py:264 msgid "Error: an existing conf.py has been found in the selected root path." msgstr "" -#: sphinx/cmd/quickstart.py:277 +#: sphinx/cmd/quickstart.py:266 msgid "sphinx-quickstart will not overwrite existing Sphinx projects." msgstr "" -#: sphinx/cmd/quickstart.py:279 +#: sphinx/cmd/quickstart.py:268 msgid "Please enter a new root path (or just Enter to exit)" msgstr "" -#: sphinx/cmd/quickstart.py:285 +#: sphinx/cmd/quickstart.py:274 msgid "" "\n" "You have two options for placing the build directory for Sphinx output.\n" @@ -1517,11 +1402,11 @@ msgid "" "\"source\" and \"build\" directories within the root path." msgstr "" -#: sphinx/cmd/quickstart.py:289 +#: sphinx/cmd/quickstart.py:278 msgid "Separate source and build directories (y/n)" msgstr "" -#: sphinx/cmd/quickstart.py:293 +#: sphinx/cmd/quickstart.py:282 msgid "" "\n" "Inside the root directory, two more directories will be created; " @@ -1532,25 +1417,25 @@ msgid "" "underscore." msgstr "" -#: sphinx/cmd/quickstart.py:297 +#: sphinx/cmd/quickstart.py:286 msgid "Name prefix for templates and static dir" msgstr "" -#: sphinx/cmd/quickstart.py:300 +#: sphinx/cmd/quickstart.py:289 msgid "" "\n" "The project name will occur in several places in the built documentation." msgstr "" -#: sphinx/cmd/quickstart.py:302 +#: sphinx/cmd/quickstart.py:291 msgid "Project name" msgstr "" -#: sphinx/cmd/quickstart.py:304 +#: sphinx/cmd/quickstart.py:293 msgid "Author name(s)" msgstr "" -#: sphinx/cmd/quickstart.py:307 +#: sphinx/cmd/quickstart.py:296 msgid "" "\n" "Sphinx has the notion of a \"version\" and a \"release\" for the\n" @@ -1560,15 +1445,15 @@ msgid "" "just set both to the same value." msgstr "" -#: sphinx/cmd/quickstart.py:313 +#: sphinx/cmd/quickstart.py:302 msgid "Project version" msgstr "" -#: sphinx/cmd/quickstart.py:315 +#: sphinx/cmd/quickstart.py:304 msgid "Project release" msgstr "" -#: sphinx/cmd/quickstart.py:318 +#: sphinx/cmd/quickstart.py:307 msgid "" "\n" "If the documents are to be written in a language other than English,\n" @@ -1579,22 +1464,22 @@ msgid "" "http://sphinx-doc.org/config.html#confval-language." msgstr "" -#: sphinx/cmd/quickstart.py:325 +#: sphinx/cmd/quickstart.py:314 msgid "Project language" msgstr "" -#: sphinx/cmd/quickstart.py:330 +#: sphinx/cmd/quickstart.py:319 msgid "" "\n" "The file name suffix for source files. Commonly, this is either \".txt\"\n" "or \".rst\". Only files with this suffix are considered documents." msgstr "" -#: sphinx/cmd/quickstart.py:333 +#: sphinx/cmd/quickstart.py:322 msgid "Source file suffix" msgstr "" -#: sphinx/cmd/quickstart.py:336 +#: sphinx/cmd/quickstart.py:325 msgid "" "\n" "One document is special in that it is considered the top node of the\n" @@ -1603,36 +1488,36 @@ msgid "" "document is a custom template, you can also set this to another filename." msgstr "" -#: sphinx/cmd/quickstart.py:341 +#: sphinx/cmd/quickstart.py:330 msgid "Name of your master document (without suffix)" msgstr "" -#: sphinx/cmd/quickstart.py:347 +#: sphinx/cmd/quickstart.py:336 #, python-format msgid "" "Error: the master file %s has already been found in the selected root " "path." msgstr "" -#: sphinx/cmd/quickstart.py:349 +#: sphinx/cmd/quickstart.py:338 msgid "sphinx-quickstart will not overwrite the existing file." msgstr "" -#: sphinx/cmd/quickstart.py:351 +#: sphinx/cmd/quickstart.py:340 msgid "Please enter a new file name, or rename the existing file and press Enter" msgstr "" -#: sphinx/cmd/quickstart.py:355 +#: sphinx/cmd/quickstart.py:344 msgid "Indicate which of the following Sphinx extensions should be enabled:" msgstr "" -#: sphinx/cmd/quickstart.py:365 +#: sphinx/cmd/quickstart.py:354 msgid "" "Note: imgmath and mathjax cannot be enabled at the same time. imgmath has" " been deselected." msgstr "" -#: sphinx/cmd/quickstart.py:370 +#: sphinx/cmd/quickstart.py:359 msgid "" "\n" "A Makefile and a Windows command file can be generated for you so that " @@ -1641,29 +1526,29 @@ msgid "" "directly." msgstr "" -#: sphinx/cmd/quickstart.py:374 +#: sphinx/cmd/quickstart.py:363 msgid "Create Makefile? (y/n)" msgstr "" -#: sphinx/cmd/quickstart.py:377 +#: sphinx/cmd/quickstart.py:366 msgid "Create Windows command file? (y/n)" msgstr "" -#: sphinx/cmd/quickstart.py:439 sphinx/ext/apidoc.py:79 +#: sphinx/cmd/quickstart.py:409 sphinx/ext/apidoc.py:74 #, python-format msgid "Creating file %s." msgstr "" -#: sphinx/cmd/quickstart.py:444 sphinx/ext/apidoc.py:77 +#: sphinx/cmd/quickstart.py:414 sphinx/ext/apidoc.py:72 #, python-format msgid "File %s already exists, skipping." msgstr "" -#: sphinx/cmd/quickstart.py:480 +#: sphinx/cmd/quickstart.py:450 msgid "Finished: An initial directory structure has been created." msgstr "" -#: sphinx/cmd/quickstart.py:481 +#: sphinx/cmd/quickstart.py:451 #, python-format msgid "" "\n" @@ -1672,26 +1557,26 @@ msgid "" "source files. " msgstr "" -#: sphinx/cmd/quickstart.py:483 +#: sphinx/cmd/quickstart.py:453 msgid "" "Use the Makefile to build the docs, like so:\n" " make builder\n" msgstr "" -#: sphinx/cmd/quickstart.py:486 +#: sphinx/cmd/quickstart.py:456 #, python-format msgid "" "Use the sphinx-build command to build the docs, like so:\n" " sphinx-build -b builder %s %s\n" msgstr "" -#: sphinx/cmd/quickstart.py:489 +#: sphinx/cmd/quickstart.py:459 msgid "" "where \"builder\" is one of the supported builders, e.g. html, latex or " "linkcheck.\n" msgstr "" -#: sphinx/cmd/quickstart.py:529 +#: sphinx/cmd/quickstart.py:499 msgid "" "\n" "Generate required files for a Sphinx project.\n" @@ -1702,215 +1587,215 @@ msgid "" "Makefile to be used with sphinx-build.\n" msgstr "" -#: sphinx/cmd/quickstart.py:539 +#: sphinx/cmd/quickstart.py:509 msgid "quiet mode" msgstr "" -#: sphinx/cmd/quickstart.py:544 +#: sphinx/cmd/quickstart.py:514 msgid "output path" msgstr "" -#: sphinx/cmd/quickstart.py:546 +#: sphinx/cmd/quickstart.py:516 msgid "Structure options" msgstr "" -#: sphinx/cmd/quickstart.py:548 +#: sphinx/cmd/quickstart.py:518 msgid "if specified, separate source and build dirs" msgstr "" -#: sphinx/cmd/quickstart.py:550 +#: sphinx/cmd/quickstart.py:520 msgid "replacement for dot in _templates etc." msgstr "" -#: sphinx/cmd/quickstart.py:552 +#: sphinx/cmd/quickstart.py:522 msgid "Project basic options" msgstr "" -#: sphinx/cmd/quickstart.py:554 +#: sphinx/cmd/quickstart.py:524 msgid "project name" msgstr "" -#: sphinx/cmd/quickstart.py:556 +#: sphinx/cmd/quickstart.py:526 msgid "author names" msgstr "" -#: sphinx/cmd/quickstart.py:558 +#: sphinx/cmd/quickstart.py:528 msgid "version of project" msgstr "" -#: sphinx/cmd/quickstart.py:560 +#: sphinx/cmd/quickstart.py:530 msgid "release of project" msgstr "" -#: sphinx/cmd/quickstart.py:562 +#: sphinx/cmd/quickstart.py:532 msgid "document language" msgstr "" -#: sphinx/cmd/quickstart.py:564 +#: sphinx/cmd/quickstart.py:534 msgid "source file suffix" msgstr "" -#: sphinx/cmd/quickstart.py:566 +#: sphinx/cmd/quickstart.py:536 msgid "master document name" msgstr "" -#: sphinx/cmd/quickstart.py:568 +#: sphinx/cmd/quickstart.py:538 msgid "use epub" msgstr "" -#: sphinx/cmd/quickstart.py:570 +#: sphinx/cmd/quickstart.py:540 msgid "Extension options" msgstr "" -#: sphinx/cmd/quickstart.py:574 sphinx/ext/apidoc.py:380 +#: sphinx/cmd/quickstart.py:544 sphinx/ext/apidoc.py:379 #, python-format msgid "enable %s extension" msgstr "" -#: sphinx/cmd/quickstart.py:576 +#: sphinx/cmd/quickstart.py:546 sphinx/ext/apidoc.py:375 msgid "enable arbitrary extensions" msgstr "" -#: sphinx/cmd/quickstart.py:578 +#: sphinx/cmd/quickstart.py:548 msgid "Makefile and Batchfile creation" msgstr "" -#: sphinx/cmd/quickstart.py:580 +#: sphinx/cmd/quickstart.py:550 msgid "create makefile" msgstr "" -#: sphinx/cmd/quickstart.py:582 +#: sphinx/cmd/quickstart.py:552 msgid "do not create makefile" msgstr "" -#: sphinx/cmd/quickstart.py:584 +#: sphinx/cmd/quickstart.py:554 msgid "create batchfile" msgstr "" -#: sphinx/cmd/quickstart.py:587 +#: sphinx/cmd/quickstart.py:557 msgid "do not create batchfile" msgstr "" -#: sphinx/cmd/quickstart.py:590 +#: sphinx/cmd/quickstart.py:560 msgid "use make-mode for Makefile/make.bat" msgstr "" -#: sphinx/cmd/quickstart.py:593 +#: sphinx/cmd/quickstart.py:563 msgid "do not use make-mode for Makefile/make.bat" msgstr "" -#: sphinx/cmd/quickstart.py:595 +#: sphinx/cmd/quickstart.py:565 msgid "Project templating" msgstr "" -#: sphinx/cmd/quickstart.py:598 +#: sphinx/cmd/quickstart.py:568 msgid "template directory for template files" msgstr "" -#: sphinx/cmd/quickstart.py:601 +#: sphinx/cmd/quickstart.py:571 msgid "define a template variable" msgstr "" -#: sphinx/cmd/quickstart.py:628 +#: sphinx/cmd/quickstart.py:605 msgid "" "\"quiet\" is specified, but any of \"project\" or \"author\" is not " "specified." msgstr "" -#: sphinx/cmd/quickstart.py:642 +#: sphinx/cmd/quickstart.py:619 msgid "Error: specified path is not a directory, or sphinx files already exist." msgstr "" -#: sphinx/cmd/quickstart.py:644 +#: sphinx/cmd/quickstart.py:621 msgid "" "sphinx-quickstart only generate into a empty directory. Please specify a " "new root path." msgstr "" -#: sphinx/cmd/quickstart.py:671 +#: sphinx/cmd/quickstart.py:636 #, python-format msgid "Invalid template variable: %s" msgstr "" -#: sphinx/directives/code.py:75 +#: sphinx/directives/code.py:74 msgid "Over dedent has detected" msgstr "" -#: sphinx/directives/code.py:95 +#: sphinx/directives/code.py:94 #, python-format msgid "Invalid caption: %s" msgstr "" -#: sphinx/directives/code.py:138 sphinx/directives/code.py:280 -#: sphinx/directives/code.py:450 +#: sphinx/directives/code.py:140 sphinx/directives/code.py:292 +#: sphinx/directives/code.py:462 #, python-format msgid "line number spec is out of range(1-%d): %r" msgstr "" -#: sphinx/directives/code.py:210 +#: sphinx/directives/code.py:222 #, python-format msgid "Cannot use both \"%s\" and \"%s\" options" msgstr "" -#: sphinx/directives/code.py:223 +#: sphinx/directives/code.py:235 #, python-format msgid "Include file %r not found or reading it failed" msgstr "" -#: sphinx/directives/code.py:225 +#: sphinx/directives/code.py:237 #, python-format msgid "" "Encoding %r used for reading included file %r seems to be wrong, try " "giving an :encoding: option" msgstr "" -#: sphinx/directives/code.py:263 +#: sphinx/directives/code.py:275 #, python-format msgid "Object named %r not found in include file %r" msgstr "" -#: sphinx/directives/code.py:289 +#: sphinx/directives/code.py:301 msgid "Cannot use \"lineno-match\" with a disjoint set of \"lines\"" msgstr "" -#: sphinx/directives/code.py:294 +#: sphinx/directives/code.py:306 #, python-format msgid "Line spec %r: no lines pulled from include file %r" msgstr "" -#: sphinx/directives/other.py:169 +#: sphinx/directives/other.py:172 msgid "Section author: " msgstr "" -#: sphinx/directives/other.py:171 +#: sphinx/directives/other.py:174 msgid "Module author: " msgstr "" -#: sphinx/directives/other.py:173 +#: sphinx/directives/other.py:176 msgid "Code author: " msgstr "" -#: sphinx/directives/other.py:175 +#: sphinx/directives/other.py:178 msgid "Author: " msgstr "" -#: sphinx/domains/__init__.py:336 +#: sphinx/domains/__init__.py:344 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:65 sphinx/domains/cpp.py:6070 -#: sphinx/domains/python.py:212 sphinx/ext/napoleon/docstring.py:695 +#: sphinx/domains/c.py:64 sphinx/domains/cpp.py:6371 +#: sphinx/domains/python.py:210 sphinx/ext/napoleon/docstring.py:696 msgid "Parameters" msgstr "" -#: sphinx/domains/c.py:68 sphinx/domains/cpp.py:6079 -#: sphinx/domains/javascript.py:211 sphinx/domains/python.py:224 +#: sphinx/domains/c.py:67 sphinx/domains/cpp.py:6380 +#: sphinx/domains/javascript.py:210 sphinx/domains/python.py:222 msgid "Returns" msgstr "" -#: sphinx/domains/c.py:70 sphinx/domains/javascript.py:213 -#: sphinx/domains/python.py:226 +#: sphinx/domains/c.py:69 sphinx/domains/javascript.py:212 +#: sphinx/domains/python.py:224 msgid "Return type" msgstr "" @@ -1939,12 +1824,12 @@ msgstr "" msgid "%s (C variable)" msgstr "" -#: sphinx/domains/c.py:258 sphinx/domains/cpp.py:6547 -#: sphinx/domains/javascript.py:299 sphinx/domains/python.py:740 +#: sphinx/domains/c.py:258 sphinx/domains/cpp.py:6956 +#: sphinx/domains/javascript.py:298 sphinx/domains/python.py:741 msgid "function" msgstr "" -#: sphinx/domains/c.py:259 sphinx/domains/cpp.py:6548 +#: sphinx/domains/c.py:259 sphinx/domains/cpp.py:6957 msgid "member" msgstr "" @@ -1952,7 +1837,7 @@ msgstr "" msgid "macro" msgstr "" -#: sphinx/domains/c.py:261 sphinx/domains/cpp.py:6549 +#: sphinx/domains/c.py:261 sphinx/domains/cpp.py:6958 msgid "type" msgstr "" @@ -1960,297 +1845,262 @@ msgstr "" msgid "variable" msgstr "" -#: sphinx/domains/changeset.py:34 +#: sphinx/domains/changeset.py:33 #, python-format msgid "New in version %s" msgstr "" -#: sphinx/domains/changeset.py:35 +#: sphinx/domains/changeset.py:34 #, python-format msgid "Changed in version %s" msgstr "" -#: sphinx/domains/changeset.py:36 +#: sphinx/domains/changeset.py:35 #, python-format msgid "Deprecated since version %s" msgstr "" -#: sphinx/domains/cpp.py:4037 +#: sphinx/domains/cpp.py:4297 #, python-format msgid "" "Duplicate declaration, also defined in '%s'.\n" "Declaration is '%s'." msgstr "" -#: sphinx/domains/cpp.py:6073 +#: sphinx/domains/cpp.py:6374 msgid "Template Parameters" msgstr "" -#: sphinx/domains/cpp.py:6076 sphinx/domains/javascript.py:208 +#: sphinx/domains/cpp.py:6377 sphinx/domains/javascript.py:207 msgid "Throws" msgstr "" -#: sphinx/domains/cpp.py:6283 +#: sphinx/domains/cpp.py:6505 #, python-format -msgid "%s (C++ type)" +msgid "%s (C++ %s)" msgstr "" -#: sphinx/domains/cpp.py:6293 -#, python-format -msgid "%s (C++ concept)" -msgstr "" - -#: sphinx/domains/cpp.py:6303 -#, python-format -msgid "%s (C++ member)" -msgstr "" - -#: sphinx/domains/cpp.py:6313 -#, python-format -msgid "%s (C++ function)" -msgstr "" - -#: sphinx/domains/cpp.py:6323 -#, python-format -msgid "%s (C++ class)" -msgstr "" - -#: sphinx/domains/cpp.py:6333 -#, python-format -msgid "%s (C++ union)" -msgstr "" - -#: sphinx/domains/cpp.py:6343 -#, python-format -msgid "%s (C++ enum)" -msgstr "" - -#: sphinx/domains/cpp.py:6363 -#, python-format -msgid "%s (C++ enumerator)" -msgstr "" - -#: sphinx/domains/cpp.py:6545 sphinx/domains/javascript.py:301 -#: sphinx/domains/python.py:742 +#: sphinx/domains/cpp.py:6954 sphinx/domains/javascript.py:300 +#: sphinx/domains/python.py:743 msgid "class" msgstr "" -#: sphinx/domains/cpp.py:6546 +#: sphinx/domains/cpp.py:6955 msgid "union" msgstr "" -#: sphinx/domains/cpp.py:6550 +#: sphinx/domains/cpp.py:6959 msgid "concept" msgstr "" -#: sphinx/domains/cpp.py:6551 +#: sphinx/domains/cpp.py:6960 msgid "enum" msgstr "" -#: sphinx/domains/cpp.py:6552 +#: sphinx/domains/cpp.py:6961 msgid "enumerator" msgstr "" -#: sphinx/domains/cpp.py:6617 +#: sphinx/domains/cpp.py:7054 #, python-format msgid "" "Duplicate declaration, also defined in '%s'.\n" "Name of declaration is '%s'." msgstr "" -#: sphinx/domains/javascript.py:132 sphinx/domains/python.py:431 +#: sphinx/domains/javascript.py:131 sphinx/domains/python.py:429 #, python-format msgid "%s() (built-in function)" msgstr "" -#: sphinx/domains/javascript.py:133 sphinx/domains/python.py:496 +#: sphinx/domains/javascript.py:132 sphinx/domains/python.py:494 #, python-format msgid "%s() (%s method)" msgstr "" -#: sphinx/domains/javascript.py:135 +#: sphinx/domains/javascript.py:134 #, python-format msgid "%s() (class)" msgstr "" -#: sphinx/domains/javascript.py:137 +#: sphinx/domains/javascript.py:136 #, python-format msgid "%s (global variable or constant)" msgstr "" -#: sphinx/domains/javascript.py:139 sphinx/domains/python.py:534 +#: sphinx/domains/javascript.py:138 sphinx/domains/python.py:532 #, python-format msgid "%s (%s attribute)" msgstr "" -#: sphinx/domains/javascript.py:205 +#: sphinx/domains/javascript.py:204 msgid "Arguments" msgstr "" -#: sphinx/domains/javascript.py:266 sphinx/domains/python.py:611 +#: sphinx/domains/javascript.py:265 sphinx/domains/python.py:609 #, python-format msgid "%s (module)" msgstr "" -#: sphinx/domains/javascript.py:300 sphinx/domains/python.py:744 +#: sphinx/domains/javascript.py:299 sphinx/domains/python.py:745 msgid "method" msgstr "" -#: sphinx/domains/javascript.py:302 sphinx/domains/python.py:741 +#: sphinx/domains/javascript.py:301 sphinx/domains/python.py:742 msgid "data" msgstr "" -#: sphinx/domains/javascript.py:303 sphinx/domains/python.py:747 +#: sphinx/domains/javascript.py:302 sphinx/domains/python.py:748 msgid "attribute" msgstr "" -#: sphinx/domains/javascript.py:304 sphinx/domains/python.py:50 -#: sphinx/domains/python.py:748 +#: sphinx/domains/javascript.py:303 sphinx/domains/python.py:49 +#: sphinx/domains/python.py:749 msgid "module" msgstr "" -#: sphinx/domains/math.py:88 sphinx/writers/latex.py:2529 +#: sphinx/domains/math.py:101 sphinx/writers/latex.py:2451 #, python-format msgid "Invalid math_eqref_format: %r" msgstr "" -#: sphinx/domains/math.py:113 +#: sphinx/domains/math.py:126 #, python-format msgid "duplicate label of equation %s, other instance in %s" msgstr "" -#: sphinx/domains/python.py:51 +#: sphinx/domains/python.py:50 msgid "keyword" msgstr "" -#: sphinx/domains/python.py:52 +#: sphinx/domains/python.py:51 msgid "operator" msgstr "" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:52 msgid "object" msgstr "" -#: sphinx/domains/python.py:54 sphinx/domains/python.py:743 +#: sphinx/domains/python.py:53 sphinx/domains/python.py:744 msgid "exception" msgstr "" -#: sphinx/domains/python.py:55 +#: sphinx/domains/python.py:54 msgid "statement" msgstr "" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:55 msgid "built-in function" msgstr "" -#: sphinx/domains/python.py:217 +#: sphinx/domains/python.py:215 msgid "Variables" msgstr "" -#: sphinx/domains/python.py:221 +#: sphinx/domains/python.py:219 msgid "Raises" msgstr "" -#: sphinx/domains/python.py:432 sphinx/domains/python.py:490 -#: sphinx/domains/python.py:502 sphinx/domains/python.py:515 +#: sphinx/domains/python.py:430 sphinx/domains/python.py:488 +#: sphinx/domains/python.py:500 sphinx/domains/python.py:513 #, python-format msgid "%s() (in module %s)" msgstr "" -#: sphinx/domains/python.py:435 +#: sphinx/domains/python.py:433 #, python-format msgid "%s (built-in variable)" msgstr "" -#: sphinx/domains/python.py:436 sphinx/domains/python.py:528 +#: sphinx/domains/python.py:434 sphinx/domains/python.py:526 #, python-format msgid "%s (in module %s)" msgstr "" -#: sphinx/domains/python.py:456 +#: sphinx/domains/python.py:454 #, python-format msgid "%s (built-in class)" msgstr "" -#: sphinx/domains/python.py:457 +#: sphinx/domains/python.py:455 #, python-format msgid "%s (class in %s)" msgstr "" -#: sphinx/domains/python.py:494 +#: sphinx/domains/python.py:492 #, python-format msgid "%s() (%s.%s method)" msgstr "" -#: sphinx/domains/python.py:506 +#: sphinx/domains/python.py:504 #, python-format msgid "%s() (%s.%s static method)" msgstr "" -#: sphinx/domains/python.py:509 +#: sphinx/domains/python.py:507 #, python-format msgid "%s() (%s static method)" msgstr "" -#: sphinx/domains/python.py:519 +#: sphinx/domains/python.py:517 #, python-format msgid "%s() (%s.%s class method)" msgstr "" -#: sphinx/domains/python.py:522 +#: sphinx/domains/python.py:520 #, python-format msgid "%s() (%s class method)" msgstr "" -#: sphinx/domains/python.py:532 +#: sphinx/domains/python.py:530 #, python-format msgid "%s (%s.%s attribute)" msgstr "" -#: sphinx/domains/python.py:669 +#: sphinx/domains/python.py:667 msgid "Python Module Index" msgstr "" -#: sphinx/domains/python.py:670 +#: sphinx/domains/python.py:668 msgid "modules" msgstr "" -#: sphinx/domains/python.py:718 +#: sphinx/domains/python.py:719 msgid "Deprecated" msgstr "" -#: sphinx/domains/python.py:745 +#: sphinx/domains/python.py:746 msgid "class method" msgstr "" -#: sphinx/domains/python.py:746 +#: sphinx/domains/python.py:747 msgid "static method" msgstr "" -#: sphinx/domains/python.py:878 +#: sphinx/domains/python.py:879 #, python-format msgid "more than one target found for cross-reference %r: %s" msgstr "" -#: sphinx/domains/python.py:918 +#: sphinx/domains/python.py:917 msgid " (deprecated)" msgstr "" -#: sphinx/domains/rst.py:65 +#: sphinx/domains/rst.py:62 #, python-format msgid "%s (directive)" msgstr "" -#: sphinx/domains/rst.py:67 +#: sphinx/domains/rst.py:64 #, python-format msgid "%s (role)" msgstr "" -#: sphinx/domains/rst.py:119 +#: sphinx/domains/rst.py:116 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:120 +#: sphinx/domains/rst.py:117 msgid "role" msgstr "" @@ -2259,209 +2109,200 @@ msgstr "" msgid "environment variable; %s" msgstr "" -#: sphinx/domains/std.py:164 +#: sphinx/domains/std.py:166 #, python-format msgid "" "Malformed option description %r, should look like \"opt\", \"-opt args\"," " \"--opt args\", \"/opt args\" or \"+opt args\"" msgstr "" -#: sphinx/domains/std.py:203 +#: sphinx/domains/std.py:207 #, python-format msgid "%scommand line option; %s" msgstr "" -#: sphinx/domains/std.py:455 +#: sphinx/domains/std.py:458 msgid "glossary term" msgstr "" -#: sphinx/domains/std.py:456 +#: sphinx/domains/std.py:459 msgid "grammar token" msgstr "" -#: sphinx/domains/std.py:457 +#: sphinx/domains/std.py:460 msgid "reference label" msgstr "" -#: sphinx/domains/std.py:459 +#: sphinx/domains/std.py:462 msgid "environment variable" msgstr "" -#: sphinx/domains/std.py:460 +#: sphinx/domains/std.py:463 msgid "program option" msgstr "" -#: sphinx/domains/std.py:461 +#: sphinx/domains/std.py:464 msgid "document" msgstr "" -#: sphinx/domains/std.py:498 sphinx/themes/basic/genindex-single.html:30 -#: sphinx/themes/basic/genindex-single.html:55 -#: sphinx/themes/basic/genindex-split.html:11 -#: sphinx/themes/basic/genindex-split.html:14 -#: sphinx/themes/basic/genindex.html:30 sphinx/themes/basic/genindex.html:33 -#: sphinx/themes/basic/genindex.html:66 sphinx/themes/basic/layout.html:150 -#: sphinx/writers/latex.py:503 sphinx/writers/texinfo.py:516 -msgid "Index" -msgstr "" - -#: sphinx/domains/std.py:499 +#: sphinx/domains/std.py:502 msgid "Module Index" msgstr "" -#: sphinx/domains/std.py:500 sphinx/themes/basic/defindex.html:25 +#: sphinx/domains/std.py:503 sphinx/themes/basic/defindex.html:25 msgid "Search Page" msgstr "" -#: sphinx/domains/std.py:595 +#: sphinx/domains/std.py:598 #, python-format msgid "duplicate citation %s, other instance in %s" msgstr "" -#: sphinx/domains/std.py:627 sphinx/ext/autosectionlabel.py:47 +#: sphinx/domains/std.py:631 sphinx/ext/autosectionlabel.py:61 #, python-format msgid "duplicate label %s, other instance in %s" msgstr "" -#: sphinx/domains/std.py:652 +#: sphinx/domains/std.py:665 #, python-format msgid "Citation [%s] is not referenced." msgstr "" -#: sphinx/domains/std.py:735 +#: sphinx/domains/std.py:748 msgid "numfig is disabled. :numref: is ignored." msgstr "" -#: sphinx/domains/std.py:743 +#: sphinx/domains/std.py:756 #, python-format msgid "no number is assigned for %s: %s" msgstr "" -#: sphinx/domains/std.py:754 +#: sphinx/domains/std.py:767 #, python-format msgid "the link has no caption: %s" msgstr "" -#: sphinx/domains/std.py:768 +#: sphinx/domains/std.py:781 #, python-format msgid "invalid numfig_format: %s (%r)" msgstr "" -#: sphinx/domains/std.py:771 +#: sphinx/domains/std.py:784 #, python-format msgid "invalid numfig_format: %s" msgstr "" -#: sphinx/environment/__init__.py:79 +#: sphinx/environment/__init__.py:69 msgid "new config" msgstr "" -#: sphinx/environment/__init__.py:80 +#: sphinx/environment/__init__.py:70 msgid "config changed" msgstr "" -#: sphinx/environment/__init__.py:81 +#: sphinx/environment/__init__.py:71 msgid "extensions changed" msgstr "" -#: sphinx/environment/__init__.py:223 +#: sphinx/environment/__init__.py:210 msgid "build environment version not current" msgstr "" -#: sphinx/environment/__init__.py:225 +#: sphinx/environment/__init__.py:212 msgid "source directory has changed" msgstr "" -#: sphinx/environment/__init__.py:292 +#: sphinx/environment/__init__.py:283 msgid "" "This environment is incompatible with the selected builder, please choose" " another doctree directory." msgstr "" -#: sphinx/environment/__init__.py:425 -msgid "document not readable. Ignored." -msgstr "" - -#: sphinx/environment/__init__.py:445 +#: sphinx/environment/__init__.py:408 #, python-format msgid "Failed to scan documents in %s: %r" msgstr "" -#: sphinx/environment/__init__.py:599 +#: sphinx/environment/__init__.py:536 #, python-format msgid "Domain %r is not registered" msgstr "" -#: sphinx/environment/__init__.py:694 +#: sphinx/environment/__init__.py:621 msgid "self referenced toctree found. Ignored." msgstr "" -#: sphinx/environment/__init__.py:734 +#: sphinx/environment/__init__.py:662 msgid "document isn't included in any toctree" msgstr "" -#: sphinx/environment/adapters/indexentries.py:85 +#: sphinx/environment/adapters/indexentries.py:82 #, python-format msgid "see %s" msgstr "" -#: sphinx/environment/adapters/indexentries.py:89 +#: sphinx/environment/adapters/indexentries.py:86 #, python-format msgid "see also %s" msgstr "" -#: sphinx/environment/adapters/indexentries.py:92 +#: sphinx/environment/adapters/indexentries.py:89 #, python-format msgid "unknown index entry type %r" msgstr "" -#: sphinx/environment/adapters/indexentries.py:159 sphinx/writers/latex.py:673 +#: sphinx/environment/adapters/indexentries.py:156 msgid "Symbols" msgstr "" -#: sphinx/environment/adapters/toctree.py:151 +#: sphinx/environment/adapters/toctree.py:153 #, python-format msgid "circular toctree references detected, ignoring: %s <- %s" msgstr "" -#: sphinx/environment/adapters/toctree.py:170 +#: sphinx/environment/adapters/toctree.py:172 #, python-format msgid "" "toctree contains reference to document %r that doesn't have a title: no " "link will be generated" msgstr "" -#: sphinx/environment/adapters/toctree.py:175 +#: sphinx/environment/adapters/toctree.py:178 +#, python-format +msgid "toctree contains reference to excluded document %r" +msgstr "" + +#: sphinx/environment/adapters/toctree.py:180 #, python-format msgid "toctree contains reference to nonexisting document %r" msgstr "" -#: sphinx/environment/collectors/asset.py:93 +#: sphinx/environment/collectors/asset.py:91 #, python-format msgid "image file not readable: %s" msgstr "" -#: sphinx/environment/collectors/asset.py:109 +#: sphinx/environment/collectors/asset.py:107 #, python-format msgid "image file %s not readable: %s" msgstr "" -#: sphinx/environment/collectors/asset.py:134 +#: sphinx/environment/collectors/asset.py:135 #, python-format msgid "download file not readable: %s" msgstr "" -#: sphinx/environment/collectors/toctree.py:193 +#: sphinx/environment/collectors/toctree.py:197 #, python-format msgid "%s is already assigned section numbers (nested numbered toctree?)" msgstr "" -#: sphinx/ext/apidoc.py:74 +#: sphinx/ext/apidoc.py:69 #, python-format msgid "Would create file %s." msgstr "" -#: sphinx/ext/apidoc.py:304 +#: sphinx/ext/apidoc.py:299 msgid "" "\n" "Look recursively in for Python modules and packages and " @@ -2476,344 +2317,373 @@ msgid "" "Note: By default this script will not overwrite already created files." msgstr "" -#: sphinx/ext/apidoc.py:317 +#: sphinx/ext/apidoc.py:312 msgid "path to module to document" msgstr "" -#: sphinx/ext/apidoc.py:319 +#: sphinx/ext/apidoc.py:314 msgid "fnmatch-style file and/or directory patterns to exclude from generation" msgstr "" -#: sphinx/ext/apidoc.py:324 +#: sphinx/ext/apidoc.py:319 msgid "directory to place all output" msgstr "" -#: sphinx/ext/apidoc.py:327 +#: sphinx/ext/apidoc.py:322 msgid "maximum depth of submodules to show in the TOC (default: 4)" msgstr "" -#: sphinx/ext/apidoc.py:330 +#: sphinx/ext/apidoc.py:325 msgid "overwrite existing files" msgstr "" -#: sphinx/ext/apidoc.py:333 +#: sphinx/ext/apidoc.py:328 msgid "" "follow symbolic links. Powerful when combined with " "collective.recipe.omelette." msgstr "" -#: sphinx/ext/apidoc.py:336 +#: sphinx/ext/apidoc.py:331 msgid "run the script without creating files" msgstr "" -#: sphinx/ext/apidoc.py:339 +#: sphinx/ext/apidoc.py:334 msgid "put documentation for each module on its own page" msgstr "" -#: sphinx/ext/apidoc.py:342 +#: sphinx/ext/apidoc.py:337 msgid "include \"_private\" modules" msgstr "" -#: sphinx/ext/apidoc.py:344 +#: sphinx/ext/apidoc.py:339 +msgid "filename of table of contents (default: modules)" +msgstr "" + +#: sphinx/ext/apidoc.py:341 msgid "don't create a table of contents file" msgstr "" -#: sphinx/ext/apidoc.py:347 +#: sphinx/ext/apidoc.py:344 msgid "" "don't create headings for the module/package packages (e.g. when the " "docstrings already contain them)" msgstr "" -#: sphinx/ext/apidoc.py:352 +#: sphinx/ext/apidoc.py:349 msgid "put module documentation before submodule documentation" msgstr "" -#: sphinx/ext/apidoc.py:356 +#: sphinx/ext/apidoc.py:353 msgid "" "interpret module paths according to PEP-0420 implicit namespaces " "specification" msgstr "" -#: sphinx/ext/apidoc.py:360 +#: sphinx/ext/apidoc.py:357 msgid "file suffix (default: rst)" msgstr "" -#: sphinx/ext/apidoc.py:362 +#: sphinx/ext/apidoc.py:359 msgid "generate a full project with sphinx-quickstart" msgstr "" -#: sphinx/ext/apidoc.py:365 +#: sphinx/ext/apidoc.py:362 msgid "append module_path to sys.path, used when --full is given" msgstr "" -#: sphinx/ext/apidoc.py:367 +#: sphinx/ext/apidoc.py:364 msgid "project name (default: root module name)" msgstr "" -#: sphinx/ext/apidoc.py:369 +#: sphinx/ext/apidoc.py:366 msgid "project author(s), used when --full is given" msgstr "" -#: sphinx/ext/apidoc.py:371 +#: sphinx/ext/apidoc.py:368 msgid "project version, used when --full is given" msgstr "" -#: sphinx/ext/apidoc.py:373 +#: sphinx/ext/apidoc.py:370 msgid "project release, used when --full is given, defaults to --doc-version" msgstr "" -#: sphinx/ext/apidoc.py:376 +#: sphinx/ext/apidoc.py:373 msgid "extension options" msgstr "" -#: sphinx/ext/apidoc.py:403 +#: sphinx/ext/apidoc.py:402 #, python-format msgid "%s is not a directory." msgstr "" -#: sphinx/ext/coverage.py:49 +#: sphinx/ext/coverage.py:46 #, python-format msgid "invalid regex %r in %s" msgstr "" -#: sphinx/ext/coverage.py:58 +#: sphinx/ext/coverage.py:55 #, python-format msgid "" "Testing of coverage in the sources finished, look at the results in " "%(outdir)spython.txt." msgstr "" -#: sphinx/ext/coverage.py:73 +#: sphinx/ext/coverage.py:70 #, python-format msgid "invalid regex %r in coverage_c_regexes" msgstr "" -#: sphinx/ext/coverage.py:155 +#: sphinx/ext/coverage.py:152 #, python-format msgid "module %s could not be imported: %s" msgstr "" -#: sphinx/ext/doctest.py:143 +#: sphinx/ext/doctest.py:142 #, python-format msgid "missing '+' or '-' in '%s' option." msgstr "" -#: sphinx/ext/doctest.py:148 +#: sphinx/ext/doctest.py:147 #, python-format msgid "'%s' is not a valid option." msgstr "" -#: sphinx/ext/doctest.py:162 +#: sphinx/ext/doctest.py:161 #, python-format msgid "'%s' is not a valid pyversion option" msgstr "" -#: sphinx/ext/doctest.py:231 +#: sphinx/ext/doctest.py:230 msgid "invalid TestCode type" msgstr "" -#: sphinx/ext/doctest.py:292 +#: sphinx/ext/doctest.py:291 #, python-format msgid "" "Testing of doctests in the sources finished, look at the results in " "%(outdir)s/output.txt." msgstr "" -#: sphinx/ext/doctest.py:444 +#: sphinx/ext/doctest.py:438 #, python-format msgid "no code/output in %s block at %s:%s" msgstr "" -#: sphinx/ext/doctest.py:535 +#: sphinx/ext/doctest.py:527 #, python-format msgid "ignoring invalid doctest code: %r" msgstr "" -#: sphinx/ext/graphviz.py:139 +#: sphinx/ext/graphviz.py:140 msgid "Graphviz directive cannot have both content and a filename argument" msgstr "" -#: sphinx/ext/graphviz.py:149 +#: sphinx/ext/graphviz.py:150 #, python-format msgid "External Graphviz file %r not found or reading it failed" msgstr "" -#: sphinx/ext/graphviz.py:155 +#: sphinx/ext/graphviz.py:156 msgid "Ignoring \"graphviz\" directive without content." msgstr "" -#: sphinx/ext/graphviz.py:255 +#: sphinx/ext/graphviz.py:252 +#, python-format +msgid "" +"dot did not produce an output file:\n" +"[stderr]\n" +"%r\n" +"[stdout]\n" +"%r" +msgstr "" + +#: sphinx/ext/graphviz.py:256 #, python-format msgid "" "dot command %r cannot be run (needed for graphviz output), check the " "graphviz_dot setting" msgstr "" -#: sphinx/ext/graphviz.py:273 +#: sphinx/ext/graphviz.py:263 #, python-format msgid "" "dot exited with error:\n" "[stderr]\n" -"%s\n" +"%r\n" "[stdout]\n" -"%s" +"%r" msgstr "" -#: sphinx/ext/graphviz.py:276 -#, python-format -msgid "" -"dot did not produce an output file:\n" -"[stderr]\n" -"%s\n" -"[stdout]\n" -"%s" -msgstr "" - -#: sphinx/ext/graphviz.py:287 +#: sphinx/ext/graphviz.py:273 #, python-format msgid "graphviz_output_format must be one of 'png', 'svg', but is %r" msgstr "" -#: sphinx/ext/graphviz.py:291 sphinx/ext/graphviz.py:345 -#: sphinx/ext/graphviz.py:383 +#: sphinx/ext/graphviz.py:277 sphinx/ext/graphviz.py:331 +#: sphinx/ext/graphviz.py:369 #, python-format msgid "dot code %r: %s" msgstr "" -#: sphinx/ext/graphviz.py:398 sphinx/ext/graphviz.py:407 +#: sphinx/ext/graphviz.py:384 sphinx/ext/graphviz.py:393 #, python-format msgid "[graph: %s]" msgstr "" -#: sphinx/ext/graphviz.py:400 sphinx/ext/graphviz.py:409 +#: sphinx/ext/graphviz.py:386 sphinx/ext/graphviz.py:395 msgid "[graph]" msgstr "" -#: sphinx/ext/imgconverter.py:44 sphinx/ext/imgconverter.py:81 +#: sphinx/ext/imgconverter.py:43 sphinx/ext/imgconverter.py:68 #, python-format msgid "convert command %r cannot be run.check the image_converter setting" msgstr "" -#: sphinx/ext/imgconverter.py:58 sphinx/ext/imgconverter.py:94 +#: sphinx/ext/imgconverter.py:48 sphinx/ext/imgconverter.py:73 #, python-format msgid "" "convert exited with error:\n" "[stderr]\n" -"%s\n" +"%r\n" "[stdout]\n" -"%s" +"%r" msgstr "" -#: sphinx/ext/imgmath.py:143 +#: sphinx/ext/imgmath.py:140 #, python-format msgid "" "LaTeX command %r cannot be run (needed for math display), check the " "imgmath_latex setting" msgstr "" -#: sphinx/ext/imgmath.py:163 +#: sphinx/ext/imgmath.py:155 #, python-format msgid "" "%s command %r cannot be run (needed for math display), check the " "imgmath_%s setting" msgstr "" -#: sphinx/ext/imgmath.py:302 +#: sphinx/ext/imgmath.py:290 #, python-format msgid "display latex %r: %s" msgstr "" -#: sphinx/ext/imgmath.py:329 +#: sphinx/ext/imgmath.py:317 #, python-format msgid "inline latex %r: %s" msgstr "" -#: sphinx/ext/imgmath.py:336 sphinx/ext/jsmath.py:47 sphinx/ext/mathjax.py:50 +#: sphinx/ext/imgmath.py:324 sphinx/ext/mathjax.py:54 msgid "Permalink to this equation" msgstr "" -#: sphinx/ext/intersphinx.py:226 +#: sphinx/ext/intersphinx.py:182 #, python-format -msgid "intersphinx identifier %r is not string. Ignored" +msgid "intersphinx inventory has moved: %s -> %s" msgstr "" -#: sphinx/ext/intersphinx.py:268 +#: sphinx/ext/intersphinx.py:217 +#, python-format +msgid "loading intersphinx inventory from %s..." +msgstr "" + +#: sphinx/ext/intersphinx.py:232 +msgid "" +"encountered some issues with some of the inventories, but they had " +"working alternatives:" +msgstr "" + +#: sphinx/ext/intersphinx.py:237 msgid "failed to reach any of the inventories with the following issues:" msgstr "" -#: sphinx/ext/intersphinx.py:343 +#: sphinx/ext/intersphinx.py:312 #, python-format msgid "(in %s v%s)" msgstr "" -#: sphinx/ext/intersphinx.py:345 +#: sphinx/ext/intersphinx.py:314 #, python-format msgid "(in %s)" msgstr "" -#: sphinx/ext/linkcode.py:75 sphinx/ext/viewcode.py:128 +#: sphinx/ext/intersphinx.py:348 +#, python-format +msgid "intersphinx identifier %r is not string. Ignored" +msgstr "" + +#: sphinx/ext/intersphinx.py:361 +#, python-format +msgid "Fail to read intersphinx_mapping[%s], Ignored: %r" +msgstr "" + +#: sphinx/ext/linkcode.py:72 sphinx/ext/viewcode.py:117 msgid "[source]" msgstr "" -#: sphinx/ext/todo.py:67 +#: sphinx/ext/todo.py:70 msgid "Todo" msgstr "" -#: sphinx/ext/todo.py:105 +#: sphinx/ext/todo.py:111 #, python-format msgid "TODO entry found: %s" msgstr "" -#: sphinx/ext/todo.py:153 +#: sphinx/ext/todo.py:160 msgid "<>" msgstr "" -#: sphinx/ext/todo.py:156 +#: sphinx/ext/todo.py:163 #, python-format msgid "(The <> is located in %s, line %d.)" msgstr "" -#: sphinx/ext/todo.py:165 +#: sphinx/ext/todo.py:172 msgid "original entry" msgstr "" -#: sphinx/ext/viewcode.py:196 +#: sphinx/ext/viewcode.py:158 +msgid "highlighting module code... " +msgstr "" + +#: sphinx/ext/viewcode.py:187 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:210 +#: sphinx/ext/viewcode.py:201 msgid "Module code" msgstr "" -#: sphinx/ext/viewcode.py:216 +#: sphinx/ext/viewcode.py:207 #, python-format msgid "

    Source code for %s

    " msgstr "" -#: sphinx/ext/viewcode.py:242 +#: sphinx/ext/viewcode.py:233 msgid "Overview: module code" msgstr "" -#: sphinx/ext/viewcode.py:243 +#: sphinx/ext/viewcode.py:234 msgid "

    All modules for which code is available

    " msgstr "" -#: sphinx/ext/autodoc/__init__.py:345 +#: sphinx/ext/autodoc/__init__.py:302 #, python-format msgid "invalid signature for auto%s (%r)" msgstr "" -#: sphinx/ext/autodoc/__init__.py:444 +#: sphinx/ext/autodoc/__init__.py:402 #, python-format msgid "error while formatting arguments for %s: %s" msgstr "" -#: sphinx/ext/autodoc/__init__.py:566 +#: sphinx/ext/autodoc/__init__.py:514 #, python-format msgid "missing attribute %s in object %s" msgstr "" -#: sphinx/ext/autodoc/__init__.py:654 +#: sphinx/ext/autodoc/__init__.py:602 #, python-format msgid "" "autodoc: failed to determine %r to be documented.the following exception " @@ -2821,7 +2691,7 @@ msgid "" "%s" msgstr "" -#: sphinx/ext/autodoc/__init__.py:746 +#: sphinx/ext/autodoc/__init__.py:694 #, python-format msgid "" "don't know which module to import for autodocumenting %r (try placing a " @@ -2829,67 +2699,86 @@ msgid "" "explicit module name)" msgstr "" -#: sphinx/ext/autodoc/__init__.py:835 +#: sphinx/ext/autodoc/__init__.py:788 msgid "\"::\" in automodule name doesn't make sense" msgstr "" -#: sphinx/ext/autodoc/__init__.py:842 +#: sphinx/ext/autodoc/__init__.py:796 #, python-format msgid "signature arguments or return annotation given for automodule %s" msgstr "" -#: sphinx/ext/autodoc/__init__.py:876 +#: sphinx/ext/autodoc/__init__.py:829 #, python-format msgid "" "__all__ should be a list of strings, not %r (in module %s) -- ignoring " "__all__" msgstr "" -#: sphinx/ext/autodoc/__init__.py:889 +#: sphinx/ext/autodoc/__init__.py:844 #, python-format msgid "" "missing attribute mentioned in :members: or __all__: module %s, attribute" " %s" msgstr "" -#: sphinx/ext/autodoc/__init__.py:1134 +#: sphinx/ext/autodoc/__init__.py:1128 #, python-format msgid "Bases: %s" msgstr "" -#: sphinx/ext/autodoc/__init__.py:1195 +#: sphinx/ext/autodoc/__init__.py:1185 #, python-format msgid "alias of :class:`%s`" msgstr "" -#: sphinx/ext/autodoc/__init__.py:1541 -msgid "" -"autodoc_default_flags is now deprecated. Please use " -"autodoc_default_options instead." -msgstr "" - -#: sphinx/ext/autodoc/__init__.py:1549 +#: sphinx/ext/autodoc/__init__.py:1470 #, python-format msgid "Ignoring invalid option in autodoc_default_flags: %r" msgstr "" -#: sphinx/ext/autosummary/__init__.py:683 +#: sphinx/ext/autosummary/__init__.py:256 +#, python-format +msgid "toctree references excluded document %r" +msgstr "" + +#: sphinx/ext/autosummary/__init__.py:258 +#, python-format +msgid "toctree references unknown document %r" +msgstr "" + +#: sphinx/ext/autosummary/__init__.py:292 +#, python-format +msgid "failed to import %s" +msgstr "" + +#: sphinx/ext/autosummary/__init__.py:307 +#, python-format +msgid "failed to parse name %s" +msgstr "" + +#: sphinx/ext/autosummary/__init__.py:311 +#, python-format +msgid "failed to import object %s" +msgstr "" + +#: sphinx/ext/autosummary/__init__.py:702 msgid "" "autosummary generats .rst files internally. But your source_suffix does " "not contain .rst. Skipped." msgstr "" -#: sphinx/ext/autosummary/generate.py:105 +#: sphinx/ext/autosummary/generate.py:102 #, python-format msgid "[autosummary] generating autosummary for: %s" msgstr "" -#: sphinx/ext/autosummary/generate.py:109 +#: sphinx/ext/autosummary/generate.py:106 #, python-format msgid "[autosummary] writing to %s" msgstr "" -#: sphinx/ext/autosummary/generate.py:370 +#: sphinx/ext/autosummary/generate.py:366 msgid "" "\n" "Generate ReStructuredText using autosummary directives.\n" @@ -2906,106 +2795,106 @@ msgid "" " pydoc sphinx.ext.autosummary\n" msgstr "" -#: sphinx/ext/autosummary/generate.py:387 +#: sphinx/ext/autosummary/generate.py:383 msgid "source files to generate rST files for" msgstr "" -#: sphinx/ext/autosummary/generate.py:391 +#: sphinx/ext/autosummary/generate.py:387 msgid "directory to place all output in" msgstr "" -#: sphinx/ext/autosummary/generate.py:394 +#: sphinx/ext/autosummary/generate.py:390 #, python-format msgid "default suffix for files (default: %(default)s)" msgstr "" -#: sphinx/ext/autosummary/generate.py:398 +#: sphinx/ext/autosummary/generate.py:394 #, python-format msgid "custom template directory (default: %(default)s)" msgstr "" -#: sphinx/ext/autosummary/generate.py:402 +#: sphinx/ext/autosummary/generate.py:398 #, python-format msgid "document imported members (default: %(default)s)" msgstr "" -#: sphinx/ext/napoleon/__init__.py:334 sphinx/ext/napoleon/docstring.py:668 +#: sphinx/ext/napoleon/__init__.py:330 sphinx/ext/napoleon/docstring.py:669 msgid "Keyword Arguments" msgstr "" -#: sphinx/ext/napoleon/docstring.py:626 +#: sphinx/ext/napoleon/docstring.py:627 msgid "Example" msgstr "" -#: sphinx/ext/napoleon/docstring.py:627 +#: sphinx/ext/napoleon/docstring.py:628 msgid "Examples" msgstr "" -#: sphinx/ext/napoleon/docstring.py:683 +#: sphinx/ext/napoleon/docstring.py:684 msgid "Notes" msgstr "" -#: sphinx/ext/napoleon/docstring.py:687 +#: sphinx/ext/napoleon/docstring.py:688 msgid "Other Parameters" msgstr "" -#: sphinx/ext/napoleon/docstring.py:739 +#: sphinx/ext/napoleon/docstring.py:717 msgid "References" msgstr "" -#: sphinx/ext/napoleon/docstring.py:776 +#: sphinx/ext/napoleon/docstring.py:754 msgid "Warns" msgstr "" -#: sphinx/ext/napoleon/docstring.py:781 +#: sphinx/ext/napoleon/docstring.py:759 msgid "Yields" msgstr "" -#: sphinx/locale/__init__.py:341 +#: sphinx/locale/__init__.py:307 msgid "Attention" msgstr "" -#: sphinx/locale/__init__.py:342 +#: sphinx/locale/__init__.py:308 msgid "Caution" msgstr "" -#: sphinx/locale/__init__.py:343 +#: sphinx/locale/__init__.py:309 msgid "Danger" msgstr "" -#: sphinx/locale/__init__.py:344 +#: sphinx/locale/__init__.py:310 msgid "Error" msgstr "" -#: sphinx/locale/__init__.py:345 +#: sphinx/locale/__init__.py:311 msgid "Hint" msgstr "" -#: sphinx/locale/__init__.py:346 +#: sphinx/locale/__init__.py:312 msgid "Important" msgstr "" -#: sphinx/locale/__init__.py:347 +#: sphinx/locale/__init__.py:313 msgid "Note" msgstr "" -#: sphinx/locale/__init__.py:348 +#: sphinx/locale/__init__.py:314 msgid "See also" msgstr "" -#: sphinx/locale/__init__.py:349 +#: sphinx/locale/__init__.py:315 msgid "Tip" msgstr "" -#: sphinx/locale/__init__.py:350 +#: sphinx/locale/__init__.py:316 msgid "Warning" msgstr "" -#: sphinx/templates/latex/longtable.tex_t:22 sphinx/writers/latex.py:664 +#: sphinx/templates/latex/longtable.tex_t:23 msgid "continued from previous page" msgstr "" -#: sphinx/templates/latex/longtable.tex_t:28 +#: sphinx/templates/latex/longtable.tex_t:29 msgid "Continued on next page" msgstr "" @@ -3024,7 +2913,7 @@ msgstr "" msgid "Go" msgstr "" -#: sphinx/themes/agogo/layout.html:81 sphinx/themes/basic/sourcelink.html:15 +#: sphinx/themes/agogo/layout.html:79 sphinx/themes/basic/sourcelink.html:15 msgid "Show Source" msgstr "" @@ -3171,12 +3060,12 @@ msgid "search" msgstr "" #: sphinx/themes/basic/search.html:46 sphinx/themes/basic/searchresults.html:21 -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:295 msgid "Search Results" msgstr "" #: sphinx/themes/basic/search.html:48 sphinx/themes/basic/searchresults.html:23 -#: sphinx/themes/basic/static/searchtools.js:292 +#: sphinx/themes/basic/static/searchtools.js:297 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -3218,36 +3107,36 @@ msgstr "" msgid "Other changes" msgstr "" -#: sphinx/themes/basic/static/doctools.js:195 sphinx/writers/html.py:403 -#: sphinx/writers/html.py:408 sphinx/writers/html5.py:349 -#: sphinx/writers/html5.py:354 +#: sphinx/themes/basic/static/doctools.js:194 sphinx/writers/html.py:454 +#: sphinx/writers/html.py:459 sphinx/writers/html5.py:400 +#: sphinx/writers/html5.py:405 msgid "Permalink to this headline" msgstr "" -#: sphinx/themes/basic/static/doctools.js:201 sphinx/writers/html.py:124 -#: sphinx/writers/html.py:135 sphinx/writers/html5.py:93 -#: sphinx/writers/html5.py:104 +#: sphinx/themes/basic/static/doctools.js:200 sphinx/writers/html.py:134 +#: sphinx/writers/html.py:145 sphinx/writers/html5.py:103 +#: sphinx/writers/html5.py:114 msgid "Permalink to this definition" msgstr "" -#: sphinx/themes/basic/static/doctools.js:234 +#: sphinx/themes/basic/static/doctools.js:233 msgid "Hide Search Matches" msgstr "" -#: sphinx/themes/basic/static/searchtools.js:121 +#: sphinx/themes/basic/static/searchtools.js:131 msgid "Searching" msgstr "" -#: sphinx/themes/basic/static/searchtools.js:126 +#: sphinx/themes/basic/static/searchtools.js:136 msgid "Preparing search..." msgstr "" -#: sphinx/themes/basic/static/searchtools.js:294 +#: sphinx/themes/basic/static/searchtools.js:299 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "" -#: sphinx/themes/basic/static/searchtools.js:347 +#: sphinx/themes/basic/static/searchtools.js:352 msgid ", in " msgstr "" @@ -3264,75 +3153,88 @@ msgstr "" msgid "Contents" msgstr "" -#: sphinx/transforms/__init__.py:258 +#: sphinx/transforms/__init__.py:259 #, python-format msgid "4 column based index found. It might be a bug of extensions you use: %r" msgstr "" -#: sphinx/transforms/__init__.py:300 +#: sphinx/transforms/__init__.py:301 #, python-format msgid "Footnote [%s] is not referenced." msgstr "" -#: sphinx/transforms/__init__.py:306 +#: sphinx/transforms/__init__.py:307 msgid "Footnote [#] is not referenced." msgstr "" -#: sphinx/transforms/i18n.py:294 sphinx/transforms/i18n.py:371 +#: sphinx/transforms/i18n.py:293 sphinx/transforms/i18n.py:363 msgid "" "inconsistent footnote references in translated message. original: {0}, " "translated: {1}" msgstr "" -#: sphinx/transforms/i18n.py:340 +#: sphinx/transforms/i18n.py:335 msgid "" "inconsistent references in translated message. original: {0}, translated:" " {1}" msgstr "" -#: sphinx/transforms/i18n.py:393 +#: sphinx/transforms/i18n.py:382 msgid "" "inconsistent citation references in translated message. original: {0}, " "translated: {1}" msgstr "" -#: sphinx/transforms/i18n.py:413 +#: sphinx/transforms/i18n.py:402 msgid "" "inconsistent term references in translated message. original: {0}, " "translated: {1}" msgstr "" -#: sphinx/transforms/post_transforms/__init__.py:142 +#: sphinx/transforms/post_transforms/__init__.py:110 #, python-format msgid "more than one target found for 'any' cross-reference %r: could be %s" msgstr "" -#: sphinx/transforms/post_transforms/__init__.py:172 +#: sphinx/transforms/post_transforms/__init__.py:142 #, python-format msgid "%s:%s reference target not found: %%(target)s" msgstr "" -#: sphinx/transforms/post_transforms/__init__.py:175 +#: sphinx/transforms/post_transforms/__init__.py:145 #, python-format msgid "%r reference target not found: %%(target)s" msgstr "" -#: sphinx/transforms/post_transforms/images.py:94 +#: sphinx/transforms/post_transforms/images.py:92 #, python-format msgid "Could not fetch remote image: %s [%d]" msgstr "" -#: sphinx/transforms/post_transforms/images.py:122 +#: sphinx/transforms/post_transforms/images.py:120 #, python-format msgid "Could not fetch remote image: %s [%s]" msgstr "" -#: sphinx/transforms/post_transforms/images.py:143 +#: sphinx/transforms/post_transforms/images.py:140 #, python-format msgid "Unknown image format: %s..." msgstr "" -#: sphinx/util/docutils.py:281 +#: sphinx/util/__init__.py:415 +#, python-format +msgid "undecodable source characters, replacing with \"?\": %r" +msgstr "" + +#: sphinx/util/__init__.py:695 +msgid "skipped" +msgstr "" + +#: sphinx/util/__init__.py:700 +msgid "failed" +msgstr "" + +#: sphinx/util/docutils.py:321 msgid "when adding directive classes, no additional arguments may be given" msgstr "" @@ -3346,139 +3248,125 @@ msgstr "" msgid "writing error: %s, %s" msgstr "" -#: sphinx/util/i18n.py:217 +#: sphinx/util/i18n.py:215 #, python-format msgid "" "Invalid date format. Quote the string by single quote if you want to " "output it directly: %s" msgstr "" -#: sphinx/util/nodes.py:360 +#: sphinx/util/nodes.py:428 #, python-format msgid "toctree contains ref to nonexisting file %r" msgstr "" -#: sphinx/util/nodes.py:433 +#: sphinx/util/nodes.py:501 #, python-format msgid "exception while evaluating only directive expression: %s" msgstr "" -#: sphinx/util/rst.py:47 +#: sphinx/util/pycompat.py:82 +#, python-format +msgid "" +"Support for evaluating Python 2 syntax is deprecated and will be removed " +"in Sphinx 4.0. Convert %s to Python 3 syntax." +msgstr "" + +#: sphinx/util/rst.py:49 #, python-format msgid "default role %s not found" msgstr "" -#: sphinx/writers/html.py:336 sphinx/writers/html5.py:304 +#: sphinx/writers/html.py:345 sphinx/writers/html5.py:313 #, python-format msgid "numfig_format is not defined for %s" msgstr "" -#: sphinx/writers/html.py:346 sphinx/writers/html5.py:314 +#: sphinx/writers/html.py:355 sphinx/writers/html5.py:323 #, python-format msgid "Any IDs not assigned for %s node" msgstr "" -#: sphinx/writers/html.py:412 sphinx/writers/html5.py:358 +#: sphinx/writers/html.py:463 sphinx/writers/html5.py:409 msgid "Permalink to this table" msgstr "" -#: sphinx/writers/html.py:459 sphinx/writers/html5.py:405 +#: sphinx/writers/html.py:510 sphinx/writers/html5.py:456 msgid "Permalink to this code" msgstr "" -#: sphinx/writers/html.py:463 sphinx/writers/html5.py:409 +#: sphinx/writers/html.py:514 sphinx/writers/html5.py:460 msgid "Permalink to this image" msgstr "" -#: sphinx/writers/html.py:465 sphinx/writers/html5.py:411 +#: sphinx/writers/html.py:516 sphinx/writers/html5.py:462 msgid "Permalink to this toctree" msgstr "" -#: sphinx/writers/html.py:612 sphinx/writers/html5.py:558 +#: sphinx/writers/html.py:674 sphinx/writers/html5.py:608 msgid "Could not obtain image size. :scale: option is ignored." msgstr "" -#: sphinx/writers/latex.py:508 -msgid "Release" -msgstr "" - -#: sphinx/writers/latex.py:531 +#: sphinx/writers/latex.py:510 #, python-format msgid "unknown %r toplevel_sectioning for class %r" msgstr "" -#: sphinx/writers/latex.py:582 -#, python-format -msgid "no Babel option known for language %r" -msgstr "" - -#: sphinx/writers/latex.py:633 +#: sphinx/writers/latex.py:603 msgid "too large :maxdepth:, ignored." msgstr "" -#: sphinx/writers/latex.py:667 -msgid "continues on next page" -msgstr "" - -#: sphinx/writers/latex.py:670 -msgid "Non-alphabetical" -msgstr "" - -#: sphinx/writers/latex.py:676 -msgid "Numbers" -msgstr "" - -#: sphinx/writers/latex.py:680 -msgid "page" -msgstr "" - -#: sphinx/writers/latex.py:1013 +#: sphinx/writers/latex.py:893 msgid "document title is not a single Text node" msgstr "" -#: sphinx/writers/latex.py:1046 sphinx/writers/texinfo.py:651 +#: sphinx/writers/latex.py:926 sphinx/writers/texinfo.py:658 msgid "encountered title node not in section, topic, table, admonition or sidebar" msgstr "" -#: sphinx/writers/latex.py:1223 sphinx/writers/manpage.py:275 -#: sphinx/writers/texinfo.py:669 +#: sphinx/writers/latex.py:1102 sphinx/writers/manpage.py:277 +#: sphinx/writers/texinfo.py:675 msgid "Footnotes" msgstr "" -#: sphinx/writers/latex.py:1638 +#: sphinx/writers/latex.py:1150 +msgid "both tabularcolumns and :widths: option are given. :widths: is ignored." +msgstr "" + +#: sphinx/writers/latex.py:1521 #, python-format msgid "dimension unit %s is invalid. Ignored." msgstr "" -#: sphinx/writers/latex.py:1927 +#: sphinx/writers/latex.py:1843 #, python-format msgid "unknown index entry type %s found" msgstr "" -#: sphinx/writers/latex.py:2606 +#: sphinx/writers/latex.py:2552 #, python-format msgid "Unknown configure key: latex_elements[%r] is ignored." msgstr "" -#: sphinx/writers/manpage.py:331 sphinx/writers/text.py:709 +#: sphinx/writers/manpage.py:333 sphinx/writers/text.py:887 #, python-format msgid "[image: %s]" msgstr "" -#: sphinx/writers/manpage.py:332 sphinx/writers/text.py:710 +#: sphinx/writers/manpage.py:334 sphinx/writers/text.py:888 msgid "[image]" msgstr "" -#: sphinx/writers/texinfo.py:1321 +#: sphinx/writers/texinfo.py:1330 msgid "caption not inside a figure." msgstr "" -#: sphinx/writers/texinfo.py:1412 +#: sphinx/writers/texinfo.py:1421 #, python-format msgid "unimplemented node type: %r" msgstr "" -#: sphinx/writers/texinfo.py:1417 +#: sphinx/writers/texinfo.py:1426 #, python-format msgid "unknown node type: %r" msgstr "" From 0b2210c99afee75acd5dd9dfaa3bbd6c9ecabbfe Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 18 Feb 2019 01:56:26 +0900 Subject: [PATCH 84/86] Bump version --- CHANGES | 21 +++++++++++++++++++++ sphinx/__init__.py | 6 +++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 36a08b8d0..2781cddf8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,24 @@ +Release 2.0.0 beta2 (in development) +==================================== + +Dependencies +------------ + +Incompatible changes +-------------------- + +Deprecated +---------- + +Features added +-------------- + +Bugs fixed +---------- + +Testing +-------- + Release 2.0.0 beta1 (in development) ==================================== diff --git a/sphinx/__init__.py b/sphinx/__init__.py index acc5dfef2..6c15f920f 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -32,8 +32,8 @@ if 'PYTHONWARNINGS' not in os.environ: warnings.filterwarnings('ignore', "'U' mode is deprecated", DeprecationWarning, module='docutils.io') -__version__ = '2.0.0b1' -__released__ = '2.0.0b1' # used when Sphinx builds its own docs +__version__ = '2.0.0+' +__released__ = '2.0.0' # used when Sphinx builds its own docs #: Version info for better programmatic use. #: @@ -43,7 +43,7 @@ __released__ = '2.0.0b1' # used when Sphinx builds its own docs #: #: .. versionadded:: 1.2 #: Before version 1.2, check the string ``sphinx.__version__``. -version_info = (2, 0, 0, 'beta', 1) +version_info = (2, 0, 0, 'beta', 2) package_dir = path.abspath(path.dirname(__file__)) From 12c614911ac1ea94e88ec781fd36281ffd026f0a Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 18 Feb 2019 01:57:55 +0900 Subject: [PATCH 85/86] Bump version --- CHANGES | 21 +++++++++++++++++++++ sphinx/__init__.py | 6 +++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 2781cddf8..28e99a44d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,24 @@ +Release 2.1.0 (in development) +============================== + +Dependencies +------------ + +Incompatible changes +-------------------- + +Deprecated +---------- + +Features added +-------------- + +Bugs fixed +---------- + +Testing +-------- + Release 2.0.0 beta2 (in development) ==================================== diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 6c15f920f..0a6f26584 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -32,8 +32,8 @@ if 'PYTHONWARNINGS' not in os.environ: warnings.filterwarnings('ignore', "'U' mode is deprecated", DeprecationWarning, module='docutils.io') -__version__ = '2.0.0+' -__released__ = '2.0.0' # used when Sphinx builds its own docs +__version__ = '2.1.0+' +__released__ = '2.1.0' # used when Sphinx builds its own docs #: Version info for better programmatic use. #: @@ -43,7 +43,7 @@ __released__ = '2.0.0' # used when Sphinx builds its own docs #: #: .. versionadded:: 1.2 #: Before version 1.2, check the string ``sphinx.__version__``. -version_info = (2, 0, 0, 'beta', 2) +version_info = (2, 1, 0, 'beta', 0) package_dir = path.abspath(path.dirname(__file__)) From 77cd1de79fb2999c060e1c3fe687ee1011647127 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 22 Feb 2019 13:15:42 +0900 Subject: [PATCH 86/86] Update warning messages for script_files --- sphinx/builders/html.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index bb11bfb4a..2c5ebcd4d 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -109,24 +109,24 @@ class JSContainer(list): """The container for JavaScript scripts.""" def insert(self, index, obj): # type: (int, str) -> None - warnings.warn('builder.script_files is deprecated. ' - 'Please use app.add_js_file() instead.', - RemovedInSphinx30Warning, stacklevel=2) + warnings.warn('To modify script_files in the theme is deprecated. ' + 'Please insert a