From 72e70b1b361a649f1e81ed4ed29cbb8a31024563 Mon Sep 17 00:00:00 2001
From: "Mr. Senko"
Date: Thu, 21 Jan 2016 11:13:29 +0200
Subject: [PATCH 01/95] Add tests for sphinx.ext.inheritance_diagram
---
AUTHORS | 1 +
CHANGES | 1 +
.../roots/test-inheritance/basic_diagram.rst | 5 ++
tests/roots/test-inheritance/conf.py | 6 ++
tests/roots/test-inheritance/contents.rst | 4 ++
.../test-inheritance/diagram_w_parts.rst | 7 ++
.../roots/test-inheritance/dummy/__init__.py | 0
tests/roots/test-inheritance/dummy/test.py | 30 +++++++++
tests/test_ext_inheritance.py | 67 +++++++++++++++++++
9 files changed, 121 insertions(+)
create mode 100644 tests/roots/test-inheritance/basic_diagram.rst
create mode 100644 tests/roots/test-inheritance/conf.py
create mode 100644 tests/roots/test-inheritance/contents.rst
create mode 100644 tests/roots/test-inheritance/diagram_w_parts.rst
create mode 100644 tests/roots/test-inheritance/dummy/__init__.py
create mode 100644 tests/roots/test-inheritance/dummy/test.py
create mode 100644 tests/test_ext_inheritance.py
diff --git a/AUTHORS b/AUTHORS
index 580feeb32..bafe242e8 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -18,6 +18,7 @@ Other co-maintainers:
Other contributors, listed alphabetically, are:
* Alastair Houghton -- Apple Help builder
+* Alexander Todorov -- inheritance_diagram tests and improvements
* Andi Albrecht -- agogo theme
* Jakob Lykke Andersen -- Rewritten C++ domain
* Henrique Bastos -- SVG support for graphviz extension
diff --git a/CHANGES b/CHANGES
index 85b660775..30d6a5aae 100644
--- a/CHANGES
+++ b/CHANGES
@@ -52,6 +52,7 @@ Bugs fixed
Testing
--------
+* Add tests for the ``sphinx.ext.inheritance_diagram`` extension.
Release 1.6.3 (in development)
==============================
diff --git a/tests/roots/test-inheritance/basic_diagram.rst b/tests/roots/test-inheritance/basic_diagram.rst
new file mode 100644
index 000000000..4c3838e65
--- /dev/null
+++ b/tests/roots/test-inheritance/basic_diagram.rst
@@ -0,0 +1,5 @@
+Basic Diagram
+==============
+
+.. inheritance-diagram::
+ dummy.test
diff --git a/tests/roots/test-inheritance/conf.py b/tests/roots/test-inheritance/conf.py
new file mode 100644
index 000000000..f1ddb4ad6
--- /dev/null
+++ b/tests/roots/test-inheritance/conf.py
@@ -0,0 +1,6 @@
+import sys, os
+
+sys.path.insert(0, os.path.abspath('.'))
+
+extensions = ['sphinx.ext.inheritance_diagram']
+source_suffix = '.rst'
diff --git a/tests/roots/test-inheritance/contents.rst b/tests/roots/test-inheritance/contents.rst
new file mode 100644
index 000000000..db4fbacb8
--- /dev/null
+++ b/tests/roots/test-inheritance/contents.rst
@@ -0,0 +1,4 @@
+.. toctree::
+ :glob:
+
+ *
diff --git a/tests/roots/test-inheritance/diagram_w_parts.rst b/tests/roots/test-inheritance/diagram_w_parts.rst
new file mode 100644
index 000000000..65a831802
--- /dev/null
+++ b/tests/roots/test-inheritance/diagram_w_parts.rst
@@ -0,0 +1,7 @@
+Diagram using the parts option
+==============================
+
+.. inheritance-diagram::
+ dummy.test
+ :parts: 1
+
diff --git a/tests/roots/test-inheritance/dummy/__init__.py b/tests/roots/test-inheritance/dummy/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/roots/test-inheritance/dummy/test.py b/tests/roots/test-inheritance/dummy/test.py
new file mode 100644
index 000000000..cafa07886
--- /dev/null
+++ b/tests/roots/test-inheritance/dummy/test.py
@@ -0,0 +1,30 @@
+"""
+
+ Test with a class diagram like this::
+
+ A
+ / \
+ B C
+ / \ / \
+ E D F
+
+"""
+
+class A(object):
+ pass
+
+class B(A):
+ pass
+
+class C(A):
+ pass
+
+class D(B, C):
+ pass
+
+class E(B):
+ pass
+
+class F(C):
+ pass
+
diff --git a/tests/test_ext_inheritance.py b/tests/test_ext_inheritance.py
new file mode 100644
index 000000000..ce4b50a56
--- /dev/null
+++ b/tests/test_ext_inheritance.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+"""
+ test_inheritance
+ ~~~~~~~~~~~~~~~~
+
+ Tests for :mod:`sphinx.ext.inheritance_diagram` module.
+
+ :copyright: Copyright 2015 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import os
+import pytest
+from sphinx.ext.inheritance_diagram import InheritanceDiagram
+
+@pytest.mark.sphinx(buildername="html", testroot="inheritance")
+@pytest.mark.usefixtures('if_graphviz_found')
+def test_inheritance_diagram(app, status, warning):
+ # monkey-patch InheritaceDiagram.run() so we can get access to its
+ # results.
+ orig_run = InheritanceDiagram.run
+ graphs = {}
+
+ def new_run(self):
+ result = orig_run(self)
+ node = result[0]
+ source = os.path.basename(node.document.current_source).replace(".rst", "")
+ graphs[source] = node['graph']
+ return result
+
+ InheritanceDiagram.run = new_run
+
+ try:
+ app.builder.build_all()
+ finally:
+ InheritanceDiagram.run = orig_run
+
+ assert app.statuscode == 0
+
+ html_warnings = warning.getvalue()
+ assert html_warnings == ""
+
+ # note: it is better to split these asserts into separate test functions
+ # but I can't figure out how to build only a specific .rst file
+
+ # basic inheritance diagram showing all classes
+ for cls in graphs['basic_diagram'].class_info:
+ # use in b/c traversing order is different sometimes
+ assert cls in [
+ ('dummy.test.A', 'dummy.test.A', [], None),
+ ('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
+ ('dummy.test.C', 'dummy.test.C', ['dummy.test.A'], None),
+ ('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
+ ('dummy.test.D', 'dummy.test.D', ['dummy.test.B', 'dummy.test.C'], None),
+ ('dummy.test.B', 'dummy.test.B', ['dummy.test.A'], None)
+ ]
+
+ # inheritance diagram using :parts: 1 option
+ for cls in graphs['diagram_w_parts'].class_info:
+ assert cls in [
+ ('A', 'dummy.test.A', [], None),
+ ('F', 'dummy.test.F', ['C'], None),
+ ('C', 'dummy.test.C', ['A'], None),
+ ('E', 'dummy.test.E', ['B'], None),
+ ('D', 'dummy.test.D', ['B', 'C'], None),
+ ('B', 'dummy.test.B', ['A'], None)
+ ]
From 9486f0d8f78c5683ed4df2015fd42b24ab147ab8 Mon Sep 17 00:00:00 2001
From: "Mr. Senko"
Date: Thu, 21 Jan 2016 11:11:06 +0200
Subject: [PATCH 02/95] Add top-classes option to
sphinx.ext.inheritance_diagram
This will limit the scope of inheritance traversal
---
CHANGES | 4 ++
doc/ext/inheritance.rst | 52 ++++++++++++++
sphinx/ext/inheritance_diagram.py | 26 +++++--
.../diagram_module_w_2_top_classes.rst | 6 ++
.../diagram_w_1_top_class.rst | 7 ++
.../diagram_w_2_top_classes.rst | 9 +++
tests/test_ext_inheritance.py | 68 ++++++++++++++++++-
7 files changed, 165 insertions(+), 7 deletions(-)
create mode 100644 tests/roots/test-inheritance/diagram_module_w_2_top_classes.rst
create mode 100644 tests/roots/test-inheritance/diagram_w_1_top_class.rst
create mode 100644 tests/roots/test-inheritance/diagram_w_2_top_classes.rst
diff --git a/CHANGES b/CHANGES
index 30d6a5aae..67db95381 100644
--- a/CHANGES
+++ b/CHANGES
@@ -17,6 +17,8 @@ Features added
* C++, add a ``cpp:expr`` role for inserting inline C++ expressions or types.
* #3638: Allow to change a label of reference to equation using
``math_eqref_format``
+* Add ``top-classes`` option for the ``sphinx.ext.inheritance_diagram``
+ extension to limit the scope of inheritance graphs.
Features removed
----------------
@@ -52,8 +54,10 @@ Bugs fixed
Testing
--------
+
* Add tests for the ``sphinx.ext.inheritance_diagram`` extension.
+
Release 1.6.3 (in development)
==============================
diff --git a/doc/ext/inheritance.rst b/doc/ext/inheritance.rst
index bd287aa49..4a8a3c0f1 100644
--- a/doc/ext/inheritance.rst
+++ b/doc/ext/inheritance.rst
@@ -42,6 +42,58 @@ It adds this directive:
.. versionchanged:: 1.5
Added ``caption`` option
+ It also supports a ``top-classes`` option which requires one or more class
+ names separated by comma. If specified inheritance traversal will stop at the
+ specified class names. Given the following Python module::
+
+ """
+ A
+ / \
+ B C
+ / \ / \
+ E D F
+ """
+
+ class A(object):
+ pass
+
+ class B(A):
+ pass
+
+ class C(A):
+ pass
+
+ class D(B, C):
+ pass
+
+ class E(B):
+ pass
+
+ class F(C):
+ pass
+
+ If you have specified a module in the inheritance diagram like this::
+
+ .. inheritance-diagram::
+ dummy.test
+ :top-classes: dummy.test.B, dummy.test.C
+
+ any base classes which are ancestors to ``top-classes`` and are also defined
+ in the same module will be rendered as stand alone nodes. In this example
+ class A will be rendered as stand alone node in the graph. This is a known
+ issue due to how this extension works internally.
+
+ If you don't want class A (or any other ancestors) to be visible then specify
+ only the classes you would like to generate the diagram for like this::
+
+ .. inheritance-diagram::
+ dummy.test.D
+ dummy.test.E
+ dummy.test.F
+ :top-classes: dummy.test.B, dummy.test.C
+
+ .. versionchanged:: 1.7
+ Added ``top-classes`` option to limit the scope of inheritance graphs.
New config values are:
diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py
index f5b0228a5..7ba972f9f 100644
--- a/sphinx/ext/inheritance_diagram.py
+++ b/sphinx/ext/inheritance_diagram.py
@@ -133,8 +133,8 @@ class InheritanceGraph(object):
graphviz dot graph from them.
"""
def __init__(self, class_names, currmodule, show_builtins=False,
- private_bases=False, parts=0):
- # type: (unicode, str, bool, bool, int) -> None
+ private_bases=False, parts=0, top_classes=[]):
+ # type: (unicode, str, bool, bool, int, List[Any]) -> None
"""*class_names* is a list of child classes to show bases from.
If *show_builtins* is True, then Python builtins will be shown
@@ -143,7 +143,7 @@ class InheritanceGraph(object):
self.class_names = class_names
classes = self._import_classes(class_names, currmodule)
self.class_info = self._class_info(classes, show_builtins,
- private_bases, parts)
+ private_bases, parts, top_classes)
if not self.class_info:
raise InheritanceException('No classes found for '
'inheritance diagram')
@@ -156,13 +156,16 @@ class InheritanceGraph(object):
classes.extend(import_classes(name, currmodule))
return classes
- def _class_info(self, classes, show_builtins, private_bases, parts):
- # type: (List[Any], bool, bool, int) -> List[Tuple[unicode, unicode, List[unicode], unicode]] # NOQA
+ def _class_info(self, classes, show_builtins, private_bases, parts, top_classes):
+ # type: (List[Any], bool, bool, int, List[Any]) -> List[Tuple[unicode, unicode, List[unicode], unicode]] # NOQA
"""Return name and bases for all classes that are ancestors of
*classes*.
*parts* gives the number of dotted name parts that is removed from the
displayed node names.
+
+ *top_classes* gives the name(s) of the top most ancestor class to traverse
+ to. Multiple names can be specified separated by comma.
"""
all_classes = {}
py_builtins = vars(builtins).values()
@@ -192,6 +195,10 @@ class InheritanceGraph(object):
baselist = [] # type: List[unicode]
all_classes[cls] = (nodename, fullname, baselist, tooltip)
+
+ if fullname in top_classes:
+ return
+
for base in cls.__bases__:
if not show_builtins and base in py_builtins:
continue
@@ -321,6 +328,7 @@ class InheritanceDiagram(Directive):
'parts': directives.nonnegative_int,
'private-bases': directives.flag,
'caption': directives.unchanged,
+ 'top-classes': directives.unchanged_required,
}
def run(self):
@@ -333,13 +341,19 @@ class InheritanceDiagram(Directive):
# Store the original content for use as a hash
node['parts'] = self.options.get('parts', 0)
node['content'] = ', '.join(class_names)
+ node['top-classes'] = []
+ for cls in self.options.get('top-classes', '').split(','):
+ cls = cls.strip()
+ if cls:
+ node['top-classes'].append(cls)
# Create a graph starting with the list of classes
try:
graph = InheritanceGraph(
class_names, env.ref_context.get('py:module'),
parts=node['parts'],
- private_bases='private-bases' in self.options)
+ private_bases='private-bases' in self.options,
+ top_classes=node['top-classes'])
except InheritanceException as err:
return [node.document.reporter.warning(err.args[0],
line=self.lineno)]
diff --git a/tests/roots/test-inheritance/diagram_module_w_2_top_classes.rst b/tests/roots/test-inheritance/diagram_module_w_2_top_classes.rst
new file mode 100644
index 000000000..cc4365e9c
--- /dev/null
+++ b/tests/roots/test-inheritance/diagram_module_w_2_top_classes.rst
@@ -0,0 +1,6 @@
+Diagram using module with 2 top classes
+=======================================
+
+.. inheritance-diagram::
+ dummy.test
+ :top-classes: dummy.test.B, dummy.test.C
diff --git a/tests/roots/test-inheritance/diagram_w_1_top_class.rst b/tests/roots/test-inheritance/diagram_w_1_top_class.rst
new file mode 100644
index 000000000..97da82557
--- /dev/null
+++ b/tests/roots/test-inheritance/diagram_w_1_top_class.rst
@@ -0,0 +1,7 @@
+Diagram using 1 top class
+=========================
+
+.. inheritance-diagram::
+ dummy.test
+ :top-classes: dummy.test.B
+
diff --git a/tests/roots/test-inheritance/diagram_w_2_top_classes.rst b/tests/roots/test-inheritance/diagram_w_2_top_classes.rst
new file mode 100644
index 000000000..8a6ae5865
--- /dev/null
+++ b/tests/roots/test-inheritance/diagram_w_2_top_classes.rst
@@ -0,0 +1,9 @@
+Diagram using 2 top classes
+===========================
+
+.. inheritance-diagram::
+ dummy.test.F
+ dummy.test.D
+ dummy.test.E
+ :top-classes: dummy.test.B, dummy.test.C
+
diff --git a/tests/test_ext_inheritance.py b/tests/test_ext_inheritance.py
index ce4b50a56..fcf313a30 100644
--- a/tests/test_ext_inheritance.py
+++ b/tests/test_ext_inheritance.py
@@ -13,6 +13,7 @@ import os
import pytest
from sphinx.ext.inheritance_diagram import InheritanceDiagram
+
@pytest.mark.sphinx(buildername="html", testroot="inheritance")
@pytest.mark.usefixtures('if_graphviz_found')
def test_inheritance_diagram(app, status, warning):
@@ -51,7 +52,8 @@ def test_inheritance_diagram(app, status, warning):
('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
('dummy.test.C', 'dummy.test.C', ['dummy.test.A'], None),
('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
- ('dummy.test.D', 'dummy.test.D', ['dummy.test.B', 'dummy.test.C'], None),
+ ('dummy.test.D', 'dummy.test.D',
+ ['dummy.test.B', 'dummy.test.C'], None),
('dummy.test.B', 'dummy.test.B', ['dummy.test.A'], None)
]
@@ -65,3 +67,67 @@ def test_inheritance_diagram(app, status, warning):
('D', 'dummy.test.D', ['B', 'C'], None),
('B', 'dummy.test.B', ['A'], None)
]
+
+ # inheritance diagram with 1 top class
+ # :top-classes: dummy.test.B
+ # rendering should be
+ # A
+ # \
+ # B C
+ # / \ / \
+ # E D F
+ #
+ for cls in graphs['diagram_w_1_top_class'].class_info:
+ assert cls in [
+ ('dummy.test.A', 'dummy.test.A', [], None),
+ ('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
+ ('dummy.test.C', 'dummy.test.C', ['dummy.test.A'], None),
+ ('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
+ ('dummy.test.D', 'dummy.test.D',
+ ['dummy.test.B', 'dummy.test.C'], None),
+ ('dummy.test.B', 'dummy.test.B', [], None)
+ ]
+
+
+ # inheritance diagram with 2 top classes
+ # :top-classes: dummy.test.B, dummy.test.C
+ # Note: we're specifying separate classes, not the entire module here
+ # rendering should be
+ #
+ # B C
+ # / \ / \
+ # E D F
+ #
+ for cls in graphs['diagram_w_2_top_classes'].class_info:
+ assert cls in [
+ ('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
+ ('dummy.test.C', 'dummy.test.C', [], None),
+ ('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
+ ('dummy.test.D', 'dummy.test.D',
+ ['dummy.test.B', 'dummy.test.C'], None),
+ ('dummy.test.B', 'dummy.test.B', [], None)
+ ]
+
+ # inheritance diagram with 2 top classes and specifiying the entire module
+ # rendering should be
+ #
+ # A
+ # B C
+ # / \ / \
+ # E D F
+ #
+ # Note: dummy.test.A is included in the graph before its descendants are even processed
+ # b/c we've specified to load the entire module. The way InheritanceGraph works it is very
+ # hard to exclude parent classes once after they have been included in the graph.
+ # If you'd like to not show class A in the graph don't specify the entire module.
+ # this is a known issue.
+ for cls in graphs['diagram_module_w_2_top_classes'].class_info:
+ assert cls in [
+ ('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
+ ('dummy.test.C', 'dummy.test.C', [], None),
+ ('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
+ ('dummy.test.D', 'dummy.test.D',
+ ['dummy.test.B', 'dummy.test.C'], None),
+ ('dummy.test.B', 'dummy.test.B', [], None),
+ ('dummy.test.A', 'dummy.test.A', [], None),
+ ]
From 6b15c9c1c738c587a73b4144e8fe2b0d3b8aa4b4 Mon Sep 17 00:00:00 2001
From: Matthew Woodcraft
Date: Sun, 5 Nov 2017 22:47:57 +0000
Subject: [PATCH 03/95] #3998: Add optional section numbering in plain text
output
Controlled by new config values: text_add_secnumbers and
text_secnumber_suffix.
---
AUTHORS | 1 +
CHANGES | 2 +
doc/config.rst | 14 ++++++
sphinx/builders/text.py | 6 ++-
sphinx/writers/text.py | 15 +++++++
tests/roots/test-build-text/contents.txt | 3 ++
tests/roots/test-build-text/doc1.txt | 2 +
tests/roots/test-build-text/doc2.txt | 9 ++++
tests/test_build_text.py | 54 ++++++++++++++++++++++++
9 files changed, 105 insertions(+), 1 deletion(-)
create mode 100644 tests/roots/test-build-text/doc1.txt
create mode 100644 tests/roots/test-build-text/doc2.txt
diff --git a/AUTHORS b/AUTHORS
index f4ce16164..5a7fb0842 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -67,6 +67,7 @@ Other contributors, listed alphabetically, are:
* Barry Warsaw -- setup command improvements
* Sebastian Wiesner -- image handling, distutils support
* Michael Wilson -- Intersphinx HTTP basic auth support
+* Matthew Woodcraft -- text output improvements
* Joel Wurtz -- cellspanning support in LaTeX
* Hong Xu -- svg support in imgmath extension and various bug fixes
* Stephen Finucane -- setup command improvements and documentation
diff --git a/CHANGES b/CHANGES
index 79c562606..98705d233 100644
--- a/CHANGES
+++ b/CHANGES
@@ -43,6 +43,8 @@ Features added
* #4168: improve zh search with jieba
* HTML themes can set up default sidebars through ``theme.conf``
* #3160: html: Use ```` to represent ``:kbd:`` role
+* #3998: text: Add new config values :confval:`text_add_secnumbers` and
+ :confval:`text_secnumber_suffix`
Features removed
diff --git a/doc/config.rst b/doc/config.rst
index 415a2298a..a5554d7cc 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -1959,6 +1959,20 @@ These options influence text output.
.. versionadded:: 1.1
+.. confval:: text_add_secnumbers
+
+ A boolean that decides whether section numbers are included in text output.
+ Default is ``False``.
+
+ .. versionadded:: 1.7
+
+.. confval:: text_secnumber_suffix
+
+ Suffix for section numbers in text output. Default: ``". "``. Set to ``" "``
+ to suppress the final dot on section numbers.
+
+ .. versionadded:: 1.7
+
.. _man-options:
diff --git a/sphinx/builders/text.py b/sphinx/builders/text.py
index 29ceaa855..5c6aa94f2 100644
--- a/sphinx/builders/text.py
+++ b/sphinx/builders/text.py
@@ -39,7 +39,8 @@ class TextBuilder(Builder):
def init(self):
# type: () -> None
- pass
+ # section numbers for headings in the currently visited document
+ self.secnumbers = {} # type: Dict[unicode, Tuple[int, ...]]
def get_outdated_docs(self):
# type: () -> Iterator[unicode]
@@ -72,6 +73,7 @@ class TextBuilder(Builder):
def write_doc(self, docname, doctree):
# type: (unicode, nodes.Node) -> None
self.current_docname = docname
+ self.secnumbers = self.env.toc_secnumbers.get(docname, {})
destination = StringOutput(encoding='utf-8')
self.writer.write(doctree, destination)
outfilename = path.join(self.outdir, os_path(docname) + self.out_suffix)
@@ -93,6 +95,8 @@ def setup(app):
app.add_config_value('text_sectionchars', '*=-~"+`', 'env')
app.add_config_value('text_newlines', 'unix', 'env')
+ app.add_config_value('text_add_secnumbers', False, 'env')
+ app.add_config_value('text_secnumber_suffix', '. ', 'env')
return {
'version': 'builtin',
diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py
index d2b2f9045..ce60164b2 100644
--- a/sphinx/writers/text.py
+++ b/sphinx/writers/text.py
@@ -183,6 +183,8 @@ class TextTranslator(nodes.NodeVisitor):
else:
self.nl = '\n'
self.sectionchars = builder.config.text_sectionchars
+ self.add_secnumbers = builder.config.text_add_secnumbers
+ self.secnumber_suffix = builder.config.text_secnumber_suffix
self.states = [[]] # type: List[List[Tuple[int, Union[unicode, List[unicode]]]]]
self.stateindent = [0]
self.list_counter = [] # type: List[int]
@@ -307,6 +309,17 @@ class TextTranslator(nodes.NodeVisitor):
raise nodes.SkipNode
self.new_state(0)
+ def get_section_number_string(self, node):
+ # type: (nodes.Node) -> unicode
+ if isinstance(node.parent, nodes.section):
+ anchorname = '#' + node.parent['ids'][0]
+ numbers = self.builder.secnumbers.get(anchorname)
+ if numbers is None:
+ numbers = self.builder.secnumbers.get('')
+ if numbers is not None:
+ return '.'.join(map(str, numbers)) + self.secnumber_suffix
+ return ''
+
def depart_title(self, node):
# type: (nodes.Node) -> None
if isinstance(node.parent, nodes.section):
@@ -315,6 +328,8 @@ class TextTranslator(nodes.NodeVisitor):
char = '^'
text = None # type: unicode
text = ''.join(x[1] for x in self.states.pop() if x[0] == -1) # type: ignore
+ if self.add_secnumbers:
+ text = self.get_section_number_string(node) + text
self.stateindent.pop()
title = ['', text, '%s' % (char * column_width(text)), ''] # type: List[unicode]
if len(self.states) == 2 and len(self.states[-1]) == 0:
diff --git a/tests/roots/test-build-text/contents.txt b/tests/roots/test-build-text/contents.txt
index 420d14280..ca9f8dc6c 100644
--- a/tests/roots/test-build-text/contents.txt
+++ b/tests/roots/test-build-text/contents.txt
@@ -1,5 +1,8 @@
.. toctree::
+ :numbered:
+ doc1
+ doc2
maxwidth
lineblock
nonascii_title
diff --git a/tests/roots/test-build-text/doc1.txt b/tests/roots/test-build-text/doc1.txt
new file mode 100644
index 000000000..da1909aa8
--- /dev/null
+++ b/tests/roots/test-build-text/doc1.txt
@@ -0,0 +1,2 @@
+Section A
+=========
diff --git a/tests/roots/test-build-text/doc2.txt b/tests/roots/test-build-text/doc2.txt
new file mode 100644
index 000000000..ebc88e963
--- /dev/null
+++ b/tests/roots/test-build-text/doc2.txt
@@ -0,0 +1,9 @@
+Section B
+=========
+
+Sub Ba
+------
+
+Sub Bb
+------
+
diff --git a/tests/test_build_text.py b/tests/test_build_text.py
index 81e354ecd..6e9aec4b6 100644
--- a/tests/test_build_text.py
+++ b/tests/test_build_text.py
@@ -110,3 +110,57 @@ def test_list_items_in_admonition(app, status, warning):
assert lines[2] == " * item 1"
assert lines[3] == ""
assert lines[4] == " * item 2"
+
+
+@with_text_app()
+def test_secnums(app, status, warning):
+ app.builder.build_all()
+ result = (app.outdir / 'doc2.txt').text(encoding='utf8')
+ expect = (
+ "Section B\n"
+ "*********\n"
+ "\n"
+ "\n"
+ "Sub Ba\n"
+ "======\n"
+ "\n"
+ "\n"
+ "Sub Bb\n"
+ "======\n"
+ )
+ assert result == expect
+
+ app.config.text_add_secnumbers = True
+ app.builder.build_all()
+ result = (app.outdir / 'doc2.txt').text(encoding='utf8')
+ expect = (
+ "2. Section B\n"
+ "************\n"
+ "\n"
+ "\n"
+ "2.1. Sub Ba\n"
+ "===========\n"
+ "\n"
+ "\n"
+ "2.2. Sub Bb\n"
+ "===========\n"
+ )
+ assert result == expect
+
+ app.config.text_secnumber_suffix = " "
+ app.builder.build_all()
+ result = (app.outdir / 'doc2.txt').text(encoding='utf8')
+ expect = (
+ "2 Section B\n"
+ "***********\n"
+ "\n"
+ "\n"
+ "2.1 Sub Ba\n"
+ "==========\n"
+ "\n"
+ "\n"
+ "2.2 Sub Bb\n"
+ "==========\n"
+ )
+ assert result == expect
+
From 7fac34bb44282d26af0ba67fbda7544ccf53c33f Mon Sep 17 00:00:00 2001
From: Matthew Woodcraft
Date: Mon, 6 Nov 2017 20:54:17 +0000
Subject: [PATCH 04/95] Fix typing imports for mypy in sphinx.builders.text
---
sphinx/builders/text.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/builders/text.py b/sphinx/builders/text.py
index 5c6aa94f2..e553e89d0 100644
--- a/sphinx/builders/text.py
+++ b/sphinx/builders/text.py
@@ -21,7 +21,7 @@ from sphinx.writers.text import TextWriter, TextTranslator
if False:
# For type annotation
- from typing import Any, Dict, Iterator, Set # NOQA
+ from typing import Any, Dict, Iterator, Set, Tuple # NOQA
from docutils import nodes # NOQA
from sphinx.application import Sphinx # NOQA
From 604db47228689c512fd5f7eb75669b1a0f30e867 Mon Sep 17 00:00:00 2001
From: Matthew Woodcraft
Date: Tue, 7 Nov 2017 23:10:11 +0000
Subject: [PATCH 05/95] #3998: Toctree section numbering in plain text output
---
sphinx/writers/text.py | 5 +++-
tests/test_build_text.py | 51 +++++++++++++++++++++++++++++++++++-----
2 files changed, 49 insertions(+), 7 deletions(-)
diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py
index ce60164b2..b54bde38b 100644
--- a/sphinx/writers/text.py
+++ b/sphinx/writers/text.py
@@ -1002,7 +1002,10 @@ class TextTranslator(nodes.NodeVisitor):
def visit_reference(self, node):
# type: (nodes.Node) -> None
- pass
+ if self.add_secnumbers:
+ numbers = node.get("secnumber")
+ if numbers is not None:
+ self.add_text('.'.join(map(str, numbers)) + self.secnumber_suffix)
def depart_reference(self, node):
# type: (nodes.Node) -> None
diff --git a/tests/test_build_text.py b/tests/test_build_text.py
index 6e9aec4b6..2651f35ce 100644
--- a/tests/test_build_text.py
+++ b/tests/test_build_text.py
@@ -115,7 +115,20 @@ def test_list_items_in_admonition(app, status, warning):
@with_text_app()
def test_secnums(app, status, warning):
app.builder.build_all()
- result = (app.outdir / 'doc2.txt').text(encoding='utf8')
+ contents = (app.outdir / 'contents.txt').text(encoding='utf8')
+ expect = (
+ "* Section A\n"
+ "\n"
+ "* Section B\n"
+ "\n"
+ " * Sub Ba\n"
+ "\n"
+ " * Sub Bb\n"
+ "\n"
+ "* 日本語\n"
+ )
+ assert contents == expect
+ doc2 = (app.outdir / 'doc2.txt').text(encoding='utf8')
expect = (
"Section B\n"
"*********\n"
@@ -128,11 +141,24 @@ def test_secnums(app, status, warning):
"Sub Bb\n"
"======\n"
)
- assert result == expect
+ assert doc2 == expect
app.config.text_add_secnumbers = True
app.builder.build_all()
- result = (app.outdir / 'doc2.txt').text(encoding='utf8')
+ contents = (app.outdir / 'contents.txt').text(encoding='utf8')
+ expect = (
+ "* 1. Section A\n"
+ "\n"
+ "* 2. Section B\n"
+ "\n"
+ " * 2.1. Sub Ba\n"
+ "\n"
+ " * 2.2. Sub Bb\n"
+ "\n"
+ "* 3. 日本語\n"
+ )
+ assert contents == expect
+ doc2 = (app.outdir / 'doc2.txt').text(encoding='utf8')
expect = (
"2. Section B\n"
"************\n"
@@ -145,11 +171,24 @@ def test_secnums(app, status, warning):
"2.2. Sub Bb\n"
"===========\n"
)
- assert result == expect
+ assert doc2 == expect
app.config.text_secnumber_suffix = " "
app.builder.build_all()
- result = (app.outdir / 'doc2.txt').text(encoding='utf8')
+ contents = (app.outdir / 'contents.txt').text(encoding='utf8')
+ expect = (
+ "* 1 Section A\n"
+ "\n"
+ "* 2 Section B\n"
+ "\n"
+ " * 2.1 Sub Ba\n"
+ "\n"
+ " * 2.2 Sub Bb\n"
+ "\n"
+ "* 3 日本語\n"
+ )
+ assert contents == expect
+ doc2 = (app.outdir / 'doc2.txt').text(encoding='utf8')
expect = (
"2 Section B\n"
"***********\n"
@@ -162,5 +201,5 @@ def test_secnums(app, status, warning):
"2.2 Sub Bb\n"
"==========\n"
)
- assert result == expect
+ assert doc2 == expect
From e99eb094295cecc17059c3ba210761d46c06ccd0 Mon Sep 17 00:00:00 2001
From: Matthew Woodcraft
Date: Tue, 7 Nov 2017 23:51:45 +0000
Subject: [PATCH 06/95] Fix Python 2 problems in test_build_text
---
tests/test_build_text.py | 60 ++++++++++++++++------------------------
1 file changed, 24 insertions(+), 36 deletions(-)
diff --git a/tests/test_build_text.py b/tests/test_build_text.py
index 2651f35ce..b18a08cbb 100644
--- a/tests/test_build_text.py
+++ b/tests/test_build_text.py
@@ -116,18 +116,14 @@ def test_list_items_in_admonition(app, status, warning):
def test_secnums(app, status, warning):
app.builder.build_all()
contents = (app.outdir / 'contents.txt').text(encoding='utf8')
- expect = (
- "* Section A\n"
- "\n"
- "* Section B\n"
- "\n"
- " * Sub Ba\n"
- "\n"
- " * Sub Bb\n"
- "\n"
- "* 日本語\n"
- )
- assert contents == expect
+ lines = contents.splitlines()
+ assert lines[0] == "* Section A"
+ assert lines[1] == ""
+ assert lines[2] == "* Section B"
+ assert lines[3] == ""
+ assert lines[4] == " * Sub Ba"
+ assert lines[5] == ""
+ assert lines[6] == " * Sub Bb"
doc2 = (app.outdir / 'doc2.txt').text(encoding='utf8')
expect = (
"Section B\n"
@@ -146,18 +142,14 @@ def test_secnums(app, status, warning):
app.config.text_add_secnumbers = True
app.builder.build_all()
contents = (app.outdir / 'contents.txt').text(encoding='utf8')
- expect = (
- "* 1. Section A\n"
- "\n"
- "* 2. Section B\n"
- "\n"
- " * 2.1. Sub Ba\n"
- "\n"
- " * 2.2. Sub Bb\n"
- "\n"
- "* 3. 日本語\n"
- )
- assert contents == expect
+ lines = contents.splitlines()
+ assert lines[0] == "* 1. Section A"
+ assert lines[1] == ""
+ assert lines[2] == "* 2. Section B"
+ assert lines[3] == ""
+ assert lines[4] == " * 2.1. Sub Ba"
+ assert lines[5] == ""
+ assert lines[6] == " * 2.2. Sub Bb"
doc2 = (app.outdir / 'doc2.txt').text(encoding='utf8')
expect = (
"2. Section B\n"
@@ -176,18 +168,14 @@ def test_secnums(app, status, warning):
app.config.text_secnumber_suffix = " "
app.builder.build_all()
contents = (app.outdir / 'contents.txt').text(encoding='utf8')
- expect = (
- "* 1 Section A\n"
- "\n"
- "* 2 Section B\n"
- "\n"
- " * 2.1 Sub Ba\n"
- "\n"
- " * 2.2 Sub Bb\n"
- "\n"
- "* 3 日本語\n"
- )
- assert contents == expect
+ lines = contents.splitlines()
+ assert lines[0] == "* 1 Section A"
+ assert lines[1] == ""
+ assert lines[2] == "* 2 Section B"
+ assert lines[3] == ""
+ assert lines[4] == " * 2.1 Sub Ba"
+ assert lines[5] == ""
+ assert lines[6] == " * 2.2 Sub Bb"
doc2 = (app.outdir / 'doc2.txt').text(encoding='utf8')
expect = (
"2 Section B\n"
From 3cef4b6e7cdbfccad05de0b9ad00b1c0bb30fc1f Mon Sep 17 00:00:00 2001
From: Aaron Carlisle
Date: Thu, 16 Nov 2017 14:17:28 -0500
Subject: [PATCH 07/95] Themes: Add language to javascript vars list
---
sphinx/themes/basic/layout.html | 1 +
1 file changed, 1 insertion(+)
diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html
index b337a977e..af91e42f2 100644
--- a/sphinx/themes/basic/layout.html
+++ b/sphinx/themes/basic/layout.html
@@ -91,6 +91,7 @@
var DOCUMENTATION_OPTIONS = {
URL_ROOT: '{{ url_root }}',
VERSION: '{{ release|e }}',
+ LANGUAGE: '{{ language }}',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}',
HAS_SOURCE: {{ has_source|lower }},
From 832914423e84067f3cf764bcb84e20ade00f02af Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sun, 17 Dec 2017 00:03:56 +0900
Subject: [PATCH 08/95] autodoc: refactor AutoDirective
---
sphinx/application.py | 3 +-
sphinx/ext/autodoc/__init__.py | 123 +--------------------
sphinx/ext/autodoc/directive.py | 172 +++++++++++++++++++++++++++++
sphinx/ext/autosummary/__init__.py | 2 +-
sphinx/util/docutils.py | 8 +-
5 files changed, 180 insertions(+), 128 deletions(-)
create mode 100644 sphinx/ext/autodoc/directive.py
diff --git a/sphinx/application.py b/sphinx/application.py
index 05d302c81..e49ac6174 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -659,8 +659,9 @@ class Sphinx(object):
# type: (Any) -> None
logger.debug('[app] adding autodocumenter: %r', cls)
from sphinx.ext import autodoc
+ from sphinx.ext.autodoc.directive import AutodocDirective
autodoc.add_documenter(cls)
- self.add_directive('auto' + cls.objtype, autodoc.AutoDirective)
+ self.add_directive('auto' + cls.objtype, AutodocDirective)
def add_autodoc_attrgetter(self, type, getter):
# type: (Any, Callable) -> None
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index ff161565c..c74ca43c9 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -18,8 +18,6 @@ import traceback
from six import PY2, iterkeys, iteritems, itervalues, text_type, class_types, string_types
-from docutils import nodes
-from docutils.utils import assemble_option_dict
from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
@@ -32,7 +30,6 @@ from sphinx.locale import _
from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.application import ExtensionError
from sphinx.util import logging
-from sphinx.util.nodes import nested_parse_with_titles
from sphinx.util.inspect import Signature, isdescriptor, safe_getmembers, \
safe_getattr, object_description, is_builtin_class_method, \
isenumclass, isenumattribute, getdoc
@@ -42,11 +39,11 @@ if False:
# For type annotation
from types import ModuleType # NOQA
from typing import Any, Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union # NOQA
- from docutils.utils import Reporter # NOQA
from sphinx.application import Sphinx # NOQA
logger = logging.getLogger(__name__)
+
# This type isn't exposed directly in any modules, but can be found
# here in most Python versions
MethodDescriptorType = type(type.__subclasses__)
@@ -63,42 +60,11 @@ py_ext_sig_re = re.compile(
''', re.VERBOSE)
-class DefDict(dict):
- """A dict that returns a default on nonexisting keys."""
- def __init__(self, default):
- # type: (Any) -> None
- dict.__init__(self)
- self.default = default
-
- def __getitem__(self, key):
- # type: (Any) -> Any
- try:
- return dict.__getitem__(self, key)
- except KeyError:
- return self.default
-
- def __bool__(self):
- # type: () -> bool
- # docutils check "if option_spec"
- return True
- __nonzero__ = __bool__ # for python2 compatibility
-
-
def identity(x):
# type: (Any) -> Any
return x
-class Options(dict):
- """A dict/attribute hybrid that returns None on nonexisting keys."""
- def __getattr__(self, name):
- # type: (unicode) -> Any
- try:
- return self[name.replace('_', '-')]
- except KeyError:
- return None
-
-
ALL = object()
INSTANCEATTR = object()
@@ -1525,93 +1491,6 @@ class AutoDirective(Directive):
# a registry of type -> getattr function
_special_attrgetters = {} # type: Dict[Type, Callable]
- # flags that can be given in autodoc_default_flags
- _default_flags = set([
- 'members', 'undoc-members', 'inherited-members', 'show-inheritance',
- 'private-members', 'special-members',
- ])
-
- # standard docutils directive settings
- has_content = True
- required_arguments = 1
- optional_arguments = 0
- final_argument_whitespace = True
- # allow any options to be passed; the options are parsed further
- # by the selected Documenter
- option_spec = DefDict(identity)
-
- def warn(self, msg):
- # type: (unicode) -> None
- self.warnings.append(self.reporter.warning(msg, line=self.lineno))
-
- def run(self):
- # type: () -> List[nodes.Node]
- self.filename_set = set() # type: Set[unicode]
- # a set of dependent filenames
- self.reporter = self.state.document.reporter
- self.env = self.state.document.settings.env
- self.warnings = [] # type: List[unicode]
- self.result = ViewList()
-
- try:
- source, lineno = self.reporter.get_source_and_line(self.lineno)
- except AttributeError:
- source = lineno = None
- logger.debug('[autodoc] %s:%s: input:\n%s',
- source, lineno, self.block_text)
-
- # find out what documenter to call
- objtype = self.name[4:]
- doc_class = self._registry[objtype]
- # add default flags
- for flag in self._default_flags:
- if flag not in doc_class.option_spec:
- continue
- negated = self.options.pop('no-' + flag, 'not given') is None
- if flag in self.env.config.autodoc_default_flags and \
- not negated:
- self.options[flag] = None
- # process the options with the selected documenter's option_spec
- try:
- self.genopt = Options(assemble_option_dict(
- self.options.items(), doc_class.option_spec))
- except (KeyError, ValueError, TypeError) as err:
- # an option is either unknown or has a wrong type
- msg = self.reporter.error('An option to %s is either unknown or '
- 'has an invalid value: %s' % (self.name, err),
- line=self.lineno)
- return [msg]
- # generate the output
- documenter = doc_class(self, self.arguments[0])
- documenter.generate(more_content=self.content)
- if not self.result:
- return self.warnings
-
- logger.debug('[autodoc] output:\n%s', '\n'.join(self.result))
-
- # record all filenames as dependencies -- this will at least
- # partially make automatic invalidation possible
- for fn in self.filename_set:
- self.state.document.settings.record_dependencies.add(fn)
-
- # use a custom reporter that correctly assigns lines to source
- # filename/description and lineno
- old_reporter = self.state.memo.reporter
- self.state.memo.reporter = AutodocReporter(self.result,
- self.state.memo.reporter)
-
- if documenter.titles_allowed:
- node = nodes.section()
- # necessary so that the child nodes get the right source/line set
- node.document = self.state.document
- nested_parse_with_titles(self.state, self.result, node)
- else:
- node = nodes.paragraph()
- node.document = self.state.document
- self.state.nested_parse(self.result, 0, node)
- self.state.memo.reporter = old_reporter
- return self.warnings + node.children
-
def add_documenter(cls):
# type: (Type[Documenter]) -> None
diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py
new file mode 100644
index 000000000..9be273982
--- /dev/null
+++ b/sphinx/ext/autodoc/directive.py
@@ -0,0 +1,172 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.ext.autodoc.directive
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from docutils import nodes
+from docutils.parsers.rst import Directive
+from docutils.statemachine import ViewList
+from docutils.utils import assemble_option_dict
+
+from sphinx.ext.autodoc import AutoDirective, AutodocReporter, identity
+from sphinx.util import logging
+from sphinx.util.nodes import nested_parse_with_titles
+
+if False:
+ # For type annotation
+ from typing import Any, Dict, List, Set, Type # NOQA
+ from docutils.statemachine import State, StateMachine, StringList # NOQA
+ from docutils.utils import Reporter # NOQA
+ from sphinx.config import Config # NOQA
+ from sphinx.environment import BuildEnvironment # NOQA
+ from sphinx.ext.autodoc import Documenter # NOQA
+
+logger = logging.getLogger(__name__)
+
+
+# common option names for autodoc directives
+AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members',
+ 'show-inheritance', 'private-members', 'special-members']
+
+
+class DefDict(dict):
+ """A dict that returns a default on nonexisting keys."""
+ def __init__(self, default):
+ # type: (Any) -> None
+ dict.__init__(self)
+ self.default = default
+
+ def __getitem__(self, key):
+ # type: (Any) -> Any
+ try:
+ return dict.__getitem__(self, key)
+ except KeyError:
+ return self.default
+
+ def __bool__(self):
+ # type: () -> bool
+ # docutils check "if option_spec"
+ return True
+
+ __nonzero__ = __bool__ # for python2 compatibility
+
+
+class Options(dict):
+ """A dict/attribute hybrid that returns None on nonexisting keys."""
+ def __getattr__(self, name):
+ # type: (unicode) -> Any
+ try:
+ return self[name.replace('_', '-')]
+ except KeyError:
+ return None
+
+
+class DocumenterBridge(object):
+ def __init__(self, env, reporter, options, lineno):
+ # type: (BuildEnvironment, Reporter, Options, int) -> None
+ self.env = env
+ self.reporter = reporter
+ self.genopt = options
+ self.lineno = lineno
+ self.filename_set = set() # type: Set[unicode]
+ self.warnings = [] # type: List[nodes.Node]
+ self.result = ViewList()
+
+ def warn(self, msg):
+ # type: (unicode) -> None
+ self.warnings.append(self.reporter.warning(msg, line=self.lineno))
+
+
+def process_documenter_options(documenter, config, options):
+ # type: (Type[Documenter], Config, Dict) -> Options
+ for name in AUTODOC_DEFAULT_OPTIONS:
+ if name not in documenter.option_spec:
+ continue
+ else:
+ negated = options.pop('no-' + name, True) is None
+ if name in config.autodoc_default_flags and not negated:
+ options[name] = None
+
+ return Options(assemble_option_dict(options.items(), documenter.option_spec))
+
+
+def parse_generated_content(state, content, documenter):
+ # type: (State, StringList, Documenter) -> List[nodes.Node]
+ try:
+ # use a custom reporter that correctly assigns lines to source
+ # filename/description and lineno
+ old_reporter = state.memo.reporter
+ state.memo.reporter = AutodocReporter(content, state.memo.reporter)
+
+ if documenter.titles_allowed:
+ node = nodes.section()
+ # necessary so that the child nodes get the right source/line set
+ node.document = state.document
+ nested_parse_with_titles(state, content, node)
+ else:
+ node = nodes.paragraph()
+ node.document = state.document
+ state.nested_parse(content, 0, node)
+
+ return node.children
+ finally:
+ state.memo.reporter = old_reporter
+
+
+class AutodocDirective(Directive):
+ """A directive class for all autodoc directives. It works as a dispatcher of Documenters.
+
+ It invokes a Documenter on running. After the processing, it parses and returns
+ the generated content by Documenter.
+ """
+ option_spec = DefDict(identity)
+ has_content = True
+ required_arguments = 1
+ optional_arguments = 0
+ final_argument_whitespace = True
+
+ def run(self):
+ # type: () -> List[nodes.Node]
+ env = self.state.document.settings.env
+ reporter = self.state.document.reporter
+
+ try:
+ source, lineno = reporter.get_source_and_line(self.lineno)
+ except AttributeError:
+ source, lineno = (None, None)
+ logger.debug('[autodoc] %s:%s: input:\n%s', source, lineno, self.block_text)
+
+ # look up target Documenter
+ objtype = self.name[4:] # strip prefix (auto-).
+ doccls = AutoDirective._registry[objtype]
+
+ # process the options with the selected documenter's option_spec
+ try:
+ documenter_options = process_documenter_options(doccls, env.config, self.options)
+ except (KeyError, ValueError, TypeError) as exc:
+ # an option is either unknown or has a wrong type
+ msg = reporter.error('An option to %s is either unknown or '
+ 'has an invalid value: %s' % (self.name, exc),
+ line=lineno)
+ return [msg]
+
+ # generate the output
+ params = DocumenterBridge(env, reporter, documenter_options, lineno)
+ documenter = doccls(params, self.arguments[0])
+ documenter.generate(more_content=self.content)
+ if not params.result:
+ return params.warnings
+
+ logger.debug('[autodoc] output:\n%s', '\n'.join(params.result))
+
+ # record all filenames as dependencies -- this will at least
+ # partially make automatic invalidation possible
+ for fn in params.filename_set:
+ self.state.document.settings.record_dependencies.add(fn)
+
+ result = parse_generated_content(self.state, params.result, documenter)
+ return params.warnings + result
diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py
index 21bfe7b13..b56f649bf 100644
--- a/sphinx/ext/autosummary/__init__.py
+++ b/sphinx/ext/autosummary/__init__.py
@@ -72,7 +72,7 @@ from sphinx import addnodes
from sphinx.environment.adapters.toctree import TocTree
from sphinx.util import import_object, rst, logging
from sphinx.pycode import ModuleAnalyzer, PycodeError
-from sphinx.ext.autodoc import Options
+from sphinx.ext.autodoc.directive import Options
from sphinx.ext.autodoc.importer import import_module
if False:
diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py
index 00ea5919e..a745e058a 100644
--- a/sphinx/util/docutils.py
+++ b/sphinx/util/docutils.py
@@ -31,7 +31,7 @@ report_re = re.compile('^(.+?:(?:\\d+)?): \\((DEBUG|INFO|WARNING|ERROR|SEVERE)/(
if False:
# For type annotation
- from typing import Any, Callable, Iterator, List, Tuple # NOQA
+ from typing import Any, Callable, Dict, Iterator, List, Tuple # NOQA
from docutils import nodes # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.io import SphinxFileInput # NOQA
@@ -204,12 +204,12 @@ def is_html5_writer_available():
return __version_info__ > (0, 13, 0)
-def directive_helper(obj, has_content=None, argument_spec=None, **option_spec):
- # type: (Any, bool, Tuple[int, int, bool], Any) -> Any
+def directive_helper(obj, has_content=None, argument_spec=None, option_spec=None, **options):
+ # type: (Any, bool, Tuple[int, int, bool], Dict, Any) -> Any
if isinstance(obj, (types.FunctionType, types.MethodType)):
obj.content = has_content # type: ignore
obj.arguments = argument_spec or (0, 0, False) # type: ignore
- obj.options = option_spec # type: ignore
+ obj.options = option_spec or options # type: ignore
return convert_directive_function(obj)
else:
if has_content or argument_spec or option_spec:
From cac965cf77cb69d7807df566bcaa62d31cafd143 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sat, 16 Dec 2017 23:43:08 +0900
Subject: [PATCH 09/95] autodoc: refactor option_spec of autodirectives
---
sphinx/ext/autodoc/directive.py | 24 +++++-------------------
1 file changed, 5 insertions(+), 19 deletions(-)
diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py
index 9be273982..f348e105a 100644
--- a/sphinx/ext/autodoc/directive.py
+++ b/sphinx/ext/autodoc/directive.py
@@ -12,7 +12,7 @@ from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
from docutils.utils import assemble_option_dict
-from sphinx.ext.autodoc import AutoDirective, AutodocReporter, identity
+from sphinx.ext.autodoc import AutoDirective, AutodocReporter
from sphinx.util import logging
from sphinx.util.nodes import nested_parse_with_titles
@@ -33,26 +33,12 @@ AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members',
'show-inheritance', 'private-members', 'special-members']
-class DefDict(dict):
- """A dict that returns a default on nonexisting keys."""
- def __init__(self, default):
- # type: (Any) -> None
- dict.__init__(self)
- self.default = default
+class DummyOptionSpec(object):
+ """An option_spec allows any options."""
def __getitem__(self, key):
# type: (Any) -> Any
- try:
- return dict.__getitem__(self, key)
- except KeyError:
- return self.default
-
- def __bool__(self):
- # type: () -> bool
- # docutils check "if option_spec"
- return True
-
- __nonzero__ = __bool__ # for python2 compatibility
+ return lambda x: x
class Options(dict):
@@ -123,7 +109,7 @@ class AutodocDirective(Directive):
It invokes a Documenter on running. After the processing, it parses and returns
the generated content by Documenter.
"""
- option_spec = DefDict(identity)
+ option_spec = DummyOptionSpec()
has_content = True
required_arguments = 1
optional_arguments = 0
From 299b11f26f98b1f6bf61602ff9955a12b7d1593e Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sun, 17 Dec 2017 01:20:18 +0900
Subject: [PATCH 10/95] Replace AutodocReporter by switch_source_input()
---
CHANGES | 2 ++
doc/extdev/markupapi.rst | 22 ++++++++++++++++++++--
sphinx/ext/autodoc/__init__.py | 5 +++++
sphinx/ext/autodoc/directive.py | 12 +++---------
sphinx/util/docutils.py | 22 +++++++++++++++++++++-
5 files changed, 51 insertions(+), 12 deletions(-)
diff --git a/CHANGES b/CHANGES
index ed76ea4a7..73a75a894 100644
--- a/CHANGES
+++ b/CHANGES
@@ -19,6 +19,8 @@ Deprecated
* using a string value for :confval:`html_sidebars` is deprecated and only list
values will be accepted at 2.0.
+* ``sphinx.ext.autodoc.AutodocReporter`` is replaced by ``sphinx.util.docutils.
+ switch_source_input()`` and now deprecated. It will be removed in Sphinx-2.0.
Features added
--------------
diff --git a/doc/extdev/markupapi.rst b/doc/extdev/markupapi.rst
index df23f164d..8a18e2306 100644
--- a/doc/extdev/markupapi.rst
+++ b/doc/extdev/markupapi.rst
@@ -117,12 +117,30 @@ Both APIs parse the content into a given node. They are used like this::
node = docutils.nodes.paragraph()
# either
- from sphinx.ext.autodoc import AutodocReporter
- self.state.memo.reporter = AutodocReporter(self.result, self.state.memo.reporter) # override reporter to avoid errors from "include" directive
nested_parse_with_titles(self.state, self.result, node)
# or
self.state.nested_parse(self.result, 0, node)
+.. note::
+
+ ``sphinx.util.docutils.switch_source_input()`` allows to change a target file
+ during nested_parse. It is useful to mixture contents. For example, ``sphinx.
+ ext.autodoc`` uses it to parse docstrings.
+
+ from sphinx.util.docutils import switch_source_input
+
+ # Switch source_input between parsing content.
+ # Inside this context, all parsing errors and warnings are reported as
+ # happened in new source_input (in this case, ``self.result``).
+ with switch_source_input(self.state, self.result):
+ node = docutils.nodes.paragraph()
+ self.state.nested_parse(self.result, 0, node)
+
+ .. deprecated:: 1.7
+
+ Since Sphinx-1.6, ``sphinx.ext.autodoc.AutodocReporter`` is used for this purpose.
+ For now, it is replaced by ``switch_source_input()``.
+
If you don't need the wrapping node, you can use any concrete node type and
return ``node.children`` from the Directive.
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index c74ca43c9..ebe929ea3 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -15,6 +15,7 @@ import re
import sys
import inspect
import traceback
+import warnings
from six import PY2, iterkeys, iteritems, itervalues, text_type, class_types, string_types
@@ -22,6 +23,7 @@ from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
import sphinx
+from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.ext.autodoc.importer import mock, import_module
from sphinx.ext.autodoc.importer import _MockImporter # to keep compatibility # NOQA
from sphinx.ext.autodoc.inspector import format_annotation, formatargspec # to keep compatibility # NOQA
@@ -112,6 +114,9 @@ class AutodocReporter(object):
"""
def __init__(self, viewlist, reporter):
# type: (ViewList, Reporter) -> None
+ warnings.warn('AutodocRerporter is now deprecated. '
+ 'Use sphinx.util.docutils.switch_source_input() instead.',
+ RemovedInSphinx20Warning)
self.viewlist = viewlist
self.reporter = reporter
diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py
index f348e105a..78593d27c 100644
--- a/sphinx/ext/autodoc/directive.py
+++ b/sphinx/ext/autodoc/directive.py
@@ -12,8 +12,9 @@ from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
from docutils.utils import assemble_option_dict
-from sphinx.ext.autodoc import AutoDirective, AutodocReporter
+from sphinx.ext.autodoc import AutoDirective
from sphinx.util import logging
+from sphinx.util.docutils import switch_source_input
from sphinx.util.nodes import nested_parse_with_titles
if False:
@@ -82,12 +83,7 @@ def process_documenter_options(documenter, config, options):
def parse_generated_content(state, content, documenter):
# type: (State, StringList, Documenter) -> List[nodes.Node]
- try:
- # use a custom reporter that correctly assigns lines to source
- # filename/description and lineno
- old_reporter = state.memo.reporter
- state.memo.reporter = AutodocReporter(content, state.memo.reporter)
-
+ with switch_source_input(state, content):
if documenter.titles_allowed:
node = nodes.section()
# necessary so that the child nodes get the right source/line set
@@ -99,8 +95,6 @@ def parse_generated_content(state, content, documenter):
state.nested_parse(content, 0, node)
return node.children
- finally:
- state.memo.reporter = old_reporter
class AutodocDirective(Directive):
diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py
index a745e058a..f4dd96158 100644
--- a/sphinx/util/docutils.py
+++ b/sphinx/util/docutils.py
@@ -18,7 +18,7 @@ from contextlib import contextmanager
import docutils
from docutils.languages import get_language
-from docutils.statemachine import ViewList
+from docutils.statemachine import StateMachine, ViewList
from docutils.parsers.rst import directives, roles, convert_directive_function
from docutils.utils import Reporter
@@ -33,6 +33,7 @@ if False:
# For type annotation
from typing import Any, Callable, Dict, Iterator, List, Tuple # NOQA
from docutils import nodes # NOQA
+ from docutils.statemachine import State # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.io import SphinxFileInput # NOQA
@@ -216,3 +217,22 @@ def directive_helper(obj, has_content=None, argument_spec=None, option_spec=None
raise ExtensionError(__('when adding directive classes, no '
'additional arguments may be given'))
return obj
+
+
+@contextmanager
+def switch_source_input(state, content):
+ # type: (State, ViewList) -> None
+ """Switch current source input of state temporarily."""
+ try:
+ # remember the original ``get_source_and_line()`` method
+ get_source_and_line = state.memo.reporter.get_source_and_line
+
+ # replace it by new one
+ state_machine = StateMachine([], None)
+ state_machine.input_lines = content
+ state.memo.reporter.get_source_and_line = state_machine.get_source_and_line
+
+ yield
+ finally:
+ # restore the method
+ state.memo.reporter.get_source_and_line = get_source_and_line
From 1ab0d96a5f8b3ef1f07598a80aaf6c1f9eb5076c Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sun, 17 Dec 2017 01:31:32 +0900
Subject: [PATCH 11/95] Update docstrings
---
sphinx/ext/autodoc/directive.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py
index 78593d27c..077ee5dfd 100644
--- a/sphinx/ext/autodoc/directive.py
+++ b/sphinx/ext/autodoc/directive.py
@@ -53,6 +53,8 @@ class Options(dict):
class DocumenterBridge(object):
+ """A parameters container for Documenters."""
+
def __init__(self, env, reporter, options, lineno):
# type: (BuildEnvironment, Reporter, Options, int) -> None
self.env = env
@@ -70,6 +72,7 @@ class DocumenterBridge(object):
def process_documenter_options(documenter, config, options):
# type: (Type[Documenter], Config, Dict) -> Options
+ """Recognize options of Documenter from user input."""
for name in AUTODOC_DEFAULT_OPTIONS:
if name not in documenter.option_spec:
continue
@@ -83,6 +86,7 @@ def process_documenter_options(documenter, config, options):
def parse_generated_content(state, content, documenter):
# type: (State, StringList, Documenter) -> List[nodes.Node]
+ """Parse a generated content by Documenter."""
with switch_source_input(state, content):
if documenter.titles_allowed:
node = nodes.section()
From 9b18e83e32942bd1b4b8e636619ec3d479302194 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sun, 17 Dec 2017 01:39:15 +0900
Subject: [PATCH 12/95] Make AutoDirective as a simple object (not directive)
---
sphinx/ext/autodoc/__init__.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index ebe929ea3..45195ff41 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -1475,11 +1475,11 @@ class InstanceAttributeDocumenter(AttributeDocumenter):
AttributeDocumenter.add_content(self, more_content, no_docstring=True)
-class AutoDirective(Directive):
+class AutoDirective(object):
"""
- The AutoDirective class is used for all autodoc directives. It dispatches
- most of the work to one of the Documenters, which it selects through its
- *_registry* dictionary.
+ A registry of Documenters and attrgetters.
+
+ The *_registry* attribute is used to store registered Documenters.
The *_special_attrgetters* attribute is used to customize ``getattr()``
calls that the Documenters make; its entries are of the form ``type:
From 8bb6a01210ffc9994b4a2642545fb7b049655558 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sun, 17 Dec 2017 02:21:10 +0900
Subject: [PATCH 13/95] Fix mark up
---
doc/extdev/markupapi.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/extdev/markupapi.rst b/doc/extdev/markupapi.rst
index 8a18e2306..6f289ed9f 100644
--- a/doc/extdev/markupapi.rst
+++ b/doc/extdev/markupapi.rst
@@ -125,7 +125,7 @@ Both APIs parse the content into a given node. They are used like this::
``sphinx.util.docutils.switch_source_input()`` allows to change a target file
during nested_parse. It is useful to mixture contents. For example, ``sphinx.
- ext.autodoc`` uses it to parse docstrings.
+ ext.autodoc`` uses it to parse docstrings::
from sphinx.util.docutils import switch_source_input
From 109e01d94b7a9db338b4b0c5eea24aedffa7d245 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 18 Dec 2017 00:59:00 +0900
Subject: [PATCH 14/95] Fix mypy violations
---
sphinx/ext/autodoc/__init__.py | 6 ++++--
sphinx/ext/autosummary/__init__.py | 8 ++++----
sphinx/util/docutils.py | 4 ++--
3 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 45195ff41..150620a62 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -19,7 +19,6 @@ import warnings
from six import PY2, iterkeys, iteritems, itervalues, text_type, class_types, string_types
-from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
import sphinx
@@ -41,7 +40,10 @@ if False:
# For type annotation
from types import ModuleType # NOQA
from typing import Any, Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union # NOQA
+ from docutils import nodes # NOQA
+ from docutils.utils import Reporter # NOQA
from sphinx.application import Sphinx # NOQA
+ from sphinx.ext.autodoc.directive import DocumenterBridge # NOQA
logger = logging.getLogger(__name__)
@@ -271,7 +273,7 @@ class Documenter(object):
raise NotImplementedError('must be implemented in subclasses')
def __init__(self, directive, name, indent=u''):
- # type: (Directive, unicode, unicode) -> None
+ # type: (DocumenterBridge, unicode, unicode) -> None
self.directive = directive
self.env = directive.env
self.options = directive.genopt
diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py
index b56f649bf..3294709ad 100644
--- a/sphinx/ext/autosummary/__init__.py
+++ b/sphinx/ext/autosummary/__init__.py
@@ -72,7 +72,7 @@ from sphinx import addnodes
from sphinx.environment.adapters.toctree import TocTree
from sphinx.util import import_object, rst, logging
from sphinx.pycode import ModuleAnalyzer, PycodeError
-from sphinx.ext.autodoc.directive import Options
+from sphinx.ext.autodoc.directive import DocumenterBridge, Options
from sphinx.ext.autodoc.importer import import_module
if False:
@@ -153,9 +153,9 @@ def autosummary_table_visit_html(self, node):
# -- autodoc integration -------------------------------------------------------
-class FakeDirective(object):
- env = {} # type: Dict
- genopt = Options()
+class FakeDirective(DocumenterBridge):
+ def __init__(self):
+ super(FakeDirective, self).__init__({}, None, Options(), 0) # type: ignore
def get_documenter(obj, parent):
diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py
index f4dd96158..5377e493c 100644
--- a/sphinx/util/docutils.py
+++ b/sphinx/util/docutils.py
@@ -31,7 +31,7 @@ report_re = re.compile('^(.+?:(?:\\d+)?): \\((DEBUG|INFO|WARNING|ERROR|SEVERE)/(
if False:
# For type annotation
- from typing import Any, Callable, Dict, Iterator, List, Tuple # NOQA
+ from typing import Any, Callable, Dict, Generator, Iterator, List, Tuple # NOQA
from docutils import nodes # NOQA
from docutils.statemachine import State # NOQA
from sphinx.environment import BuildEnvironment # NOQA
@@ -221,7 +221,7 @@ def directive_helper(obj, has_content=None, argument_spec=None, option_spec=None
@contextmanager
def switch_source_input(state, content):
- # type: (State, ViewList) -> None
+ # type: (State, ViewList) -> Generator
"""Switch current source input of state temporarily."""
try:
# remember the original ``get_source_and_line()`` method
From 04995b703fbf43345ee805a140a64f6bf78b0ff9 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Wed, 20 Dec 2017 00:05:57 +0900
Subject: [PATCH 15/95] Fix typo
---
doc/extdev/markupapi.rst | 2 +-
sphinx/ext/autodoc/__init__.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/extdev/markupapi.rst b/doc/extdev/markupapi.rst
index 6f289ed9f..97abc17b9 100644
--- a/doc/extdev/markupapi.rst
+++ b/doc/extdev/markupapi.rst
@@ -138,7 +138,7 @@ Both APIs parse the content into a given node. They are used like this::
.. deprecated:: 1.7
- Since Sphinx-1.6, ``sphinx.ext.autodoc.AutodocReporter`` is used for this purpose.
+ Until Sphinx-1.6, ``sphinx.ext.autodoc.AutodocReporter`` is used for this purpose.
For now, it is replaced by ``switch_source_input()``.
If you don't need the wrapping node, you can use any concrete node type and
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 150620a62..68f78eeb2 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -116,7 +116,7 @@ class AutodocReporter(object):
"""
def __init__(self, viewlist, reporter):
# type: (ViewList, Reporter) -> None
- warnings.warn('AutodocRerporter is now deprecated. '
+ warnings.warn('AutodocReporter is now deprecated. '
'Use sphinx.util.docutils.switch_source_input() instead.',
RemovedInSphinx20Warning)
self.viewlist = viewlist
From 25b96b833d79936734df5f23ee055b59969cd6a2 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Wed, 20 Dec 2017 00:09:49 +0900
Subject: [PATCH 16/95] Revert the changes of directive_helper
---
sphinx/util/docutils.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py
index 5377e493c..3cd257cba 100644
--- a/sphinx/util/docutils.py
+++ b/sphinx/util/docutils.py
@@ -31,7 +31,7 @@ report_re = re.compile('^(.+?:(?:\\d+)?): \\((DEBUG|INFO|WARNING|ERROR|SEVERE)/(
if False:
# For type annotation
- from typing import Any, Callable, Dict, Generator, Iterator, List, Tuple # NOQA
+ from typing import Any, Callable, Generator, Iterator, List, Tuple # NOQA
from docutils import nodes # NOQA
from docutils.statemachine import State # NOQA
from sphinx.environment import BuildEnvironment # NOQA
@@ -205,12 +205,12 @@ def is_html5_writer_available():
return __version_info__ > (0, 13, 0)
-def directive_helper(obj, has_content=None, argument_spec=None, option_spec=None, **options):
- # type: (Any, bool, Tuple[int, int, bool], Dict, Any) -> Any
+def directive_helper(obj, has_content=None, argument_spec=None, **option_spec):
+ # type: (Any, bool, Tuple[int, int, bool], Any) -> Any
if isinstance(obj, (types.FunctionType, types.MethodType)):
obj.content = has_content # type: ignore
obj.arguments = argument_spec or (0, 0, False) # type: ignore
- obj.options = option_spec or options # type: ignore
+ obj.options = option_spec # type: ignore
return convert_directive_function(obj)
else:
if has_content or argument_spec or option_spec:
From 242bf9a38a9aa179e4f40c57d4b9a7ff90695127 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Wed, 20 Dec 2017 00:11:28 +0900
Subject: [PATCH 17/95] Fix typo
---
doc/extdev/markupapi.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/extdev/markupapi.rst b/doc/extdev/markupapi.rst
index 97abc17b9..ffa08cae7 100644
--- a/doc/extdev/markupapi.rst
+++ b/doc/extdev/markupapi.rst
@@ -124,7 +124,7 @@ Both APIs parse the content into a given node. They are used like this::
.. note::
``sphinx.util.docutils.switch_source_input()`` allows to change a target file
- during nested_parse. It is useful to mixture contents. For example, ``sphinx.
+ during nested_parse. It is useful to mixed contents. For example, ``sphinx.
ext.autodoc`` uses it to parse docstrings::
from sphinx.util.docutils import switch_source_input
From 7a194f52960fe5ace04ef7daa72563e6d3bf094f Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 18 Dec 2017 22:09:29 +0900
Subject: [PATCH 18/95] autodoc: Use logging module to emit warnings
---
CHANGES | 2 ++
sphinx/ext/autodoc/__init__.py | 41 +++++++++++++++------------------
tests/py35/test_autodoc_py35.py | 25 ++++++++------------
tests/test_autodoc.py | 38 +++++++++++++++---------------
4 files changed, 51 insertions(+), 55 deletions(-)
diff --git a/CHANGES b/CHANGES
index 2ec7d56bb..cb43a26ae 100644
--- a/CHANGES
+++ b/CHANGES
@@ -13,6 +13,8 @@ Incompatible changes
* #3929: apidoc: Move sphinx.apidoc to sphinx.ext.apidoc
* #4226: apidoc: Generate new style makefile (make-mode)
* #4274: sphinx-build returns 2 as an exit code on argument error
+* autodoc does not generate warnings messages to the generated document even if
+ :confval:`keep_warnings` is True. They are only emitted to stderr.
Deprecated
----------
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index ff161565c..48afbcc91 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -354,8 +354,7 @@ class Documenter(object):
explicit_modname, path, base, args, retann = \
py_ext_sig_re.match(self.name).groups() # type: ignore
except AttributeError:
- self.directive.warn('invalid signature for auto%s (%r)' %
- (self.objtype, self.name))
+ logger.warning('invalid signature for auto%s (%r)' % (self.objtype, self.name))
return False
# support explicit module and class name separation via ::
@@ -432,8 +431,7 @@ class Documenter(object):
if PY2:
errmsg = errmsg.decode('utf-8') # type: ignore
- logger.debug(errmsg)
- self.directive.warn(errmsg)
+ logger.warning(errmsg)
self.env.note_reread()
return False
@@ -493,8 +491,8 @@ class Documenter(object):
try:
args = self.format_args()
except Exception as err:
- self.directive.warn('error while formatting arguments for '
- '%s: %s' % (self.fullname, err))
+ logger.warning('error while formatting arguments for %s: %s' %
+ (self.fullname, err))
args = None
retann = self.retann
@@ -623,8 +621,8 @@ class Documenter(object):
members.append((mname, self.get_attr(self.object, mname)))
except AttributeError:
if mname not in analyzed_member_names:
- self.directive.warn('missing attribute %s in object %s'
- % (mname, self.fullname))
+ logger.warning('missing attribute %s in object %s' %
+ (mname, self.fullname))
elif self.options.inherited_members:
# safe_getmembers() uses dir() which pulls in members from all
# base classes
@@ -820,11 +818,11 @@ class Documenter(object):
"""
if not self.parse_name():
# need a module to import
- self.directive.warn(
+ logger.warning(
'don\'t know which module to import for autodocumenting '
'%r (try placing a "module" or "currentmodule" directive '
- 'in the document, or giving an explicit module name)'
- % self.name)
+ 'in the document, or giving an explicit module name)' %
+ self.name)
return
# now, import the module and get object to document
@@ -910,15 +908,15 @@ class ModuleDocumenter(Documenter):
def resolve_name(self, modname, parents, path, base):
# type: (str, Any, str, Any) -> Tuple[str, List[unicode]]
if modname is not None:
- self.directive.warn('"::" in automodule name doesn\'t make sense')
+ logger.warning('"::" in automodule name doesn\'t make sense')
return (path or '') + base, []
def parse_name(self):
# type: () -> bool
ret = Documenter.parse_name(self)
if self.args or self.retann:
- self.directive.warn('signature arguments or return annotation '
- 'given for automodule %s' % self.fullname)
+ logger.warning('signature arguments or return annotation '
+ 'given for automodule %s' % self.fullname)
return ret
def add_directive_header(self, sig):
@@ -949,7 +947,7 @@ class ModuleDocumenter(Documenter):
# Sometimes __all__ is broken...
if not isinstance(memberlist, (list, tuple)) or not \
all(isinstance(entry, string_types) for entry in memberlist):
- self.directive.warn(
+ logger.warning(
'__all__ should be a list of strings, not %r '
'(in module %s) -- ignoring __all__' %
(memberlist, self.fullname))
@@ -962,10 +960,10 @@ class ModuleDocumenter(Documenter):
try:
ret.append((mname, safe_getattr(self.object, mname)))
except AttributeError:
- self.directive.warn(
+ logger.warning(
'missing attribute mentioned in :members: or __all__: '
- 'module %s, attribute %s' % (
- safe_getattr(self.object, '__name__', '???'), mname))
+ 'module %s, attribute %s' %
+ (safe_getattr(self.object, '__name__', '???'), mname))
return False, ret
@@ -1542,7 +1540,7 @@ class AutoDirective(Directive):
def warn(self, msg):
# type: (unicode) -> None
- self.warnings.append(self.reporter.warning(msg, line=self.lineno))
+ logger.warning(msg, line=self.lineno)
def run(self):
# type: () -> List[nodes.Node]
@@ -1550,7 +1548,6 @@ class AutoDirective(Directive):
# a set of dependent filenames
self.reporter = self.state.document.reporter
self.env = self.state.document.settings.env
- self.warnings = [] # type: List[unicode]
self.result = ViewList()
try:
@@ -1585,7 +1582,7 @@ class AutoDirective(Directive):
documenter = doc_class(self, self.arguments[0])
documenter.generate(more_content=self.content)
if not self.result:
- return self.warnings
+ return []
logger.debug('[autodoc] output:\n%s', '\n'.join(self.result))
@@ -1610,7 +1607,7 @@ class AutoDirective(Directive):
node.document = self.state.document
self.state.nested_parse(self.result, 0, node)
self.state.memo.reporter = old_reporter
- return self.warnings + node.children
+ return node.children
def add_documenter(cls):
diff --git a/tests/py35/test_autodoc_py35.py b/tests/py35/test_autodoc_py35.py
index 481374948..9a94dbd27 100644
--- a/tests/py35/test_autodoc_py35.py
+++ b/tests/py35/test_autodoc_py35.py
@@ -21,6 +21,7 @@ from docutils.statemachine import ViewList
from sphinx.ext.autodoc import AutoDirective, add_documenter, \
ModuleLevelDocumenter, FunctionDocumenter, cut_lines, between, ALL
+from sphinx.util import logging
app = None
@@ -30,7 +31,7 @@ def setup_module(rootdir, sphinx_test_tempdir):
global app
srcdir = sphinx_test_tempdir / 'autodoc-root'
if not srcdir.exists():
- (rootdir/'test-root').copytree(srcdir)
+ (rootdir / 'test-root').copytree(srcdir)
app = SphinxTestApp(srcdir=srcdir)
app.builder.env.app = app
app.builder.env.temp_data['docname'] = 'dummy'
@@ -47,7 +48,7 @@ directive = options = None
@pytest.fixture
def setup_test():
global options, directive
- global processed_docstrings, processed_signatures, _warnings
+ global processed_docstrings, processed_signatures
options = Struct(
inherited_members = False,
@@ -70,24 +71,17 @@ def setup_test():
env = app.builder.env,
genopt = options,
result = ViewList(),
- warn = warnfunc,
filename_set = set(),
)
processed_docstrings = []
processed_signatures = []
- _warnings = []
-_warnings = []
processed_docstrings = []
processed_signatures = []
-def warnfunc(msg):
- _warnings.append(msg)
-
-
def process_docstring(app, what, name, obj, options, lines):
processed_docstrings.append((what, name))
if name == 'bar':
@@ -111,20 +105,21 @@ def skip_member(app, what, name, obj, skip, options):
@pytest.mark.usefixtures('setup_test')
def test_generate():
+ logging.setup(app, app._status, app._warning)
+
def assert_warns(warn_str, objtype, name, **kw):
inst = AutoDirective._registry[objtype](directive, name)
inst.generate(**kw)
assert len(directive.result) == 0, directive.result
- assert len(_warnings) == 1, _warnings
- assert warn_str in _warnings[0], _warnings
- del _warnings[:]
+ assert warn_str in app._warning.getvalue()
+ app._warning.truncate(0)
def assert_works(objtype, name, **kw):
inst = AutoDirective._registry[objtype](directive, name)
inst.generate(**kw)
assert directive.result
# print '\n'.join(directive.result)
- assert len(_warnings) == 0, _warnings
+ assert app._warning.getvalue() == ''
del directive.result[:]
def assert_processes(items, objtype, name, **kw):
@@ -137,7 +132,7 @@ def test_generate():
inst = AutoDirective._registry[objtype](directive, name)
inst.generate(**kw)
# print '\n'.join(directive.result)
- assert len(_warnings) == 0, _warnings
+ assert app._warning.getvalue() == ''
assert item in directive.result
del directive.result[:]
@@ -145,7 +140,7 @@ def test_generate():
inst = AutoDirective._registry[objtype](directive, name)
inst.options.member_order = member_order
inst.generate(**kw)
- assert len(_warnings) == 0, _warnings
+ assert app._warning.getvalue() == ''
items = list(reversed(items))
lineiter = iter(directive.result)
# for line in directive.result:
diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py
index 989c367b6..4ab58d891 100644
--- a/tests/test_autodoc.py
+++ b/tests/test_autodoc.py
@@ -21,6 +21,7 @@ from docutils.statemachine import ViewList
from sphinx.ext.autodoc import AutoDirective, add_documenter, \
ModuleLevelDocumenter, FunctionDocumenter, cut_lines, between, ALL
+from sphinx.util import logging
app = None
@@ -47,7 +48,7 @@ directive = options = None
@pytest.fixture
def setup_test():
global options, directive
- global processed_docstrings, processed_signatures, _warnings
+ global processed_docstrings, processed_signatures
options = Struct(
inherited_members = False,
@@ -70,28 +71,24 @@ def setup_test():
env = app.builder.env,
genopt = options,
result = ViewList(),
- warn = warnfunc,
filename_set = set(),
)
processed_docstrings = []
processed_signatures = []
- _warnings = []
+
+ app._status.truncate(0)
+ app._warning.truncate(0)
yield
AutoDirective._special_attrgetters.clear()
-_warnings = []
processed_docstrings = []
processed_signatures = []
-def warnfunc(msg):
- _warnings.append(msg)
-
-
def process_docstring(app, what, name, obj, options, lines):
processed_docstrings.append((what, name))
if name == 'bar':
@@ -115,6 +112,8 @@ def skip_member(app, what, name, obj, skip, options):
@pytest.mark.usefixtures('setup_test')
def test_parse_name():
+ logging.setup(app, app._status, app._warning)
+
def verify(objtype, name, result):
inst = AutoDirective._registry[objtype](directive, name)
assert inst.parse_name()
@@ -124,8 +123,7 @@ def test_parse_name():
verify('module', 'test_autodoc', ('test_autodoc', [], None, None))
verify('module', 'test.test_autodoc', ('test.test_autodoc', [], None, None))
verify('module', 'test(arg)', ('test', [], 'arg', None))
- assert 'signature arguments' in _warnings[0]
- del _warnings[:]
+ assert 'signature arguments' in app._warning.getvalue()
# for functions/classes
verify('function', 'test_autodoc.raises',
@@ -241,7 +239,6 @@ def test_format_signature():
# test exception handling (exception is caught and args is '')
directive.env.config.autodoc_docstring_signature = False
assert formatsig('function', 'int', int, None, None) == ''
- del _warnings[:]
# test processing by event handler
assert formatsig('method', 'bar', H.foo1, None, None) == '42'
@@ -533,6 +530,8 @@ def test_docstring_property_processing():
@pytest.mark.usefixtures('setup_test')
def test_new_documenter():
+ logging.setup(app, app._status, app._warning)
+
class MyDocumenter(ModuleLevelDocumenter):
objtype = 'integer'
directivetype = 'data'
@@ -548,10 +547,11 @@ def test_new_documenter():
add_documenter(MyDocumenter)
def assert_result_contains(item, objtype, name, **kw):
+ app._warning.truncate(0)
inst = AutoDirective._registry[objtype](directive, name)
inst.generate(**kw)
# print '\n'.join(directive.result)
- assert len(_warnings) == 0, _warnings
+ assert app._warning.getvalue() == ''
assert item in directive.result
del directive.result[:]
@@ -593,20 +593,22 @@ def test_attrgetter_using():
@pytest.mark.usefixtures('setup_test')
def test_generate():
+ logging.setup(app, app._status, app._warning)
+
def assert_warns(warn_str, objtype, name, **kw):
inst = AutoDirective._registry[objtype](directive, name)
inst.generate(**kw)
assert len(directive.result) == 0, directive.result
- assert len(_warnings) == 1, _warnings
- assert warn_str in _warnings[0], _warnings
- del _warnings[:]
+
+ assert warn_str in app._warning.getvalue()
+ app._warning.truncate(0)
def assert_works(objtype, name, **kw):
inst = AutoDirective._registry[objtype](directive, name)
inst.generate(**kw)
assert directive.result
# print '\n'.join(directive.result)
- assert len(_warnings) == 0, _warnings
+ assert app._warning.getvalue() == ''
del directive.result[:]
def assert_processes(items, objtype, name, **kw):
@@ -619,7 +621,7 @@ def test_generate():
inst = AutoDirective._registry[objtype](directive, name)
inst.generate(**kw)
# print '\n'.join(directive.result)
- assert len(_warnings) == 0, _warnings
+ assert app._warning.getvalue() == ''
assert item in directive.result
del directive.result[:]
@@ -627,7 +629,7 @@ def test_generate():
inst = AutoDirective._registry[objtype](directive, name)
inst.options.member_order = member_order
inst.generate(**kw)
- assert len(_warnings) == 0, _warnings
+ assert app._warning.getvalue() == ''
items = list(reversed(items))
lineiter = iter(directive.result)
# for line in directive.result:
From 2d99648e9982325bbd670da11df5f809e3134284 Mon Sep 17 00:00:00 2001
From: Christer Bystrom
Date: Thu, 21 Dec 2017 19:06:17 +0100
Subject: [PATCH 19/95] Closes #4334: sphinx-apidoc: Don't generate references
to non-existing files in TOC
---
CHANGES | 1 +
sphinx/apidoc.py | 7 +-
.../test-apidoc-toc/mypackage/__init__.py | 0
tests/roots/test-apidoc-toc/mypackage/main.py | 16 ++++
.../test-apidoc-toc/mypackage/no_init/foo.py | 1 +
.../mypackage/resource/__init__.py | 0
.../mypackage/resource/resource.txt | 1 +
.../mypackage/something/__init__.py | 1 +
tests/test_apidoc.py | 77 +++++++++++++++++++
9 files changed, 103 insertions(+), 1 deletion(-)
create mode 100644 tests/roots/test-apidoc-toc/mypackage/__init__.py
create mode 100755 tests/roots/test-apidoc-toc/mypackage/main.py
create mode 100644 tests/roots/test-apidoc-toc/mypackage/no_init/foo.py
create mode 100644 tests/roots/test-apidoc-toc/mypackage/resource/__init__.py
create mode 100644 tests/roots/test-apidoc-toc/mypackage/resource/resource.txt
create mode 100644 tests/roots/test-apidoc-toc/mypackage/something/__init__.py
diff --git a/CHANGES b/CHANGES
index b7a16af4f..5b3600d1d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -21,6 +21,7 @@ Features added
Bugs fixed
----------
+* #4334: sphinx-apidoc: Don't generate references to non-existing files in TOC
* #4206: latex: reST label between paragraphs loses paragraph break
* #4231: html: Apply fixFirefoxAnchorBug only under Firefox
* #4221: napoleon depends on autodoc, but users need to load it manually
diff --git a/sphinx/apidoc.py b/sphinx/apidoc.py
index 24ed874b0..335abe344 100644
--- a/sphinx/apidoc.py
+++ b/sphinx/apidoc.py
@@ -116,7 +116,12 @@ def create_package_file(root, master_package, subroot, py_files, opts, subs, is_
text += '\n'
# build a list of directories that are szvpackages (contain an INITPY file)
- subs = [sub for sub in subs if path.isfile(path.join(root, sub, INITPY))]
+ # and also checks the INITPY file is not empty, or there are other python
+ # source files in that folder.
+ # (depending on settings - but shall_skip() takes care of that)
+ subs = [sub for sub in subs if not
+ shall_skip(path.join(root, sub, INITPY), opts)]
+
# if there are some package directories, add a TOC for theses subpackages
if subs:
text += format_heading(2, 'Subpackages')
diff --git a/tests/roots/test-apidoc-toc/mypackage/__init__.py b/tests/roots/test-apidoc-toc/mypackage/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/roots/test-apidoc-toc/mypackage/main.py b/tests/roots/test-apidoc-toc/mypackage/main.py
new file mode 100755
index 000000000..5d3da04b9
--- /dev/null
+++ b/tests/roots/test-apidoc-toc/mypackage/main.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+import os
+
+import mod_resource
+
+import mod_something
+
+
+if __name__ == "__main__":
+ print("Hello, world! -> something returns: {}".format(mod_something.something()))
+
+ res_path = \
+ os.path.join(os.path.dirname(mod_resource.__file__), 'resource.txt')
+ with open(res_path) as f:
+ text = f.read()
+ print("From mod_resource:resource.txt -> {}".format(text))
diff --git a/tests/roots/test-apidoc-toc/mypackage/no_init/foo.py b/tests/roots/test-apidoc-toc/mypackage/no_init/foo.py
new file mode 100644
index 000000000..ce059b276
--- /dev/null
+++ b/tests/roots/test-apidoc-toc/mypackage/no_init/foo.py
@@ -0,0 +1 @@
+MESSAGE="There's no __init__.py in this folder, hence we should be left out"
diff --git a/tests/roots/test-apidoc-toc/mypackage/resource/__init__.py b/tests/roots/test-apidoc-toc/mypackage/resource/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/roots/test-apidoc-toc/mypackage/resource/resource.txt b/tests/roots/test-apidoc-toc/mypackage/resource/resource.txt
new file mode 100644
index 000000000..5b64c924d
--- /dev/null
+++ b/tests/roots/test-apidoc-toc/mypackage/resource/resource.txt
@@ -0,0 +1 @@
+This is a text resource to be included in this otherwise empty module. No python contents here.
\ No newline at end of file
diff --git a/tests/roots/test-apidoc-toc/mypackage/something/__init__.py b/tests/roots/test-apidoc-toc/mypackage/something/__init__.py
new file mode 100644
index 000000000..259184ba3
--- /dev/null
+++ b/tests/roots/test-apidoc-toc/mypackage/something/__init__.py
@@ -0,0 +1 @@
+"Subpackage Something"
\ No newline at end of file
diff --git a/tests/test_apidoc.py b/tests/test_apidoc.py
index 20582e9fc..faceb715f 100644
--- a/tests/test_apidoc.py
+++ b/tests/test_apidoc.py
@@ -188,3 +188,80 @@ def test_extension_parsed(make_app, apidoc):
with open(outdir / 'conf.py') as f:
rst = f.read()
assert "sphinx.ext.mathjax" in rst
+
+
+@pytest.mark.apidoc(
+ coderoot='test-apidoc-toc',
+ options=["--implicit-namespaces"],
+)
+def test_toc_all_references_should_exist_pep420_enabled(make_app, apidoc):
+ """All references in toc should exist. This test doesn't say if
+ directories with empty __init__.py and and nothing else should be
+ skipped, just ensures consistency between what's referenced in the toc
+ and what is created. This is the variant with pep420 enabled.
+ """
+ outdir = apidoc.outdir
+ assert (outdir / 'conf.py').isfile()
+
+ toc = extract_toc(outdir / 'mypackage.rst')
+
+ refs = [l.strip() for l in toc.splitlines() if l.strip()]
+ found_refs = []
+ missing_files = []
+ for ref in refs:
+ if ref and ref[0] in (':', '#'):
+ continue
+ found_refs.append(ref)
+ filename = "{}.rst".format(ref)
+ if not (outdir / filename).isfile():
+ missing_files.append(filename)
+
+ assert len(missing_files) == 0, \
+ 'File(s) referenced in TOC not found: {}\n' \
+ 'TOC:\n{}'.format(", ".join(missing_files), toc)
+
+
+@pytest.mark.apidoc(
+ coderoot='test-apidoc-toc',
+)
+def test_toc_all_references_should_exist_pep420_disabled(make_app, apidoc):
+ """All references in toc should exist. This test doesn't say if
+ directories with empty __init__.py and and nothing else should be
+ skipped, just ensures consistency between what's referenced in the toc
+ and what is created. This is the variant with pep420 disabled.
+ """
+ outdir = apidoc.outdir
+ assert (outdir / 'conf.py').isfile()
+
+ toc = extract_toc(outdir / 'mypackage.rst')
+
+ refs = [l.strip() for l in toc.splitlines() if l.strip()]
+ found_refs = []
+ missing_files = []
+ for ref in refs:
+ if ref and ref[0] in (':', '#'):
+ continue
+ filename = "{}.rst".format(ref)
+ found_refs.append(ref)
+ if not (outdir / filename).isfile():
+ missing_files.append(filename)
+
+ assert len(missing_files) == 0, \
+ 'File(s) referenced in TOC not found: {}\n' \
+ 'TOC:\n{}'.format(", ".join(missing_files), toc)
+
+
+def extract_toc(path):
+ """Helper: Extract toc section from package rst file"""
+ with open(path) as f:
+ rst = f.read()
+
+ # Read out the part containing the toctree
+ toctree_start = "\n.. toctree::\n"
+ toctree_end = "\nSubmodules"
+
+ start_idx = rst.index(toctree_start)
+ end_idx = rst.index(toctree_end, start_idx)
+ toctree = rst[start_idx + len(toctree_start):end_idx]
+
+ return toctree
From 90acaa82f64ca2a3665a9fd560dd3a9b04dd8312 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Wed, 27 Dec 2017 15:04:30 +0900
Subject: [PATCH 20/95] Diet tests on appveyor
---
.appveyor.yml | 7 -------
1 file changed, 7 deletions(-)
diff --git a/.appveyor.yml b/.appveyor.yml
index a3f83394f..cfc8d884f 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -6,19 +6,12 @@ environment:
matrix:
- PYTHON: 27
- DOCUTILS: 0.13.1
- TEST_IGNORE: --ignore py35
- - PYTHON: 27
- DOCUTILS: 0.14
TEST_IGNORE: --ignore py35
- PYTHON: 36
- DOCUTILS: 0.14
- PYTHON: 36-x64
- DOCUTILS: 0.14
install:
- C:\Python%PYTHON%\python.exe -m pip install -U pip setuptools
- - C:\Python%PYTHON%\python.exe -m pip install docutils==%DOCUTILS% mock
- C:\Python%PYTHON%\python.exe -m pip install .[test,websupport]
# No automatic build, just run python tests
From 1d64ade74911c9a9d3178d4f215fe4d75c2eddda Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sun, 18 Jun 2017 00:39:23 +0900
Subject: [PATCH 21/95] Refactor: Add import_object()
---
sphinx/ext/autodoc/__init__.py | 58 +++++-----------------------------
sphinx/ext/autodoc/importer.py | 52 +++++++++++++++++++++++++++++-
2 files changed, 59 insertions(+), 51 deletions(-)
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 5c1a71ea9..f9f7c5edc 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -14,9 +14,8 @@
import re
import sys
import inspect
-import traceback
-from six import PY2, iterkeys, iteritems, itervalues, text_type, class_types, string_types
+from six import iterkeys, iteritems, itervalues, text_type, class_types, string_types
from docutils import nodes
from docutils.utils import assemble_option_dict
@@ -24,7 +23,7 @@ from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
import sphinx
-from sphinx.ext.autodoc.importer import mock, import_module
+from sphinx.ext.autodoc.importer import mock, import_object
from sphinx.ext.autodoc.importer import _MockImporter # to keep compatibility # NOQA
from sphinx.ext.autodoc.inspector import format_annotation, formatargspec # to keep compatibility # NOQA
from sphinx.util import rpartition, force_decode
@@ -384,56 +383,15 @@ class Documenter(object):
Returns True if successful, False if an error occurred.
"""
- if self.objpath:
- logger.debug('[autodoc] from %s import %s',
- self.modname, '.'.join(self.objpath))
- # always enable mock import hook
- # it will do nothing if autodoc_mock_imports is empty
with mock(self.env.config.autodoc_mock_imports):
try:
- logger.debug('[autodoc] import %s', self.modname)
- obj = import_module(self.modname, self.env.config.autodoc_warningiserror)
- parent = None
- self.module = obj
- logger.debug('[autodoc] => %r', obj)
- for part in self.objpath:
- parent = obj
- logger.debug('[autodoc] getattr(_, %r)', part)
- obj = self.get_attr(obj, part)
- logger.debug('[autodoc] => %r', obj)
- self.object_name = part
- self.parent = parent
- self.object = obj
+ ret = import_object(self.modname, self.objpath, self.objtype,
+ attrgetter=self.get_attr,
+ warningiserror=self.env.config.autodoc_warningiserror)
+ self.module, self.parent, self.object_name, self.object = ret
return True
- except (AttributeError, ImportError) as exc:
- if self.objpath:
- errmsg = 'autodoc: failed to import %s %r from module %r' % \
- (self.objtype, '.'.join(self.objpath), self.modname)
- else:
- errmsg = 'autodoc: failed to import %s %r' % \
- (self.objtype, self.fullname)
-
- if isinstance(exc, ImportError):
- # import_module() raises ImportError having real exception obj and
- # traceback
- real_exc, traceback_msg = exc.args
- if isinstance(real_exc, SystemExit):
- errmsg += ('; the module executes module level statement ' +
- 'and it might call sys.exit().')
- elif isinstance(real_exc, ImportError):
- errmsg += ('; the following exception was raised:\n%s' %
- real_exc.args[0])
- else:
- errmsg += ('; the following exception was raised:\n%s' %
- traceback_msg)
- else:
- errmsg += ('; the following exception was raised:\n%s' %
- traceback.format_exc())
-
- if PY2:
- errmsg = errmsg.decode('utf-8') # type: ignore
- logger.debug(errmsg)
- self.directive.warn(errmsg)
+ except ImportError as exc:
+ self.directive.warn(exc.args[0])
self.env.note_reread()
return False
diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py
index 75e045c21..95ca58d0b 100644
--- a/sphinx/ext/autodoc/importer.py
+++ b/sphinx/ext/autodoc/importer.py
@@ -15,11 +15,14 @@ import traceback
import contextlib
from types import FunctionType, MethodType, ModuleType
+from six import PY2
+
from sphinx.util import logging
+from sphinx.util.inspect import safe_getattr
if False:
# For type annotation
- from typing import Any, Generator, List, Set # NOQA
+ from typing import Any, Callable, Generator, List, Set # NOQA
logger = logging.getLogger(__name__)
@@ -144,3 +147,50 @@ def import_module(modname, warningiserror=False):
# Importing modules may cause any side effects, including
# SystemExit, so we need to catch all errors.
raise ImportError(exc, traceback.format_exc())
+
+
+def import_object(modname, objpath, objtype='', attrgetter=safe_getattr, warningiserror=False):
+ # type: (str, List[unicode], str, Callable[[Any, unicode], Any]) -> Any
+ if objpath:
+ logger.debug('[autodoc] from %s import %s', modname, '.'.join(objpath))
+ else:
+ logger.debug('[autodoc] import %s', modname)
+
+ try:
+ module = import_module(modname, warningiserror=warningiserror)
+ logger.debug('[autodoc] => %r', module)
+ obj = module
+ parent = None
+ object_name = None
+ for attrname in objpath:
+ parent = obj
+ logger.debug('[autodoc] getattr(_, %r)', attrname)
+ obj = attrgetter(obj, attrname)
+ logger.debug('[autodoc] => %r', obj)
+ object_name = attrname
+ return [module, parent, object_name, obj]
+ except (AttributeError, ImportError) as exc:
+ if objpath:
+ errmsg = ('autodoc: failed to import %s %r from module %r' %
+ (objtype, '.'.join(objpath), modname))
+ else:
+ errmsg = 'autodoc: failed to import %s %r' % (objtype, modname)
+
+ if isinstance(exc, ImportError):
+ # import_module() raises ImportError having real exception obj and
+ # traceback
+ real_exc, traceback_msg = exc.args
+ if isinstance(real_exc, SystemExit):
+ errmsg += ('; the module executes module level statement '
+ 'and it might call sys.exit().')
+ elif isinstance(real_exc, ImportError):
+ errmsg += '; the following exception was raised:\n%s' % real_exc.args[0]
+ else:
+ errmsg += '; the following exception was raised:\n%s' % traceback_msg
+ else:
+ errmsg += '; the following exception was raised:\n%s' % traceback.format_exc()
+
+ if PY2:
+ errmsg = errmsg.decode('utf-8') # type: ignore
+ logger.debug(errmsg)
+ raise ImportError(errmsg)
From 5d6413b7120cfc6d3d0cc9367cfe8b6f7ee87523 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sun, 18 Jun 2017 10:25:34 +0900
Subject: [PATCH 22/95] Deprecate sphinx.ext.autodoc.add_documenter() and
AutoDirective._register
---
CHANGES | 2 ++
sphinx/application.py | 6 ++--
sphinx/ext/autodoc/__init__.py | 55 ++++++++++++++++++++++++++----
sphinx/ext/autosummary/__init__.py | 18 +++++-----
sphinx/ext/autosummary/generate.py | 38 ++++++++++++---------
sphinx/registry.py | 6 ++++
tests/py35/test_autodoc_py35.py | 8 ++---
tests/test_autodoc.py | 22 ++++++------
tests/test_ext_autosummary.py | 10 ++++--
tests/test_templating.py | 11 ++++--
10 files changed, 121 insertions(+), 55 deletions(-)
diff --git a/CHANGES b/CHANGES
index bd6a92728..c0934c396 100644
--- a/CHANGES
+++ b/CHANGES
@@ -21,6 +21,8 @@ Deprecated
values will be accepted at 2.0.
* ``format_annotation()`` and ``formatargspec()`` is deprecated. Please use
``sphinx.util.inspect.Signature`` instead.
+* ``sphinx.ext.autodoc.add_documenter()`` and ``AutoDirective._register`` is now
+ deprecated. Please use ``app.add_autodocumenter()``
Features added
--------------
diff --git a/sphinx/application.py b/sphinx/application.py
index 9195f11af..3bfea910f 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -642,9 +642,9 @@ class Sphinx(object):
def add_autodocumenter(self, cls):
# type: (Any) -> None
logger.debug('[app] adding autodocumenter: %r', cls)
- from sphinx.ext import autodoc
- autodoc.add_documenter(cls)
- self.add_directive('auto' + cls.objtype, autodoc.AutoDirective)
+ from sphinx.ext.autodoc import AutoDirective
+ self.add_directive('auto' + cls.objtype, AutoDirective)
+ self.registry.add_documenter(cls.objtype, cls)
def add_autodoc_attrgetter(self, type, getter):
# type: (Any, Callable) -> None
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index f9f7c5edc..a6afe4f4c 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -14,6 +14,7 @@
import re
import sys
import inspect
+import warnings
from six import iterkeys, iteritems, itervalues, text_type, class_types, string_types
@@ -23,6 +24,7 @@ from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
import sphinx
+from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.ext.autodoc.importer import mock, import_object
from sphinx.ext.autodoc.importer import _MockImporter # to keep compatibility # NOQA
from sphinx.ext.autodoc.inspector import format_annotation, formatargspec # to keep compatibility # NOQA
@@ -323,6 +325,14 @@ class Documenter(object):
# the module analyzer to get at attribute docs, or None
self.analyzer = None # type: Any
+ @property
+ def documenters(self):
+ # type: () -> Dict[unicode, Type[Documenter]]
+ """Returns registered Documenter classes"""
+ classes = dict(AutoDirective._registry) # registered directly
+ classes.update(self.env.app.registry.documenters) # registered by API
+ return classes
+
def add_line(self, line, source, *lineno):
# type: (unicode, unicode, int) -> None
"""Append one line of generated reST to the output."""
@@ -727,7 +737,7 @@ class Documenter(object):
# document non-skipped members
memberdocumenters = [] # type: List[Tuple[Documenter, bool]]
for (mname, member, isattr) in self.filter_members(members, want_all):
- classes = [cls for cls in itervalues(AutoDirective._registry)
+ classes = [cls for cls in itervalues(self.documenters)
if cls.can_document_member(member, mname, isattr, self)]
if not classes:
# don't know how to document this member
@@ -1463,11 +1473,28 @@ class InstanceAttributeDocumenter(AttributeDocumenter):
AttributeDocumenter.add_content(self, more_content, no_docstring=True)
+class DeprecatedDict(dict):
+ def __init__(self, message):
+ self.message = message
+ super(DeprecatedDict, self).__init__()
+
+ def __setitem__(self, key, value):
+ warnings.warn(self.message, RemovedInSphinx20Warning)
+ super(DeprecatedDict, self).__setitem__(key, value)
+
+ def setdefault(self, key, default=None):
+ warnings.warn(self.message, RemovedInSphinx20Warning)
+ super(DeprecatedDict, self).setdefault(key, default)
+
+ def update(self, other=None):
+ warnings.warn(self.message, RemovedInSphinx20Warning)
+ super(DeprecatedDict, self).update(other)
+
+
class AutoDirective(Directive):
"""
The AutoDirective class is used for all autodoc directives. It dispatches
- most of the work to one of the Documenters, which it selects through its
- *_registry* dictionary.
+ most of the work to one of the Documenters.
The *_special_attrgetters* attribute is used to customize ``getattr()``
calls that the Documenters make; its entries are of the form ``type:
@@ -1478,8 +1505,11 @@ class AutoDirective(Directive):
dictionary should include all necessary functions for accessing
attributes of the parents.
"""
- # a registry of objtype -> documenter class
- _registry = {} # type: Dict[unicode, Type[Documenter]]
+ # a registry of objtype -> documenter class (Deprecated)
+ _registry = DeprecatedDict(
+ 'AutoDirective._registry has been deprecated. '
+ 'Please use app.add_autodocumenter() instead.'
+ ) # type: Dict[unicode, Type[Documenter]]
# a registry of type -> getattr function
_special_attrgetters = {} # type: Dict[Type, Callable]
@@ -1521,7 +1551,7 @@ class AutoDirective(Directive):
# find out what documenter to call
objtype = self.name[4:]
- doc_class = self._registry[objtype]
+ doc_class = get_documenters(self.env.app)[objtype]
# add default flags
for flag in self._default_flags:
if flag not in doc_class.option_spec:
@@ -1575,6 +1605,10 @@ class AutoDirective(Directive):
def add_documenter(cls):
# type: (Type[Documenter]) -> None
"""Register a new Documenter."""
+ warnings.warn('sphinx.ext.autodoc.add_documenter() has been deprecated. '
+ 'Please use app.add_autodocumenter() instead.',
+ RemovedInSphinx20Warning)
+
if not issubclass(cls, Documenter):
raise ExtensionError('autodoc documenter %r must be a subclass '
'of Documenter' % cls)
@@ -1585,6 +1619,15 @@ def add_documenter(cls):
AutoDirective._registry[cls.objtype] = cls
+def get_documenters(app):
+ # type: (Sphinx) -> Dict[unicode, Type[Documenter]]
+ """Returns registered Documenter classes"""
+ classes = dict(AutoDirective._registry) # registered directly
+ if app:
+ classes.update(app.registry.documenters) # registered by API
+ return classes
+
+
def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
app.add_autodocumenter(ModuleDocumenter)
diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py
index 3dded11ff..af7da3b1e 100644
--- a/sphinx/ext/autosummary/__init__.py
+++ b/sphinx/ext/autosummary/__init__.py
@@ -72,8 +72,8 @@ from sphinx import addnodes
from sphinx.environment.adapters.toctree import TocTree
from sphinx.util import import_object, rst, logging
from sphinx.pycode import ModuleAnalyzer, PycodeError
-from sphinx.ext.autodoc import Options
from sphinx.ext.autodoc.importer import import_module
+from sphinx.ext.autodoc import Options, get_documenters
if False:
# For type annotation
@@ -158,8 +158,8 @@ class FakeDirective(object):
genopt = Options()
-def get_documenter(obj, parent):
- # type: (Any, Any) -> Type[Documenter]
+def get_documenter(app, obj, parent):
+ # type: (Sphinx, Any, Any) -> Type[Documenter]
"""Get an autodoc.Documenter class suitable for documenting the given
object.
@@ -167,8 +167,7 @@ def get_documenter(obj, parent):
another Python object (e.g. a module or a class) to which *obj*
belongs to.
"""
- from sphinx.ext.autodoc import AutoDirective, DataDocumenter, \
- ModuleDocumenter
+ from sphinx.ext.autodoc import DataDocumenter, ModuleDocumenter
if inspect.ismodule(obj):
# ModuleDocumenter.can_document_member always returns False
@@ -176,7 +175,7 @@ def get_documenter(obj, parent):
# Construct a fake documenter for *parent*
if parent is not None:
- parent_doc_cls = get_documenter(parent, None)
+ parent_doc_cls = get_documenter(app, parent, None)
else:
parent_doc_cls = ModuleDocumenter
@@ -186,7 +185,7 @@ def get_documenter(obj, parent):
parent_doc = parent_doc_cls(FakeDirective(), "")
# Get the corrent documenter class for *obj*
- classes = [cls for cls in AutoDirective._registry.values()
+ classes = [cls for cls in get_documenters(app).values()
if cls.can_document_member(obj, '', False, parent_doc)]
if classes:
classes.sort(key=lambda cls: cls.priority)
@@ -289,7 +288,7 @@ class Autosummary(Directive):
full_name = modname + '::' + full_name[len(modname) + 1:]
# NB. using full_name here is important, since Documenters
# handle module prefixes slightly differently
- documenter = get_documenter(obj, parent)(self, full_name)
+ documenter = get_documenter(self.env.app, obj, parent)(self, full_name)
if not documenter.parse_name():
self.warn('failed to parse name %s' % real_name)
items.append((display_name, '', '', real_name))
@@ -615,7 +614,8 @@ def process_generate_options(app):
generate_autosummary_docs(genfiles, builder=app.builder,
warn=logger.warning, info=logger.info,
- suffix=suffix, base_path=app.srcdir)
+ suffix=suffix, base_path=app.srcdir,
+ app=app)
def setup(app):
diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py
index 4db1a93e9..2873b6082 100644
--- a/sphinx/ext/autosummary/generate.py
+++ b/sphinx/ext/autosummary/generate.py
@@ -31,26 +31,13 @@ from jinja2.sandbox import SandboxedEnvironment
from sphinx import __display_version__
from sphinx import package_dir
+from sphinx.ext.autodoc import add_documenter
from sphinx.ext.autosummary import import_by_name, get_documenter
from sphinx.jinja2glue import BuiltinTemplateLoader
from sphinx.util.osutil import ensuredir
from sphinx.util.inspect import safe_getattr
from sphinx.util.rst import escape as rst_escape
-# Add documenters to AutoDirective registry
-from sphinx.ext.autodoc import add_documenter, \
- ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter, \
- FunctionDocumenter, MethodDocumenter, AttributeDocumenter, \
- InstanceAttributeDocumenter
-add_documenter(ModuleDocumenter)
-add_documenter(ClassDocumenter)
-add_documenter(ExceptionDocumenter)
-add_documenter(DataDocumenter)
-add_documenter(FunctionDocumenter)
-add_documenter(MethodDocumenter)
-add_documenter(AttributeDocumenter)
-add_documenter(InstanceAttributeDocumenter)
-
if False:
# For type annotation
from typing import Any, Callable, Dict, Tuple, List # NOQA
@@ -60,6 +47,22 @@ if False:
from sphinx.environment import BuildEnvironment # NOQA
+def setup_documenters():
+ from sphinx.ext.autodoc import (
+ ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
+ FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
+ InstanceAttributeDocumenter
+ )
+ add_documenter(ModuleDocumenter)
+ add_documenter(ClassDocumenter)
+ add_documenter(ExceptionDocumenter)
+ add_documenter(DataDocumenter)
+ add_documenter(FunctionDocumenter)
+ add_documenter(MethodDocumenter)
+ add_documenter(AttributeDocumenter)
+ add_documenter(InstanceAttributeDocumenter)
+
+
def _simple_info(msg):
# type: (unicode) -> None
print(msg)
@@ -81,7 +84,7 @@ def _underline(title, line='='):
def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
warn=_simple_warn, info=_simple_info,
base_path=None, builder=None, template_dir=None,
- imported_members=False):
+ imported_members=False, app=None):
# type: (List[unicode], unicode, unicode, Callable, Callable, unicode, Builder, unicode, bool) -> None # NOQA
showed_sources = list(sorted(sources))
@@ -148,7 +151,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
new_files.append(fn)
with open(fn, 'w') as f:
- doc = get_documenter(obj, parent)
+ doc = get_documenter(app, obj, parent)
if template_name is not None:
template = template_env.get_template(template_name)
@@ -167,7 +170,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
value = safe_getattr(obj, name)
except AttributeError:
continue
- documenter = get_documenter(value, obj)
+ documenter = get_documenter(app, value, obj)
if documenter.objtype == typ:
if typ == 'method':
items.append(name)
@@ -392,6 +395,7 @@ The format of the autosummary directive is documented in the
def main(argv=sys.argv[1:]):
# type: (List[str]) -> None
+ setup_documenters()
args = get_parser().parse_args(argv)
generate_autosummary_docs(args.source_file, args.output_dir,
'.' + args.suffix,
diff --git a/sphinx/registry.py b/sphinx/registry.py
index 6ec966a6a..38fe9caf3 100644
--- a/sphinx/registry.py
+++ b/sphinx/registry.py
@@ -38,6 +38,7 @@ if False:
from sphinx.builders import Builder # NOQA
from sphinx.domains import Domain, Index # NOQA
from sphinx.environment import BuildEnvironment # NOQA
+ from sphinx.ext.autodoc import Documenter # NOQA
from sphinx.util.typing import RoleFunction # NOQA
logger = logging.getLogger(__name__)
@@ -52,6 +53,7 @@ EXTENSION_BLACKLIST = {
class SphinxComponentRegistry(object):
def __init__(self):
self.builders = {} # type: Dict[unicode, Type[Builder]]
+ self.documenters = {} # type: Dict[unicode, Type[Documenter]]
self.domains = {} # type: Dict[unicode, Type[Domain]]
self.domain_directives = {} # type: Dict[unicode, Dict[unicode, Any]]
self.domain_indices = {} # type: Dict[unicode, List[Type[Index]]]
@@ -284,6 +286,10 @@ class SphinxComponentRegistry(object):
# type: () -> List[Type[Transform]]
return self.post_transforms
+ def add_documenter(self, objtype, documenter):
+ # type: (unicode, Type[Documenter]) -> None
+ self.documenters[objtype] = documenter
+
def load_extension(self, app, extname):
# type: (Sphinx, unicode) -> None
"""Load a Sphinx extension."""
diff --git a/tests/py35/test_autodoc_py35.py b/tests/py35/test_autodoc_py35.py
index ecb0a96af..d13e50d9c 100644
--- a/tests/py35/test_autodoc_py35.py
+++ b/tests/py35/test_autodoc_py35.py
@@ -112,7 +112,7 @@ def skip_member(app, what, name, obj, skip, options):
@pytest.mark.usefixtures('setup_test')
def test_generate():
def assert_warns(warn_str, objtype, name, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
assert len(directive.result) == 0, directive.result
assert len(_warnings) == 1, _warnings
@@ -120,7 +120,7 @@ def test_generate():
del _warnings[:]
def assert_works(objtype, name, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
assert directive.result
# print '\n'.join(directive.result)
@@ -134,7 +134,7 @@ def test_generate():
assert set(processed_docstrings) | set(processed_signatures) == set(items)
def assert_result_contains(item, objtype, name, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
# print '\n'.join(directive.result)
assert len(_warnings) == 0, _warnings
@@ -142,7 +142,7 @@ def test_generate():
del directive.result[:]
def assert_order(items, objtype, name, member_order, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.options.member_order = member_order
inst.generate(**kw)
assert len(_warnings) == 0, _warnings
diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py
index 61152ba02..1abd01b5f 100644
--- a/tests/test_autodoc.py
+++ b/tests/test_autodoc.py
@@ -121,7 +121,7 @@ def skip_member(app, what, name, obj, skip, options):
@pytest.mark.usefixtures('setup_test')
def test_parse_name():
def verify(objtype, name, result):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
assert inst.parse_name()
assert (inst.modname, inst.objpath, inst.args, inst.retann) == result
@@ -164,7 +164,7 @@ def test_parse_name():
@pytest.mark.usefixtures('setup_test')
def test_format_signature():
def formatsig(objtype, name, obj, args, retann):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.fullname = name
inst.doc_as_attr = False # for class objtype
inst.object = obj
@@ -270,7 +270,7 @@ def test_format_signature():
@pytest.mark.usefixtures('setup_test')
def test_get_doc():
def getdocl(objtype, obj, encoding=None):
- inst = AutoDirective._registry[objtype](directive, 'tmp')
+ inst = app.registry.documenters[objtype](directive, 'tmp')
inst.object = obj
inst.objpath = [obj.__name__]
inst.doc_as_attr = False
@@ -449,7 +449,7 @@ def test_get_doc():
@pytest.mark.usefixtures('setup_test')
def test_docstring_processing():
def process(objtype, name, obj):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.object = obj
inst.fullname = name
return list(inst.process_doc(inst.get_doc()))
@@ -506,7 +506,7 @@ def test_docstring_property_processing():
def genarate_docstring(objtype, name, **kw):
del processed_docstrings[:]
del processed_signatures[:]
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
results = list(directive.result)
docstrings = inst.get_doc()[0]
@@ -555,7 +555,7 @@ def test_new_documenter():
add_documenter(MyDocumenter)
def assert_result_contains(item, objtype, name, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
# print '\n'.join(directive.result)
assert len(_warnings) == 0, _warnings
@@ -581,7 +581,7 @@ def test_attrgetter_using():
AutoDirective._special_attrgetters[type] = special_getattr
del getattr_spy[:]
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
hooked_members = [s[1] for s in getattr_spy]
@@ -603,7 +603,7 @@ def test_attrgetter_using():
@pytest.mark.usefixtures('setup_test')
def test_generate():
def assert_warns(warn_str, objtype, name, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
assert len(directive.result) == 0, directive.result
assert len(_warnings) == 1, _warnings
@@ -611,7 +611,7 @@ def test_generate():
del _warnings[:]
def assert_works(objtype, name, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
assert directive.result
# print '\n'.join(directive.result)
@@ -625,7 +625,7 @@ def test_generate():
assert set(processed_docstrings) | set(processed_signatures) == set(items)
def assert_result_contains(item, objtype, name, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
# print '\n'.join(directive.result)
assert len(_warnings) == 0, _warnings
@@ -633,7 +633,7 @@ def test_generate():
del directive.result[:]
def assert_order(items, objtype, name, member_order, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.options.member_order = member_order
inst.generate(**kw)
assert len(_warnings) == 0, _warnings
diff --git a/tests/test_ext_autosummary.py b/tests/test_ext_autosummary.py
index 0aea99df6..1035d3b3b 100644
--- a/tests/test_ext_autosummary.py
+++ b/tests/test_ext_autosummary.py
@@ -57,10 +57,14 @@ def test_mangle_signature():
@pytest.mark.sphinx('dummy', **default_kw)
-def test_get_items_summary(app, status, warning):
+def test_get_items_summary(make_app, app_params):
+ import sphinx.ext.autosummary
+ import sphinx.ext.autosummary.generate
+ sphinx.ext.autosummary.generate.setup_documenters()
+ args, kwargs = app_params
+ app = make_app(*args, **kwargs)
# monkey-patch Autosummary.get_items so we can easily get access to it's
# results..
- import sphinx.ext.autosummary
orig_get_items = sphinx.ext.autosummary.Autosummary.get_items
autosummary_items = {}
@@ -81,7 +85,7 @@ def test_get_items_summary(app, status, warning):
finally:
sphinx.ext.autosummary.Autosummary.get_items = orig_get_items
- html_warnings = warning.getvalue()
+ html_warnings = app._warning.getvalue()
assert html_warnings == ''
expected_values = {
diff --git a/tests/test_templating.py b/tests/test_templating.py
index 341b33f51..88a196e77 100644
--- a/tests/test_templating.py
+++ b/tests/test_templating.py
@@ -10,10 +10,14 @@
"""
import pytest
+from sphinx.ext.autosummary.generate import setup_documenters
@pytest.mark.sphinx('html', testroot='templating')
-def test_layout_overloading(app, status, warning):
+def test_layout_overloading(make_app, app_params):
+ setup_documenters()
+ args, kwargs = app_params
+ app = make_app(*args, **kwargs)
app.builder.build_update()
result = (app.outdir / 'contents.html').text(encoding='utf-8')
@@ -22,7 +26,10 @@ def test_layout_overloading(app, status, warning):
@pytest.mark.sphinx('html', testroot='templating')
-def test_autosummary_class_template_overloading(app, status, warning):
+def test_autosummary_class_template_overloading(make_app, app_params):
+ setup_documenters()
+ args, kwargs = app_params
+ app = make_app(*args, **kwargs)
app.builder.build_update()
result = (app.outdir / 'generated' / 'sphinx.application.TemplateBridge.html').text(
From 4b51ed4aa9170265d682d120bb900e9fd94a21bc Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sun, 18 Jun 2017 19:21:46 +0900
Subject: [PATCH 23/95] Deprecate AutoDirective._special_attrgetters
---
CHANGES | 4 +++-
sphinx/application.py | 9 ++++-----
sphinx/ext/autodoc/__init__.py | 31 ++++++++++++++++++++-----------
sphinx/registry.py | 5 +++++
4 files changed, 32 insertions(+), 17 deletions(-)
diff --git a/CHANGES b/CHANGES
index c0934c396..ca0f3220a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -22,7 +22,9 @@ Deprecated
* ``format_annotation()`` and ``formatargspec()`` is deprecated. Please use
``sphinx.util.inspect.Signature`` instead.
* ``sphinx.ext.autodoc.add_documenter()`` and ``AutoDirective._register`` is now
- deprecated. Please use ``app.add_autodocumenter()``
+ deprecated. Please use ``app.add_autodocumenter()`` instead.
+* ``AutoDirective._special_attrgetters`` is now deprecated. Please use
+ ``app.add_autodoc_attrgetter()`` instead.
Features added
--------------
diff --git a/sphinx/application.py b/sphinx/application.py
index 3bfea910f..07b8539a9 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -646,11 +646,10 @@ class Sphinx(object):
self.add_directive('auto' + cls.objtype, AutoDirective)
self.registry.add_documenter(cls.objtype, cls)
- def add_autodoc_attrgetter(self, type, getter):
- # type: (Any, Callable) -> None
- logger.debug('[app] adding autodoc attrgetter: %r', (type, getter))
- from sphinx.ext import autodoc
- autodoc.AutoDirective._special_attrgetters[type] = getter
+ def add_autodoc_attrgetter(self, typ, getter):
+ # type: (Type, Callable[[Any, unicode, Any], Any]) -> None
+ logger.debug('[app] adding autodoc attrgetter: %r', (typ, getter))
+ self.registy.add_autodoc_attrgetter(typ, getter)
def add_search_language(self, cls):
# type: (Any) -> None
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index a6afe4f4c..14572e283 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -285,14 +285,10 @@ class Documenter(object):
option_spec = {'noindex': bool_option} # type: Dict[unicode, Callable]
- @staticmethod
- def get_attr(obj, name, *defargs):
+ def get_attr(self, obj, name, *defargs):
# type: (Any, unicode, Any) -> Any
"""getattr() override for types such as Zope interfaces."""
- for typ, func in iteritems(AutoDirective._special_attrgetters):
- if isinstance(obj, typ):
- return func(obj, name, *defargs)
- return safe_getattr(obj, name, *defargs)
+ return autodoc_attrgetter(self.env.app, obj, name, *defargs)
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
@@ -1496,10 +1492,6 @@ class AutoDirective(Directive):
The AutoDirective class is used for all autodoc directives. It dispatches
most of the work to one of the Documenters.
- The *_special_attrgetters* attribute is used to customize ``getattr()``
- calls that the Documenters make; its entries are of the form ``type:
- getattr_function``.
-
Note: When importing an object, all items along the import chain are
accessed using the descendant's *_special_attrgetters*, thus this
dictionary should include all necessary functions for accessing
@@ -1512,7 +1504,10 @@ class AutoDirective(Directive):
) # type: Dict[unicode, Type[Documenter]]
# a registry of type -> getattr function
- _special_attrgetters = {} # type: Dict[Type, Callable]
+ _special_attrgetters = DeprecatedDict(
+ 'AutoDirective._special_attrgetters has been deprecated. '
+ 'Please use app.add_autodoc_attrgetter() instead.'
+ ) # type: Dict[Type, Callable]
# flags that can be given in autodoc_default_flags
_default_flags = set([
@@ -1628,6 +1623,20 @@ def get_documenters(app):
return classes
+def autodoc_attrgetter(app, obj, name, *defargs):
+ # type: (Sphinx, Any, unicode, Any) -> Any
+ """Alternative getattr() for types"""
+ candidates = dict(AutoDirective._special_attrgetters)
+ if app:
+ candidates.update(app.registry.autodoc_attrgettrs)
+
+ for typ, func in iteritems(candidates):
+ if isinstance(obj, typ):
+ return func(obj, name, *defargs)
+
+ return safe_getattr(obj, name, *defargs)
+
+
def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
app.add_autodocumenter(ModuleDocumenter)
diff --git a/sphinx/registry.py b/sphinx/registry.py
index 38fe9caf3..e48c12f96 100644
--- a/sphinx/registry.py
+++ b/sphinx/registry.py
@@ -52,6 +52,7 @@ EXTENSION_BLACKLIST = {
class SphinxComponentRegistry(object):
def __init__(self):
+ self.autodoc_attrgettrs = {} # type: Dict[Type, Callable[[Any, unicode, Any], Any]]
self.builders = {} # type: Dict[unicode, Type[Builder]]
self.documenters = {} # type: Dict[unicode, Type[Documenter]]
self.domains = {} # type: Dict[unicode, Type[Domain]]
@@ -290,6 +291,10 @@ class SphinxComponentRegistry(object):
# type: (unicode, Type[Documenter]) -> None
self.documenters[objtype] = documenter
+ def add_autodoc_attrgetter(self, typ, attrgetter):
+ # type: (Type, Callable[[Any, unicode, Any], Any]) -> None
+ self.autodoc_attrgettrs[typ] = attrgetter
+
def load_extension(self, app, extname):
# type: (Sphinx, unicode) -> None
"""Load a Sphinx extension."""
From bf0153d74850cc9d498a0ed152a0d3d6e7e49f96 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Thu, 22 Jun 2017 01:20:18 +0900
Subject: [PATCH 24/95] autodoc: Refactor get_object_members()
---
sphinx/ext/autodoc/__init__.py | 63 ++++++++--------------------------
sphinx/ext/autodoc/importer.py | 33 +++++++++++++++++-
2 files changed, 47 insertions(+), 49 deletions(-)
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 14572e283..16abb8c71 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -16,7 +16,7 @@ import sys
import inspect
import warnings
-from six import iterkeys, iteritems, itervalues, text_type, class_types, string_types
+from six import iteritems, itervalues, text_type, class_types, string_types
from docutils import nodes
from docutils.utils import assemble_option_dict
@@ -25,7 +25,7 @@ from docutils.statemachine import ViewList
import sphinx
from sphinx.deprecation import RemovedInSphinx20Warning
-from sphinx.ext.autodoc.importer import mock, import_object
+from sphinx.ext.autodoc.importer import mock, import_object, get_object_members
from sphinx.ext.autodoc.importer import _MockImporter # to keep compatibility # NOQA
from sphinx.ext.autodoc.inspector import format_annotation, formatargspec # to keep compatibility # NOQA
from sphinx.util import rpartition, force_decode
@@ -36,7 +36,7 @@ from sphinx.util import logging
from sphinx.util.nodes import nested_parse_with_titles
from sphinx.util.inspect import Signature, isdescriptor, safe_getmembers, \
safe_getattr, object_description, is_builtin_class_method, \
- isenumclass, isenumattribute, getdoc
+ isenumattribute, getdoc
from sphinx.util.docstrings import prepare_docstring
if False:
@@ -570,57 +570,24 @@ class Documenter(object):
If *want_all* is True, return all members. Else, only return those
members given by *self.options.members* (which may also be none).
"""
- analyzed_member_names = set()
- if self.analyzer:
- attr_docs = self.analyzer.find_attr_docs()
- namespace = '.'.join(self.objpath)
- for item in iteritems(attr_docs):
- if item[0][0] == namespace:
- analyzed_member_names.add(item[0][1])
+ members = get_object_members(self.object, self.objpath, self.get_attr, self.analyzer)
if not want_all:
if not self.options.members:
return False, []
# specific members given
- members = []
- for mname in self.options.members:
- try:
- members.append((mname, self.get_attr(self.object, mname)))
- except AttributeError:
- if mname not in analyzed_member_names:
- self.directive.warn('missing attribute %s in object %s'
- % (mname, self.fullname))
+ selected = []
+ for name in self.options.members:
+ if name in members:
+ selected.append((name, members[name].value))
+ else:
+ self.directive.warn('missing attribute %s in object %s' %
+ (name, self.fullname))
+ return False, sorted(selected)
elif self.options.inherited_members:
- # safe_getmembers() uses dir() which pulls in members from all
- # base classes
- members = safe_getmembers(self.object, attr_getter=self.get_attr)
+ return False, sorted((m.name, m.value) for m in itervalues(members))
else:
- # __dict__ contains only the members directly defined in
- # the class (but get them via getattr anyway, to e.g. get
- # unbound method objects instead of function objects);
- # using list(iterkeys()) because apparently there are objects for which
- # __dict__ changes while getting attributes
- try:
- obj_dict = self.get_attr(self.object, '__dict__')
- except AttributeError:
- members = []
- else:
- members = [(mname, self.get_attr(self.object, mname, None))
- for mname in list(iterkeys(obj_dict))]
-
- # Py34 doesn't have enum members in __dict__.
- if isenumclass(self.object):
- members.extend(
- item for item in self.object.__members__.items()
- if item not in members
- )
-
- membernames = set(m[0] for m in members)
- # add instance attributes from the analyzer
- for aname in analyzed_member_names:
- if aname not in membernames and \
- (want_all or aname in self.options.members):
- members.append((aname, INSTANCEATTR))
- return False, sorted(members)
+ return False, sorted((m.name, m.value) for m in itervalues(members)
+ if m.directly_defined)
def filter_members(self, members, want_all):
# type: (List[Tuple[unicode, Any]], bool) -> List[Tuple[unicode, Any, bool]]
diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py
index 95ca58d0b..b22af9ff5 100644
--- a/sphinx/ext/autodoc/importer.py
+++ b/sphinx/ext/autodoc/importer.py
@@ -13,6 +13,7 @@ import sys
import warnings
import traceback
import contextlib
+from collections import namedtuple
from types import FunctionType, MethodType, ModuleType
from six import PY2
@@ -22,7 +23,7 @@ from sphinx.util.inspect import safe_getattr
if False:
# For type annotation
- from typing import Any, Callable, Generator, List, Set # NOQA
+ from typing import Any, Callable, Dict, Generator, List, Optional, Set # NOQA
logger = logging.getLogger(__name__)
@@ -194,3 +195,33 @@ def import_object(modname, objpath, objtype='', attrgetter=safe_getattr, warning
errmsg = errmsg.decode('utf-8') # type: ignore
logger.debug(errmsg)
raise ImportError(errmsg)
+
+
+Attribute = namedtuple('Attribute', ['name', 'directly_defined', 'value'])
+
+
+def get_object_members(subject, objpath, attrgetter, analyzer=None):
+ # type: (Any, List[unicode], Callable, Any) -> Dict[str, Attribute] # NOQA
+ """Get members and attributes of target object."""
+ # the members directly defined in the class
+ obj_dict = attrgetter(subject, '__dict__', {})
+
+ members = {}
+ for name in dir(subject):
+ try:
+ value = attrgetter(subject, name)
+ directly_defined = name in obj_dict
+ members[name] = Attribute(name, directly_defined, value)
+ except AttributeError:
+ continue
+
+ if analyzer:
+ # append instance attributes (cf. self.attr1) if analyzer knows
+ from sphinx.ext.autodoc import INSTANCEATTR
+
+ namespace = '.'.join(objpath)
+ for (ns, name) in analyzer.find_attr_docs():
+ if namespace == ns and name not in members:
+ members[name] = Attribute(name, True, INSTANCEATTR)
+
+ return members
From 69f39e44d91c0eb10596448f38fb1aafcbf2054a Mon Sep 17 00:00:00 2001
From: Stephen Finucane
Date: Sun, 24 Dec 2017 20:05:13 +0000
Subject: [PATCH 25/95] tox: Enable 'skipsdist'
Given that we install Sphinx as part of the dependencies, there's no
reason to do it twice. Skip that step.
Signed-off-by: Stephen Finucane
---
tox.ini | 1 +
1 file changed, 1 insertion(+)
diff --git a/tox.ini b/tox.ini
index 530e5b941..9c0f2d2f3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,6 @@
[tox]
envlist=flake8,py27,py34,py35,py36,pypy,du13,du12,du11
+skipsdist = True
[testenv]
deps=
From f21fe6f24f879fcb640e9d9711eeb777a57b10a9 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 1 Jan 2018 23:03:15 +0900
Subject: [PATCH 26/95] Revert "tox: Enable 'skipsdist'"
This reverts commit 69f39e44d91c0eb10596448f38fb1aafcbf2054a.
---
tox.ini | 1 -
1 file changed, 1 deletion(-)
diff --git a/tox.ini b/tox.ini
index 9c0f2d2f3..530e5b941 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,5 @@
[tox]
envlist=flake8,py27,py34,py35,py36,pypy,du13,du12,du11
-skipsdist = True
[testenv]
deps=
From 850e9a9c5cbd18163eaa8861c7fecbdfb9921a2b Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 1 Jan 2018 23:29:10 +0900
Subject: [PATCH 27/95] Fix links to external option docs with intersphinx
(refs: #3769)
---
CHANGES | 1 +
sphinx/domains/std.py | 16 +++++++++++-----
sphinx/ext/intersphinx.py | 1 +
tests/test_ext_intersphinx.py | 8 +++++++-
4 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/CHANGES b/CHANGES
index bb11d643d..39288bea7 100644
--- a/CHANGES
+++ b/CHANGES
@@ -38,6 +38,7 @@ Bugs fixed
* #4315: For PDF 'howto' documents, ``latex_toplevel_sectioning='part'`` generates
``\chapter`` commands
* #4214: Two todolist directives break sphinx-1.6.5
+* Fix links to external option docs with intersphinx (refs: #3769)
Testing
--------
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
index 27bb88c96..68baa04aa 100644
--- a/sphinx/domains/std.py
+++ b/sphinx/domains/std.py
@@ -959,12 +959,18 @@ class StandardDomain(Domain):
def get_full_qualified_name(self, node):
# type: (nodes.Node) -> unicode
- progname = node.get('std:program')
- target = node.get('reftarget')
- if progname is None or target is None:
- return None
+ if node.get('reftype') == 'option':
+ progname = node.get('std:program')
+ command = ws_re.split(node.get('reftarget'))
+ if progname:
+ command.insert(0, progname)
+ option = command.pop()
+ if command:
+ return '.'.join(['-'.join(command), option])
+ else:
+ return None
else:
- return '.'.join([progname, target])
+ return None
def setup(app):
diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py
index ccd2c9321..9336c4b53 100644
--- a/sphinx/ext/intersphinx.py
+++ b/sphinx/ext/intersphinx.py
@@ -304,6 +304,7 @@ def missing_reference(app, env, node, contnode):
in_set = setname
to_try.append((inventories.named_inventory[setname], newtarget))
if domain:
+ node['reftarget'] = newtarget
full_qualified_name = env.get_domain(domain).get_full_qualified_name(node)
if full_qualified_name:
to_try.append((inventories.named_inventory[setname], full_qualified_name))
diff --git a/tests/test_ext_intersphinx.py b/tests/test_ext_intersphinx.py
index 371f296ea..a978928d4 100644
--- a/tests/test_ext_intersphinx.py
+++ b/tests/test_ext_intersphinx.py
@@ -194,7 +194,7 @@ def test_missing_reference_stddomain(tempdir, app, status, warning):
inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2)
app.config.intersphinx_mapping = {
- 'https://docs.python.org/': inv_file,
+ 'cmd': ('https://docs.python.org/', inv_file),
}
app.config.intersphinx_cache_limit = 0
@@ -213,6 +213,12 @@ def test_missing_reference_stddomain(tempdir, app, status, warning):
rn = missing_reference(app, app.env, node, contnode)
assert rn.astext() == 'ls -l'
+ # refers inventory by name
+ kwargs = {}
+ node, contnode = fake_node('std', 'option', 'cmd:ls -l', '-l', **kwargs)
+ rn = missing_reference(app, app.env, node, contnode)
+ assert rn.astext() == '-l'
+
@pytest.mark.sphinx('html', testroot='ext-intersphinx-cppdomain')
def test_missing_reference_cppdomain(tempdir, app, status, warning):
From cb860f0d306b3dc5a8688e3c88035b060fc0882f Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Tue, 2 Jan 2018 01:57:41 +0900
Subject: [PATCH 28/95] Fix #4091: Private members not documented without
:undoc-members:
---
CHANGES | 1 +
sphinx/ext/autodoc.py | 3 +--
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/CHANGES b/CHANGES
index 39288bea7..1e1379178 100644
--- a/CHANGES
+++ b/CHANGES
@@ -39,6 +39,7 @@ Bugs fixed
``\chapter`` commands
* #4214: Two todolist directives break sphinx-1.6.5
* Fix links to external option docs with intersphinx (refs: #3769)
+* #4091: Private members not documented without :undoc-members:
Testing
--------
diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py
index e04b4a09d..bd686644c 100644
--- a/sphinx/ext/autodoc.py
+++ b/sphinx/ext/autodoc.py
@@ -965,8 +965,7 @@ class Documenter(object):
elif (namespace, membername) in attr_docs:
if want_all and membername.startswith('_'):
# ignore members whose name starts with _ by default
- keep = self.options.private_members and \
- (has_doc or self.options.undoc_members)
+ keep = self.options.private_members
else:
# keep documented attributes
keep = True
From 6fa344c951a8c9c67c4fd7492a758f8e70ee3c4f Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Wed, 3 Jan 2018 20:15:20 +0900
Subject: [PATCH 29/95] Show traceback if conf.py raises an exception (refs:
#4369)
---
CHANGES | 1 +
sphinx/config.py | 4 ++++
2 files changed, 5 insertions(+)
diff --git a/CHANGES b/CHANGES
index 39288bea7..a6caf7031 100644
--- a/CHANGES
+++ b/CHANGES
@@ -17,6 +17,7 @@ Features added
* ``VerbatimHighlightColor`` is a new
:ref:`LaTeX 'sphinxsetup' ` key (refs: #4285)
* Easier customizability of LaTeX macros involved in rendering of code-blocks
+* Show traceback if conf.py raises an exception (refs: #4369)
Bugs fixed
----------
diff --git a/sphinx/config.py b/sphinx/config.py
index d3468b0a5..50f7c018c 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -10,6 +10,7 @@
"""
import re
+import traceback
from os import path, getenv
from six import PY2, PY3, iteritems, string_types, binary_type, text_type, integer_types
@@ -35,6 +36,7 @@ copyright_year_re = re.compile(r'^((\d{4}-)?)(\d{4})(?=[ ,])')
CONFIG_SYNTAX_ERROR = "There is a syntax error in your configuration file: %s"
if PY3:
CONFIG_SYNTAX_ERROR += "\nDid you change the syntax from 2.x to 3.x?"
+CONFIG_ERROR = "There is a programable error in your configuration file:\n\n%s"
CONFIG_EXIT_ERROR = "The configuration file (or one of the modules it imports) " \
"called sys.exit()"
CONFIG_ENUM_WARNING = "The config value `{name}` has to be a one of {candidates}, " \
@@ -152,6 +154,8 @@ class Config(object):
raise ConfigError(CONFIG_SYNTAX_ERROR % err)
except SystemExit:
raise ConfigError(CONFIG_EXIT_ERROR)
+ except Exception:
+ raise ConfigError(CONFIG_ERROR % traceback.format_exc())
self._raw_config = config
# these two must be preinitialized because extensions can add their
From b04151bca8d43aceb8a0bf018fb736cb215d0729 Mon Sep 17 00:00:00 2001
From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
Date: Fri, 5 Jan 2018 01:41:01 +0100
Subject: [PATCH 30/95] improved sidebar search field style
---
sphinx/themes/basic/searchbox.html | 6 ++++--
sphinx/themes/basic/static/basic.css_t | 14 +++++++++++++-
sphinx/themes/nature/static/nature.css_t | 7 ++-----
sphinx/themes/pyramid/static/pyramid.css_t | 7 ++-----
4 files changed, 21 insertions(+), 13 deletions(-)
diff --git a/sphinx/themes/basic/searchbox.html b/sphinx/themes/basic/searchbox.html
index a8ea03cc8..506877410 100644
--- a/sphinx/themes/basic/searchbox.html
+++ b/sphinx/themes/basic/searchbox.html
@@ -10,12 +10,14 @@
{%- if pagename != "search" and builder != "singlehtml" %}
{{ _('Quick search') }}
+
{%- endif %}
diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t
index 745864e28..efb997d8f 100644
--- a/sphinx/themes/basic/static/basic.css_t
+++ b/sphinx/themes/basic/static/basic.css_t
@@ -82,9 +82,21 @@ div.sphinxsidebar input {
}
div.sphinxsidebar #searchbox input[type="text"] {
- width: 170px;
+ float: left;
+ width: 80%;
+ padding: 0.25em;
+ box-sizing: border-box;
}
+div.sphinxsidebar #searchbox input[type="submit"] {
+ float: left;
+ width: 20%;
+ border-left: none;
+ padding: 0.25em;
+ box-sizing: border-box;
+}
+
+
img {
border: 0;
max-width: 100%;
diff --git a/sphinx/themes/nature/static/nature.css_t b/sphinx/themes/nature/static/nature.css_t
index 5751bf940..ff2b1d5ff 100644
--- a/sphinx/themes/nature/static/nature.css_t
+++ b/sphinx/themes/nature/static/nature.css_t
@@ -125,14 +125,11 @@ div.sphinxsidebar input {
font-size: 1em;
}
-div.sphinxsidebar input[type=text]{
+div.sphinxsidebar .searchformwrapper {
margin-left: 20px;
+ margin-right: 20px;
}
-div.sphinxsidebar input[type=submit]{
- margin-left: 20px;
-}
-
/* -- body styles ----------------------------------------------------------- */
a {
diff --git a/sphinx/themes/pyramid/static/pyramid.css_t b/sphinx/themes/pyramid/static/pyramid.css_t
index 93799111e..792f45452 100644
--- a/sphinx/themes/pyramid/static/pyramid.css_t
+++ b/sphinx/themes/pyramid/static/pyramid.css_t
@@ -148,12 +148,9 @@ div.sphinxsidebar input {
font-size: 1em;
}
-div.sphinxsidebar input[type=text]{
- margin-left: 20px;
-}
-
-div.sphinxsidebar input[type=submit]{
+div.sphinxsidebar .searchformwrapper {
margin-left: 20px;
+ margin-right: 20px;
}
/* -- sidebars -------------------------------------------------------------- */
From e3efe5884b08e4e4994d58eaf9c645b8f9567547 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Fri, 5 Jan 2018 22:25:34 +0900
Subject: [PATCH 31/95] Fix #4378: tox: use usedevelop option instead skipsdist
---
tox.ini | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tox.ini b/tox.ini
index ae6b2a4b3..2862fd755 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,9 +1,9 @@
[tox]
minversion = 2.0
envlist = docs,flake8,mypy,coverage,py{27,34,35,36,py},du{11,12,13,14}
-skipsdist = True
[testenv]
+usedevelop = True
passenv =
https_proxy http_proxy no_proxy PERL PERL5LIB
description =
From bd139453c96864646c0b7b1bf2f30b12587cb235 Mon Sep 17 00:00:00 2001
From: jfbu
Date: Fri, 5 Jan 2018 15:06:10 +0100
Subject: [PATCH 32/95] Move SphinxSmartQuotes transform to
SphinxStandaloneReader
closes #4142
closes #4357
closes #4359
refs: #3967
Adds ``smartquotes``, ``smartquotes_action``, ``smartquotes_excludes``
configuration variables.
- if ``smartquotes`` is set to False, then Smart Quotes transform is not
applied even if a Docutils configuration file activates it,
- the current default of ``smartquotes_excludes`` deactivates Smart
Quotes for Japanese language, and also for the ``man`` and ``text``
builders.
However, currently ``make text html`` deactivates Smart Quotes for
``html`` too, and ``make html text`` activates them for ``text`` too,
because the picked environment is shared and already transformed.
- now Smart Quotes get applied also when source documents are in
Markdown or other formats.
---
doc/config.rst | 65 ++++++++++++++++++++++++++++++----
sphinx/config.py | 5 +++
sphinx/environment/__init__.py | 48 ++++++++++++++++++++-----
sphinx/io.py | 12 ++++++-
sphinx/parsers.py | 7 ++--
5 files changed, 117 insertions(+), 20 deletions(-)
diff --git a/doc/config.rst b/doc/config.rst
index 1f222451d..6b7690c76 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -353,6 +353,63 @@ General configuration
.. versionadded:: 1.3
+.. confval:: smartquotes
+
+ If true, the `Docutils Smart Quotes transform`__, originally based on
+ `SmartyPants`__ (limited to English) and currently applying to many
+ languages, will be used to convert quotes and dashes to typographically
+ correct entities. Default: ``True``.
+
+ __ http://docutils.sourceforge.net/docs/user/smartquotes.html
+ __ https://daringfireball.net/projects/smartypants/
+
+ .. versionadded:: 1.6.6
+ It replaces deprecated :confval:`html_use_smartypants`.
+ It applies by default to all builders except ``man`` and ``text``
+ (see :confval:`smartquotes_excludes`.)
+
+ A `docutils.conf`__ file located in the configuration directory (or a
+ global :file:`~/.docutils` file) is obeyed unconditionally if it
+ *deactivates* smart quotes via the corresponding `Docutils option`__. But
+ if it *activates* them, then :confval:`smartquotes` does prevail.
+
+ __ http://docutils.sourceforge.net/docs/user/config.html
+ __ http://docutils.sourceforge.net/docs/user/config.html#smart-quotes
+
+.. confval:: smartquotes_action
+
+ This string, for use with Docutils ``0.14`` or later, customizes the Smart
+ Quotes transform. See the file :file:`smartquotes.py` at the `Docutils
+ repository`__ for details. The default ``'qDe'`` educates normal **q**\
+ uote characters ``"``, ``'``, em- and en-**D**\ ashes ``---``, ``--``, and
+ **e**\ llipses ``...``.
+
+ .. versionadded:: 1.6.6
+
+ __ https://sourceforge.net/p/docutils/code/HEAD/tree/trunk/docutils/
+
+.. confval:: smartquotes_excludes
+
+ This is a ``dict`` whose default is::
+
+ {'languages': ['ja'], 'builders': ['man', 'text']}
+
+ Each entry gives a sufficient condition to ignore the
+ :confval:`smartquotes` setting and deactivate the Smart Quotes transform.
+ Accepted keys are as above ``'builders'`` or ``'languages'``.
+ The values are lists.
+
+ .. note:: Currently, in case of invocation of :program:`make` with multiple
+ targets, the first target name is the only one which is tested against
+ the ``'builders'`` entry and it decides for all. Also, a ``make text``
+ following ``make html`` needs to be issued in the form ``make text
+ O="-E"`` to force re-parsing of source files, as the cached ones are
+ already transformed. On the other hand the issue does not arise with
+ direct usage of :program:`sphinx-build` as it caches
+ (in its default usage) the parsed source files in per builder locations.
+
+ .. versionadded:: 1.6.6
+
.. confval:: tls_verify
If true, Sphinx verifies server certifications. Default is ``True``.
@@ -784,15 +841,11 @@ that use Sphinx's HTMLWriter class.
.. confval:: html_use_smartypants
- If true, `SmartyPants `_
- will be used to convert quotes and dashes to typographically correct
+ If true, quotes and dashes are converted to typographically correct
entities. Default: ``True``.
.. deprecated:: 1.6
- To disable or customize smart quotes, use the Docutils configuration file
- (``docutils.conf``) instead to set there its `smart_quotes option`_.
-
- .. _`smart_quotes option`: http://docutils.sourceforge.net/docs/user/config.html#smart-quotes
+ To disable smart quotes, use rather :confval:`smartquotes`.
.. confval:: html_add_permalinks
diff --git a/sphinx/config.py b/sphinx/config.py
index 50f7c018c..a6632807c 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -134,6 +134,11 @@ class Config(object):
tls_verify = (True, 'env'),
tls_cacerts = (None, 'env'),
+ smartquotes = (True, 'env'),
+ smartquotes_action = ('qDe', 'env'),
+ smartquotes_excludes = ({'languages': ['ja'],
+ 'builders': ['man', 'text']},
+ 'env'),
) # type: Dict[unicode, Tuple]
def __init__(self, dirname, filename, overrides, tags):
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index 65a73b019..781382a30 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -20,8 +20,9 @@ import warnings
from os import path
from copy import copy
from collections import defaultdict
+from contextlib import contextmanager
-from six import BytesIO, itervalues, class_types, next
+from six import BytesIO, itervalues, class_types, next, iteritems
from six.moves import cPickle as pickle
from docutils.io import NullOutput
@@ -46,7 +47,7 @@ from sphinx.util.parallel import ParallelTasks, parallel_available, make_chunks
from sphinx.util.websupport import is_commentable
from sphinx.errors import SphinxError, ExtensionError
from sphinx.locale import __
-from sphinx.transforms import SphinxTransformer
+from sphinx.transforms import SphinxTransformer, SphinxSmartQuotes
from sphinx.versioning import add_uids, merge_doctrees
from sphinx.deprecation import RemovedInSphinx17Warning, RemovedInSphinx20Warning
from sphinx.environment.adapters.indexentries import IndexEntries
@@ -54,7 +55,7 @@ from sphinx.environment.adapters.toctree import TocTree
if False:
# For type annotation
- from typing import Any, Callable, Dict, IO, Iterator, List, Pattern, Set, Tuple, Type, Union # NOQA
+ from typing import Any, Callable, Dict, IO, Iterator, List, Pattern, Set, Tuple, Type, Union, Generator # NOQA
from docutils import nodes # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
@@ -91,6 +92,22 @@ versioning_conditions = {
} # type: Dict[unicode, Union[bool, Callable]]
+@contextmanager
+def sphinx_smartquotes_action(env):
+ # type: (BuildEnvironment) -> Generator
+ if not hasattr(SphinxSmartQuotes, 'smartquotes_action'):
+ # less than docutils-0.14
+ yield
+ else:
+ # docutils-0.14 or above
+ try:
+ original = SphinxSmartQuotes.smartquotes_action
+ SphinxSmartQuotes.smartquotes_action = env.config.smartquotes_action
+ yield
+ finally:
+ SphinxSmartQuotes.smartquotes_action = original
+
+
class NoUri(Exception):
"""Raised by get_relative_uri if there is no URI available."""
pass
@@ -600,7 +617,8 @@ class BuildEnvironment(object):
# remove all inventory entries for that file
app.emit('env-purge-doc', self, docname)
self.clear_doc(docname)
- self.read_doc(docname, app)
+ with sphinx_smartquotes_action(self):
+ self.read_doc(docname, app)
def _read_parallel(self, docnames, app, nproc):
# type: (List[unicode], Sphinx, int) -> None
@@ -612,8 +630,9 @@ class BuildEnvironment(object):
def read_process(docs):
# type: (List[unicode]) -> unicode
self.app = app
- for docname in docs:
- self.read_doc(docname, app)
+ with sphinx_smartquotes_action(self):
+ for docname in docs:
+ self.read_doc(docname, app)
# allow pickling self to send it back
return BuildEnvironment.dumps(self)
@@ -677,15 +696,26 @@ class BuildEnvironment(object):
language = self.config.language or 'en'
self.settings['language_code'] = language
if 'smart_quotes' not in self.settings:
- self.settings['smart_quotes'] = True
+ self.settings['smart_quotes'] = self.config.smartquotes
if self.config.html_use_smartypants is not None:
warnings.warn("html_use_smartypants option is deprecated. Smart "
"quotes are on by default; if you want to disable "
- "or customize them, use the smart_quotes option in "
- "docutils.conf.",
+ "them, use the smartquotes option.",
RemovedInSphinx17Warning)
self.settings['smart_quotes'] = self.config.html_use_smartypants
+ # some conditions exclude smart quotes, overriding smart_quotes
+ for valname, vallist in iteritems(self.config.smartquotes_excludes):
+ if valname == 'builders':
+ # this will work only for checking first build target
+ if self.app.builder.name in vallist:
+ self.settings['smart_quotes'] = False
+ break
+ elif valname == 'languages':
+ if self.config.language in vallist:
+ self.settings['smart_quotes'] = False
+ break
+
# confirm selected language supports smart_quotes or not
for tag in normalize_language_tag(language):
if tag in smartchars.quotes:
diff --git a/sphinx/io.py b/sphinx/io.py
index 8365e22e0..6fcec2cd3 100644
--- a/sphinx/io.py
+++ b/sphinx/io.py
@@ -18,7 +18,7 @@ from sphinx.transforms import (
ApplySourceWorkaround, ExtraTranslatableNodes, CitationReferences,
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, SortIds,
AutoNumbering, AutoIndexUpgrader, FilterSystemMessages,
- UnreferencedFootnotesDetector
+ UnreferencedFootnotesDetector, SphinxSmartQuotes
)
from sphinx.transforms.compact_bullet_list import RefOnlyBulletListTransform
from sphinx.transforms.i18n import (
@@ -98,6 +98,16 @@ class SphinxStandaloneReader(SphinxBaseReader):
RemoveTranslatableInline, PreserveTranslatableMessages, FilterSystemMessages,
RefOnlyBulletListTransform, UnreferencedFootnotesDetector]
+ def __init__(self, app, parsers={}, *args, **kwargs):
+ SphinxBaseReader.__init__(self, app, parsers, *args, **kwargs)
+ self.smart_quotes = app.env.settings['smart_quotes']
+
+ def get_transforms(self):
+ transforms = SphinxBaseReader.get_transforms(self)
+ if self.smart_quotes:
+ transforms.append(SphinxSmartQuotes)
+ return transforms
+
class SphinxI18nReader(SphinxBaseReader):
"""
diff --git a/sphinx/parsers.py b/sphinx/parsers.py
index b58eefa23..1aa16a45e 100644
--- a/sphinx/parsers.py
+++ b/sphinx/parsers.py
@@ -13,8 +13,6 @@ import docutils.parsers
import docutils.parsers.rst
from docutils.transforms.universal import SmartQuotes
-from sphinx.transforms import SphinxSmartQuotes
-
if False:
# For type annotation
from typing import Any, Dict, List, Type # NOQA
@@ -60,10 +58,11 @@ class RSTParser(docutils.parsers.rst.Parser):
def get_transforms(self):
# type: () -> List[Type[Transform]]
- """Sphinx's reST parser replaces a transform class for smart-quotes by own's"""
+ """Sphinx's reST parser replaces a transform class for smart-quotes by own's
+
+ refs: sphinx.io.SphinxStandaloneReader"""
transforms = docutils.parsers.rst.Parser.get_transforms(self)
transforms.remove(SmartQuotes)
- transforms.append(SphinxSmartQuotes)
return transforms
From 0d824dfd41302bcea5d8de831bc5f1025c465c1d Mon Sep 17 00:00:00 2001
From: jfbu
Date: Fri, 5 Jan 2018 15:06:38 +0100
Subject: [PATCH 33/95] Update CHANGES for PR #4360
---
CHANGES | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/CHANGES b/CHANGES
index 181271868..80f8c4faa 100644
--- a/CHANGES
+++ b/CHANGES
@@ -18,6 +18,10 @@ Features added
:ref:`LaTeX 'sphinxsetup' ` key (refs: #4285)
* Easier customizability of LaTeX macros involved in rendering of code-blocks
* Show traceback if conf.py raises an exception (refs: #4369)
+* Add :confval:`smartquotes` to disable smart quotes through ``conf.py``
+ (refs: #3967)
+* Add :confval:`smartquotes_action` and :confval:`smartquotes_excludes`
+ (refs: #4142, #4357)
Bugs fixed
----------
From 3736768a59dd1d13b205b894385eadd9791b1ac4 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sat, 6 Jan 2018 13:22:47 +0900
Subject: [PATCH 34/95] test: Reduce DeprecationWarning on testing (from
docutils)
---
setup.cfg | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/setup.cfg b/setup.cfg
index 0ce6282dc..c19d0d518 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -44,6 +44,10 @@ incremental = True
check_untyped_defs = True
warn_unused_ignores = True
+[tool:pytest]
+filterwarnings =
+ ignore::DeprecationWarning:docutils.io
+
[coverage:run]
branch = True
source = sphinx
From fdf0a33eab4d73a36582acbf3c91530f78bf35ef Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sat, 6 Jan 2018 16:09:53 +0900
Subject: [PATCH 35/95] test: Remove PYTHONWARNINGS from travis.yml
PYTHONWARNINGS is now controled at tox.ini. So this envvar is no
longer referred.
---
.travis.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index 2bd437436..e51523c19 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,7 +6,6 @@ cache: pip
env:
global:
- PYTHONFAULTHANDLER=x
- - PYTHONWARNINGS=all
- SKIP_LATEX_BUILD=1
matrix:
From 23533e48b22c4187d172ae1d8bf42a21f5c81f2a Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sat, 6 Jan 2018 16:34:51 +0900
Subject: [PATCH 36/95] Update PYTHONWARNINGS on tox.ini to reduce meaningless
warnings
---
tests/conftest.py | 9 ---------
tox.ini | 2 +-
2 files changed, 1 insertion(+), 10 deletions(-)
diff --git a/tests/conftest.py b/tests/conftest.py
index 6cb239d9f..9fb06edab 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -35,14 +35,6 @@ def pytest_report_header(config):
sys.version.split()[0])
-def _filter_warnings():
- def ignore(**kwargs): warnings.filterwarnings('ignore', **kwargs)
-
- ignore(category=DeprecationWarning, module='site') # virtualenv
- ignore(category=PendingDeprecationWarning, module=r'_pytest\..*')
- ignore(category=ImportWarning, module='pkgutil')
-
-
def _initialize_test_directory(session):
testroot = os.path.join(str(session.config.rootdir), 'tests')
tempdir = os.path.abspath(os.getenv('SPHINX_TEST_TEMPDIR',
@@ -58,5 +50,4 @@ def _initialize_test_directory(session):
def pytest_sessionstart(session):
- _filter_warnings()
_initialize_test_directory(session)
diff --git a/tox.ini b/tox.ini
index 2862fd755..810b76f0c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -21,7 +21,7 @@ deps =
du13: docutils==0.13.1
du14: docutils==0.14
setenv =
- PYTHONWARNINGS = all,ignore::ImportWarning:pkgutil
+ PYTHONWARNINGS = all,ignore::ImportWarning:pkgutil,ignore::ImportWarning:importlib._bootstrap,ignore::ImportWarning:importlib._bootstrap_external,ignore::ImportWarning:pytest_cov.plugin,ignore::DeprecationWarning:site,ignore::DeprecationWarning:_pytest.assertion.rewrite,ignore::DeprecationWarning:_pytest.fixtures
SPHINX_TEST_TEMPDIR = {envdir}/testbuild
commands=
pytest -Wall --durations 25 {posargs}
From c1b3efe203752407f37770910da4d082b2b32d52 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sat, 6 Jan 2018 21:01:04 +0900
Subject: [PATCH 37/95] Fix mypy violation
---
sphinx/ext/autodoc/importer.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py
index b22af9ff5..cc68436cd 100644
--- a/sphinx/ext/autodoc/importer.py
+++ b/sphinx/ext/autodoc/importer.py
@@ -151,7 +151,7 @@ def import_module(modname, warningiserror=False):
def import_object(modname, objpath, objtype='', attrgetter=safe_getattr, warningiserror=False):
- # type: (str, List[unicode], str, Callable[[Any, unicode], Any]) -> Any
+ # type: (str, List[unicode], str, Callable[[Any, unicode], Any], bool) -> Any
if objpath:
logger.debug('[autodoc] from %s import %s', modname, '.'.join(objpath))
else:
From c2a7984e05943f33e0ce367baad1fc7855baf5f3 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sat, 6 Jan 2018 22:10:15 +0900
Subject: [PATCH 38/95] Fix enum34 members are treated as inherited member
---
sphinx/ext/autodoc/importer.py | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py
index cc68436cd..101cb930f 100644
--- a/sphinx/ext/autodoc/importer.py
+++ b/sphinx/ext/autodoc/importer.py
@@ -19,7 +19,7 @@ from types import FunctionType, MethodType, ModuleType
from six import PY2
from sphinx.util import logging
-from sphinx.util.inspect import safe_getattr
+from sphinx.util.inspect import isenumclass, safe_getattr
if False:
# For type annotation
@@ -206,6 +206,12 @@ def get_object_members(subject, objpath, attrgetter, analyzer=None):
# the members directly defined in the class
obj_dict = attrgetter(subject, '__dict__', {})
+ # Py34 doesn't have enum members in __dict__.
+ if sys.version_info[:2] == (3, 4) and isenumclass(subject):
+ obj_dict = dict(obj_dict)
+ for name, value in subject.__members__.items():
+ obj_dict[name] = value
+
members = {}
for name in dir(subject):
try:
From e1d8615ce4bd2e3c68f2361447e4e45bbff65d18 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sun, 7 Jan 2018 00:05:48 +0900
Subject: [PATCH 39/95] Don't use add_documenter() in sphinx-autogen
---
sphinx/ext/autosummary/generate.py | 32 +++++++++++++++++++-----------
tests/test_ext_autosummary.py | 2 +-
tests/test_templating.py | 4 ++--
3 files changed, 23 insertions(+), 15 deletions(-)
diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py
index 2873b6082..cfaa4a4ca 100644
--- a/sphinx/ext/autosummary/generate.py
+++ b/sphinx/ext/autosummary/generate.py
@@ -31,9 +31,9 @@ from jinja2.sandbox import SandboxedEnvironment
from sphinx import __display_version__
from sphinx import package_dir
-from sphinx.ext.autodoc import add_documenter
from sphinx.ext.autosummary import import_by_name, get_documenter
from sphinx.jinja2glue import BuiltinTemplateLoader
+from sphinx.registry import SphinxComponentRegistry
from sphinx.util.osutil import ensuredir
from sphinx.util.inspect import safe_getattr
from sphinx.util.rst import escape as rst_escape
@@ -47,20 +47,26 @@ if False:
from sphinx.environment import BuildEnvironment # NOQA
-def setup_documenters():
+class DummyApplication(object):
+ """Dummy Application class for sphinx-autogen command."""
+
+ def __init__(self):
+ self.registry = SphinxComponentRegistry()
+
+
+def setup_documenters(app):
from sphinx.ext.autodoc import (
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
InstanceAttributeDocumenter
)
- add_documenter(ModuleDocumenter)
- add_documenter(ClassDocumenter)
- add_documenter(ExceptionDocumenter)
- add_documenter(DataDocumenter)
- add_documenter(FunctionDocumenter)
- add_documenter(MethodDocumenter)
- add_documenter(AttributeDocumenter)
- add_documenter(InstanceAttributeDocumenter)
+ documenters = [
+ ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
+ FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
+ InstanceAttributeDocumenter
+ ]
+ for documenter in documenters:
+ app.registry.add_documenter(documenter.objtype, documenter)
def _simple_info(msg):
@@ -395,12 +401,14 @@ The format of the autosummary directive is documented in the
def main(argv=sys.argv[1:]):
# type: (List[str]) -> None
- setup_documenters()
+ app = DummyApplication()
+ setup_documenters(app)
args = get_parser().parse_args(argv)
generate_autosummary_docs(args.source_file, args.output_dir,
'.' + args.suffix,
template_dir=args.templates,
- imported_members=args.imported_members)
+ imported_members=args.imported_members,
+ app=app)
if __name__ == '__main__':
diff --git a/tests/test_ext_autosummary.py b/tests/test_ext_autosummary.py
index 1035d3b3b..000ee3af4 100644
--- a/tests/test_ext_autosummary.py
+++ b/tests/test_ext_autosummary.py
@@ -60,9 +60,9 @@ def test_mangle_signature():
def test_get_items_summary(make_app, app_params):
import sphinx.ext.autosummary
import sphinx.ext.autosummary.generate
- sphinx.ext.autosummary.generate.setup_documenters()
args, kwargs = app_params
app = make_app(*args, **kwargs)
+ sphinx.ext.autosummary.generate.setup_documenters(app)
# monkey-patch Autosummary.get_items so we can easily get access to it's
# results..
orig_get_items = sphinx.ext.autosummary.Autosummary.get_items
diff --git a/tests/test_templating.py b/tests/test_templating.py
index 88a196e77..550b3bc7d 100644
--- a/tests/test_templating.py
+++ b/tests/test_templating.py
@@ -15,9 +15,9 @@ from sphinx.ext.autosummary.generate import setup_documenters
@pytest.mark.sphinx('html', testroot='templating')
def test_layout_overloading(make_app, app_params):
- setup_documenters()
args, kwargs = app_params
app = make_app(*args, **kwargs)
+ setup_documenters(app)
app.builder.build_update()
result = (app.outdir / 'contents.html').text(encoding='utf-8')
@@ -27,9 +27,9 @@ def test_layout_overloading(make_app, app_params):
@pytest.mark.sphinx('html', testroot='templating')
def test_autosummary_class_template_overloading(make_app, app_params):
- setup_documenters()
args, kwargs = app_params
app = make_app(*args, **kwargs)
+ setup_documenters(app)
app.builder.build_update()
result = (app.outdir / 'generated' / 'sphinx.application.TemplateBridge.html').text(
From 356765ee769fb26c07f98b887cff16ededf7ca0b Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sun, 7 Jan 2018 00:39:41 +0900
Subject: [PATCH 40/95] Fix mypy violation
---
sphinx/ext/autosummary/generate.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py
index cfaa4a4ca..aeffcb564 100644
--- a/sphinx/ext/autosummary/generate.py
+++ b/sphinx/ext/autosummary/generate.py
@@ -51,10 +51,12 @@ class DummyApplication(object):
"""Dummy Application class for sphinx-autogen command."""
def __init__(self):
+ # type: () -> None
self.registry = SphinxComponentRegistry()
def setup_documenters(app):
+ # type: (Any) -> None
from sphinx.ext.autodoc import (
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
@@ -91,7 +93,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
warn=_simple_warn, info=_simple_info,
base_path=None, builder=None, template_dir=None,
imported_members=False, app=None):
- # type: (List[unicode], unicode, unicode, Callable, Callable, unicode, Builder, unicode, bool) -> None # NOQA
+ # type: (List[unicode], unicode, unicode, Callable, Callable, unicode, Builder, unicode, bool, Any) -> None # NOQA
showed_sources = list(sorted(sources))
if len(showed_sources) > 20:
From 7162fcdff9d76b6923b01c953e32d3949a767548 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sun, 7 Jan 2018 00:51:45 +0900
Subject: [PATCH 41/95] Fix typo
---
sphinx/application.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/application.py b/sphinx/application.py
index 07b8539a9..1c97fec15 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -649,7 +649,7 @@ class Sphinx(object):
def add_autodoc_attrgetter(self, typ, getter):
# type: (Type, Callable[[Any, unicode, Any], Any]) -> None
logger.debug('[app] adding autodoc attrgetter: %r', (typ, getter))
- self.registy.add_autodoc_attrgetter(typ, getter)
+ self.registry.add_autodoc_attrgetter(typ, getter)
def add_search_language(self, cls):
# type: (Any) -> None
From bbfcb2443d511bf790d4fe62ed01079b074fef4b Mon Sep 17 00:00:00 2001
From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
Date: Sat, 6 Jan 2018 17:49:27 +0100
Subject: [PATCH 42/95] cleanup formatting of some sidebar items
---
doc/_templates/indexsidebar.html | 10 ++++----
doc/_themes/sphinx13/static/sphinx13.css | 30 ++++++++++++++++++++++--
2 files changed, 34 insertions(+), 6 deletions(-)
diff --git a/doc/_templates/indexsidebar.html b/doc/_templates/indexsidebar.html
index 6359921a5..b07ef2033 100644
--- a/doc/_templates/indexsidebar.html
+++ b/doc/_templates/indexsidebar.html
@@ -20,12 +20,14 @@ Index, or install it with:{%endtrans%}
{%trans%}Questions? Suggestions?{%endtrans%}
{%trans%}Join the sphinx-users mailing list on Google Groups:{%endtrans%}
+
+
{%trans%}or come to the #sphinx-doc channel on FreeNode.{%endtrans%}
{%trans%}You can also open an issue at the
tracker.{%endtrans%}
diff --git a/doc/_themes/sphinx13/static/sphinx13.css b/doc/_themes/sphinx13/static/sphinx13.css
index 6bdc5a96c..24a33fba7 100644
--- a/doc/_themes/sphinx13/static/sphinx13.css
+++ b/doc/_themes/sphinx13/static/sphinx13.css
@@ -140,11 +140,37 @@ div.sphinxsidebar .logo img {
vertical-align: middle;
}
+div.subscribeformwrapper {
+ display: block;
+ overflow: auto;
+ margin-bottom: 1.2em;
+}
+
div.sphinxsidebar input {
border: 1px solid #aaa;
font-family: 'Open Sans', 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
'Verdana', sans-serif;
- font-size: 1em;
+}
+
+div.sphinxsidebar .subscribeform {
+ margin-top: 0;
+}
+
+div.sphinxsidebar .subscribeform input {
+ border: 1px solid #aaa;
+ font-size: 0.9em;
+ float: left;
+ padding: 0.25em 0.5em;
+ box-sizing: border-box;
+}
+
+div.sphinxsidebar .subscribeform input[type="text"] {
+ width: 60%;
+}
+
+div.sphinxsidebar .subscribeform input[type="submit"] {
+ width: 40%;
+ border-left: none;
}
div.sphinxsidebar h3 {
@@ -281,7 +307,7 @@ tt {
border: 1px solid #ddd;
border-radius: 2px;
color: #333;
- padding: 1px;
+ padding: 1px 0.2em;
}
tt.descname, tt.descclassname, tt.xref {
From 1498d1359056a80d474266708e3ff372adb40994 Mon Sep 17 00:00:00 2001
From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
Date: Sat, 6 Jan 2018 19:29:48 +0100
Subject: [PATCH 43/95] updates to conf.py generated by sphinx-quickstart
---
sphinx/templates/quickstart/conf.py_t | 39 ++++++++++++---------------
1 file changed, 17 insertions(+), 22 deletions(-)
diff --git a/sphinx/templates/quickstart/conf.py_t b/sphinx/templates/quickstart/conf.py_t
index c42861c28..a1c00f8c7 100644
--- a/sphinx/templates/quickstart/conf.py_t
+++ b/sphinx/templates/quickstart/conf.py_t
@@ -6,14 +6,11 @@
# {{ project }} documentation build configuration file, created by
# sphinx-quickstart on {{ now }}.
#
-# This file is execfile()d with the current directory set to its
-# containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
+# This file does only contain a selection of the most common options. For a
+# full list see the documentation:
+# http://www.sphinx-doc.org/en/stable/config
+
+# -- Path setup -----------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
@@ -33,6 +30,18 @@ sys.path.insert(0, u'{{ module_path }}')
{% endif -%}
{% endif %}
+# -- Project information --------------------------------------------------
+
+project = u'{{ project_str }}'
+copyright = u'{{ copyright_str }}'
+author = u'{{ author_str }}'
+
+# The short X.Y version
+version = u'{{ version_str }}'
+# The full version, including alpha/beta/rc tags
+release = u'{{ release_str }}'
+
+
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
@@ -60,20 +69,6 @@ source_suffix = '{{ suffix }}'
# The master toctree document.
master_doc = '{{ master_str }}'
-# General information about the project.
-project = u'{{ project_str }}'
-copyright = u'{{ copyright_str }}'
-author = u'{{ author_str }}'
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = u'{{ version_str }}'
-# The full version, including alpha/beta/rc tags.
-release = u'{{ release_str }}'
-
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
From 2ae7f26fe531c29cedfa01e2f0c3595a4c680972 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sun, 7 Jan 2018 12:09:41 +0900
Subject: [PATCH 44/95] test: Adjust testcase for #3962
---
tests/test_ext_apidoc.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/test_ext_apidoc.py b/tests/test_ext_apidoc.py
index 83951bf03..2bfc8016e 100644
--- a/tests/test_ext_apidoc.py
+++ b/tests/test_ext_apidoc.py
@@ -191,7 +191,7 @@ def test_extension_parsed(make_app, apidoc):
@pytest.mark.apidoc(
- coderoot='test-apidoc-toc',
+ coderoot='test-apidoc-toc/mypackage',
options=["--implicit-namespaces"],
)
def test_toc_all_references_should_exist_pep420_enabled(make_app, apidoc):
@@ -222,7 +222,7 @@ def test_toc_all_references_should_exist_pep420_enabled(make_app, apidoc):
@pytest.mark.apidoc(
- coderoot='test-apidoc-toc',
+ coderoot='test-apidoc-toc/mypackage',
)
def test_toc_all_references_should_exist_pep420_disabled(make_app, apidoc):
"""All references in toc should exist. This test doesn't say if
From 2484819e96f6616479d4822374f7d4cdf743e31e Mon Sep 17 00:00:00 2001
From: MURAOKA Yusuke
Date: Sun, 7 Jan 2018 20:04:53 +0900
Subject: [PATCH 45/95] Fix creating build directory for unknown build target
---
sphinx/application.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/sphinx/application.py b/sphinx/application.py
index 9195f11af..b4fef818f 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -157,10 +157,6 @@ class Sphinx(object):
# status code for command-line application
self.statuscode = 0
- if not path.isdir(outdir):
- logger.info('making output directory...')
- ensuredir(outdir)
-
# read config
self.tags = Tags(tags)
self.config = Config(confdir, CONFIG_FILENAME,
@@ -197,6 +193,10 @@ class Sphinx(object):
# preload builder module (before init config values)
self.preload_builder(buildername)
+ if not path.isdir(outdir):
+ logger.info('making output directory...')
+ ensuredir(outdir)
+
# the config file itself can be an extension
if self.config.setup:
self._setting_up_extension = ['conf.py']
From cc3abba171ed37dc3377b047788894733944c4d5 Mon Sep 17 00:00:00 2001
From: Jon Dufresne
Date: Sun, 7 Jan 2018 09:08:29 -0800
Subject: [PATCH 46/95] Prefer https & readthedocs.io instead of
readthedocs.org for links
Read the Docs moved hosting to readthedocs.io instead of
readthedocs.org. Fix all links in the project.
For additional details, see:
https://blog.readthedocs.com/securing-subdomains/
> Starting today, Read the Docs will start hosting projects from subdomains on
> the domain readthedocs.io, instead of on readthedocs.org. This change
> addresses some security concerns around site cookies while hosting user
> generated data on the same domain as our dashboard.
---
EXAMPLES | 4 ++--
doc/_templates/index.html | 6 +++---
doc/develop.rst | 2 +-
doc/ext/thirdparty.rst | 2 +-
doc/faq.rst | 2 +-
doc/intro.rst | 2 +-
6 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/EXAMPLES b/EXAMPLES
index 6bf6d0e31..edbf48903 100644
--- a/EXAMPLES
+++ b/EXAMPLES
@@ -93,7 +93,7 @@ Documentation using the classic theme
* simuPOP: http://simupop.sourceforge.net/manual_release/build/userGuide.html (customized)
* Sprox: http://sprox.org/ (customized)
* SymPy: http://docs.sympy.org/
-* TurboGears: https://turbogears.readthedocs.org/ (customized)
+* TurboGears: https://turbogears.readthedocs.io/ (customized)
* tvtk: http://docs.enthought.com/mayavi/tvtk/
* Varnish: https://www.varnish-cache.org/docs/ (customized, alabaster for index)
* Waf: https://waf.io/apidocs/
@@ -259,7 +259,7 @@ Documentation using sphinx_bootstrap_theme
* Bootstrap Theme: https://ryan-roemer.github.io/sphinx-bootstrap-theme/
* C/C++ Software Development with Eclipse: http://eclipsebook.in/
* Dataverse: http://guides.dataverse.org/
-* e-cidadania: http://e-cidadania.readthedocs.org/
+* e-cidadania: https://e-cidadania.readthedocs.io/
* Hangfire: http://docs.hangfire.io/
* Hedge: https://documen.tician.de/hedge/
* ObsPy: https://docs.obspy.org/
diff --git a/doc/_templates/index.html b/doc/_templates/index.html
index b4bdb5985..5a8a2f025 100644
--- a/doc/_templates/index.html
+++ b/doc/_templates/index.html
@@ -74,9 +74,9 @@
{%trans%}
You can also download PDF/EPUB versions of the Sphinx documentation:
- a PDF version generated from
+ a PDF version generated from
the LaTeX Sphinx produces, and
- a EPUB version.
+ a EPUB version.
{%endtrans%}
@@ -106,7 +106,7 @@
{%trans%}Hosting{%endtrans%}
{%trans%}Need a place to host your Sphinx docs?
- readthedocs.org hosts a lot of Sphinx docs
+ readthedocs.org hosts a lot of Sphinx docs
already, and integrates well with projects' source control. It also features a
powerful built-in search that exceeds the possibilities of Sphinx' JavaScript-based
offline search.{%endtrans%}
diff --git a/doc/develop.rst b/doc/develop.rst
index 4fc7792f7..19ca81ef9 100644
--- a/doc/develop.rst
+++ b/doc/develop.rst
@@ -138,7 +138,7 @@ own extensions.
.. _cmakedomain: https://bitbucket.org/klorenz/sphinxcontrib-cmakedomain
.. _GNU Make: http://www.gnu.org/software/make/
.. _makedomain: https://bitbucket.org/klorenz/sphinxcontrib-makedomain
-.. _inlinesyntaxhighlight: http://sphinxcontrib-inlinesyntaxhighlight.readthedocs.org
+.. _inlinesyntaxhighlight: https://sphinxcontrib-inlinesyntaxhighlight.readthedocs.io/
.. _CMake: https://cmake.org
.. _domaintools: https://bitbucket.org/klorenz/sphinxcontrib-domaintools
.. _restbuilder: https://pypi.python.org/pypi/sphinxcontrib-restbuilder
diff --git a/doc/ext/thirdparty.rst b/doc/ext/thirdparty.rst
index 6304e4af3..40c24246a 100644
--- a/doc/ext/thirdparty.rst
+++ b/doc/ext/thirdparty.rst
@@ -6,7 +6,7 @@ repository. It is open for anyone who wants to maintain an extension
publicly; just send a short message asking for write permissions.
There are also several extensions hosted elsewhere. The `Sphinx extension
-survey `__ contains a
+survey `__ contains a
comprehensive list.
If you write an extension that you think others will find useful or you think
diff --git a/doc/faq.rst b/doc/faq.rst
index 1ae9a7792..fe3173749 100644
--- a/doc/faq.rst
+++ b/doc/faq.rst
@@ -58,7 +58,7 @@ Read the Docs
Sphinx. They will host sphinx documentation, along with supporting a number
of other features including version support, PDF generation, and more. The
`Getting Started
- `_
+ `_
guide is a good place to start.
Epydoc
diff --git a/doc/intro.rst b/doc/intro.rst
index d3328a5ea..d3b191700 100644
--- a/doc/intro.rst
+++ b/doc/intro.rst
@@ -17,7 +17,7 @@ docs have a look at `Epydoc `_, which also
understands reST.
For a great "introduction" to writing docs in general -- the whys and hows, see
-also `Write the docs `_, written by Eric
+also `Write the docs `_, written by Eric
Holscher.
.. _rinohtype: https://github.com/brechtm/rinohtype
From e015ce2a0fdc7dc2640961d962ea4e153c5ad00c Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 8 Jan 2018 09:36:35 +0900
Subject: [PATCH 47/95] Update CHANGES for PR #4389
---
CHANGES | 2 ++
1 file changed, 2 insertions(+)
diff --git a/CHANGES b/CHANGES
index 040f95e1c..252804ef3 100644
--- a/CHANGES
+++ b/CHANGES
@@ -13,6 +13,7 @@ Incompatible changes
* #3929: apidoc: Move sphinx.apidoc to sphinx.ext.apidoc
* #4226: apidoc: Generate new style makefile (make-mode)
* #4274: sphinx-build returns 2 as an exit code on argument error
+* #4389: output directory will be created after loading extensions
Deprecated
----------
@@ -55,6 +56,7 @@ Features added
code-blocks
* #947: autodoc now supports ignore-module-all to ignore a module's ``__all__``
* #4332: Let LaTeX obey :confval:`math_numfig` for equation numbering
+* #4093: sphinx-build creates empty directories for unknown targets/builders
Features removed
From db415ba05c974e13ad36d5a385dbc2b552272651 Mon Sep 17 00:00:00 2001
From: Joel Nothman
Date: Mon, 8 Jan 2018 13:09:29 +1100
Subject: [PATCH 48/95] Avoid duplicate calls to autodoc-process-docstring
(#4198)
---
sphinx/ext/autosummary/__init__.py | 2 +-
tests/test_ext_autosummary.py | 4 ++++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py
index 3dded11ff..d4fd80467 100644
--- a/sphinx/ext/autosummary/__init__.py
+++ b/sphinx/ext/autosummary/__init__.py
@@ -325,7 +325,7 @@ class Autosummary(Directive):
# -- Grab the summary
documenter.add_content(None)
- doc = list(documenter.process_doc([self.result.data]))
+ doc = self.result.data
while doc and not doc[0].strip():
doc.pop(0)
diff --git a/tests/test_ext_autosummary.py b/tests/test_ext_autosummary.py
index 0aea99df6..8624153e2 100644
--- a/tests/test_ext_autosummary.py
+++ b/tests/test_ext_autosummary.py
@@ -73,6 +73,10 @@ def test_get_items_summary(app, status, warning):
def handler(app, what, name, obj, options, lines):
assert isinstance(lines, list)
+
+ # ensure no docstring is processed twice:
+ assert 'THIS HAS BEEN HANDLED' not in lines
+ lines.append('THIS HAS BEEN HANDLED')
app.connect('autodoc-process-docstring', handler)
sphinx.ext.autosummary.Autosummary.get_items = new_get_items
From b5edde474db6440f77b0e24fac0203a863429aee Mon Sep 17 00:00:00 2001
From: shimizukawa
Date: Mon, 8 Jan 2018 11:18:36 +0900
Subject: [PATCH 49/95] update CHANGES for #4198
---
CHANGES | 2 ++
1 file changed, 2 insertions(+)
diff --git a/CHANGES b/CHANGES
index 252804ef3..d841f2371 100644
--- a/CHANGES
+++ b/CHANGES
@@ -103,6 +103,8 @@ Bugs fixed
one of figures and tables
* #4330: PDF 'howto' documents have an incoherent default LaTeX tocdepth counter
setting
+* #4198: autosummary emits multiple 'autodoc-process-docstring' event. Thanks
+ to Joel Nothman.
Testing
--------
From 7ba54500fcedce47f7cbe29732d246cc84fd2462 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 8 Jan 2018 11:59:50 +0900
Subject: [PATCH 50/95] Fix #4183: doctest: ``:pyversion:`` option also follows
PEP-440 specification
---
CHANGES | 7 ++++++-
doc/ext/doctest.rst | 18 +++++++++++++++---
setup.py | 1 +
sphinx/ext/doctest.py | 39 ++++++++++++++++-----------------------
tests/test_ext_doctest.py | 39 +++++++++++++++++++++++++--------------
5 files changed, 63 insertions(+), 41 deletions(-)
diff --git a/CHANGES b/CHANGES
index 252804ef3..0133d40de 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,11 @@
Release 1.7 (in development)
============================
+Dependencies
+------------
+
+* Add ``packaging`` package
+
Incompatible changes
--------------------
@@ -57,7 +62,7 @@ Features added
* #947: autodoc now supports ignore-module-all to ignore a module's ``__all__``
* #4332: Let LaTeX obey :confval:`math_numfig` for equation numbering
* #4093: sphinx-build creates empty directories for unknown targets/builders
-
+* #4183: doctest: ``:pyversion:`` option also follows PEP-440 specification
Features removed
----------------
diff --git a/doc/ext/doctest.rst b/doc/ext/doctest.rst
index d1cb3c31d..62221bf04 100644
--- a/doc/ext/doctest.rst
+++ b/doc/ext/doctest.rst
@@ -80,12 +80,24 @@ a comma-separated list of group names.
.. doctest::
:pyversion: > 3.3
- The supported operands are ``<``, ``<=``, ``==``, ``>=``, ``>``, and
- comparison is performed by `distutils.version.LooseVersion
- `__.
+ The following operands are supported:
+
+ * ``~=``: Compatible release clause
+ * ``==``: Version matching clause
+ * ``!=``: Version exclusion clause
+ * ``<=``, ``>=``: Inclusive ordered comparison clause
+ * ``<``, ``>``: Exclusive ordered comparison clause
+ * ``===``: Arbitrary equality clause.
+
+ ``pyversion`` option is followed `PEP-440: Version Specifiers
+ `__.
.. versionadded:: 1.6
+ .. versionchanged:: 1.7
+
+ Supported PEP-440 operands and notations
+
Note that like with standard doctests, you have to use ```` to
signal a blank line in the expected output. The ```` is removed
when building presentation output (HTML, LaTeX etc.).
diff --git a/setup.py b/setup.py
index 6b7de9129..f35e5f88d 100644
--- a/setup.py
+++ b/setup.py
@@ -26,6 +26,7 @@ requires = [
'imagesize',
'requests>=2.0.0',
'setuptools',
+ 'packaging',
'sphinxcontrib-websupport',
]
diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py
index e0ce050f7..948ddfec8 100644
--- a/sphinx/ext/doctest.py
+++ b/sphinx/ext/doctest.py
@@ -20,7 +20,8 @@ from os import path
import doctest
from six import itervalues, StringIO, binary_type, text_type, PY2
-from distutils.version import LooseVersion
+from packaging.specifiers import SpecifierSet, InvalidSpecifier
+from packaging.version import Version
from docutils import nodes
from docutils.parsers.rst import Directive, directives
@@ -57,28 +58,23 @@ else:
return text
-def compare_version(ver1, ver2, operand):
- # type: (unicode, unicode, unicode) -> bool
- """Compare `ver1` to `ver2`, relying on `operand`.
+def is_allowed_version(spec, version):
+ # type: (unicode, unicode) -> bool
+ """Check `spec` satisfies `version` or not.
+
+ This obeys PEP-440 specifiers:
+ https://www.python.org/dev/peps/pep-0440/#version-specifiers
Some examples:
- >>> compare_version('3.3', '3.5', '<=')
+ >>> is_allowed_version('3.3', '<=3.5')
True
- >>> compare_version('3.3', '3.2', '<=')
+ >>> is_allowed_version('3.3', '<=3.2')
False
- >>> compare_version('3.3a0', '3.3', '<=')
+ >>> is_allowed_version('3.3', '>3.2, <4.0')
True
"""
- if operand not in ('<=', '<', '==', '>=', '>'):
- raise ValueError("'%s' is not a valid operand.")
- v1 = LooseVersion(ver1)
- v2 = LooseVersion(ver2)
- return ((operand == '<=' and (v1 <= v2)) or
- (operand == '<' and (v1 < v2)) or
- (operand == '==' and (v1 == v2)) or
- (operand == '>=' and (v1 >= v2)) or
- (operand == '>' and (v1 > v2)))
+ return Version(version) in SpecifierSet(spec)
# set up the necessary directives
@@ -143,16 +139,13 @@ class TestDirective(Directive):
node['options'][flag] = (option[0] == '+')
if self.name == 'doctest' and 'pyversion' in self.options:
try:
- option = self.options['pyversion']
- # :pyversion: >= 3.6 --> operand='>=', option_version='3.6'
- operand, option_version = [item.strip() for item in option.split()]
- running_version = platform.python_version()
- if not compare_version(running_version, option_version, operand):
+ spec = self.options['pyversion']
+ if not is_allowed_version(spec, platform.python_version()):
flag = doctest.OPTIONFLAGS_BY_NAME['SKIP']
node['options'][flag] = True # Skip the test
- except ValueError:
+ except InvalidSpecifier:
self.state.document.reporter.warning(
- _("'%s' is not a valid pyversion option") % option,
+ _("'%s' is not a valid pyversion option") % spec,
line=self.lineno)
return [node]
diff --git a/tests/test_ext_doctest.py b/tests/test_ext_doctest.py
index 020357879..7d907d086 100644
--- a/tests/test_ext_doctest.py
+++ b/tests/test_ext_doctest.py
@@ -9,7 +9,9 @@
:license: BSD, see LICENSE for details.
"""
import pytest
-from sphinx.ext.doctest import compare_version
+from sphinx.ext.doctest import is_allowed_version
+from packaging.version import InvalidVersion
+from packaging.specifiers import InvalidSpecifier
cleanup_called = 0
@@ -26,19 +28,28 @@ def test_build(app, status, warning):
assert cleanup_called == 3, 'testcleanup did not get executed enough times'
-def test_compare_version():
- assert compare_version('3.3', '3.4', '<') is True
- assert compare_version('3.3', '3.2', '<') is False
- assert compare_version('3.3', '3.4', '<=') is True
- assert compare_version('3.3', '3.2', '<=') is False
- assert compare_version('3.3', '3.3', '==') is True
- assert compare_version('3.3', '3.4', '==') is False
- assert compare_version('3.3', '3.2', '>=') is True
- assert compare_version('3.3', '3.4', '>=') is False
- assert compare_version('3.3', '3.2', '>') is True
- assert compare_version('3.3', '3.4', '>') is False
- with pytest.raises(ValueError):
- compare_version('3.3', '3.4', '+')
+def test_is_allowed_version():
+ assert is_allowed_version('<3.4', '3.3') is True
+ assert is_allowed_version('<3.4', '3.3') is True
+ assert is_allowed_version('<3.2', '3.3') is False
+ assert is_allowed_version('<=3.4', '3.3') is True
+ assert is_allowed_version('<=3.2', '3.3') is False
+ assert is_allowed_version('==3.3', '3.3') is True
+ assert is_allowed_version('==3.4', '3.3') is False
+ assert is_allowed_version('>=3.2', '3.3') is True
+ assert is_allowed_version('>=3.4', '3.3') is False
+ assert is_allowed_version('>3.2', '3.3') is True
+ assert is_allowed_version('>3.4', '3.3') is False
+ assert is_allowed_version('~=3.4', '3.4.5') is True
+ assert is_allowed_version('~=3.4', '3.5.0') is True
+
+ # invalid spec
+ with pytest.raises(InvalidSpecifier):
+ is_allowed_version('&3.4', '3.5')
+
+ # invalid version
+ with pytest.raises(InvalidVersion):
+ is_allowed_version('>3.4', 'Sphinx')
def cleanup_call():
From 273f834157a90806e0cd2031e8974c43f9787a6f Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 8 Jan 2018 12:19:08 +0900
Subject: [PATCH 51/95] Fix #4081: Warnings and errors colored the same when
building
---
CHANGES | 2 +-
sphinx/util/logging.py | 2 +-
tests/test_util_logging.py | 6 +++---
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/CHANGES b/CHANGES
index d841f2371..6227328c4 100644
--- a/CHANGES
+++ b/CHANGES
@@ -58,7 +58,6 @@ Features added
* #4332: Let LaTeX obey :confval:`math_numfig` for equation numbering
* #4093: sphinx-build creates empty directories for unknown targets/builders
-
Features removed
----------------
@@ -105,6 +104,7 @@ Bugs fixed
setting
* #4198: autosummary emits multiple 'autodoc-process-docstring' event. Thanks
to Joel Nothman.
+* #4081: Warnings and errors colored the same when building
Testing
--------
diff --git a/sphinx/util/logging.py b/sphinx/util/logging.py
index ec81f02d7..04bf91830 100644
--- a/sphinx/util/logging.py
+++ b/sphinx/util/logging.py
@@ -53,7 +53,7 @@ VERBOSITY_MAP.update({
COLOR_MAP = defaultdict(lambda: 'blue') # type: Dict[int, unicode]
COLOR_MAP.update({
logging.ERROR: 'darkred',
- logging.WARNING: 'darkred',
+ logging.WARNING: 'red',
logging.DEBUG: 'darkgray',
})
diff --git a/tests/test_util_logging.py b/tests/test_util_logging.py
index e909c2dcf..48eed82b0 100644
--- a/tests/test_util_logging.py
+++ b/tests/test_util_logging.py
@@ -183,7 +183,7 @@ def test_warning_location(app, status, warning):
assert 'index.txt:10: WARNING: message2' in warning.getvalue()
logger.warning('message3', location=None)
- assert colorize('darkred', 'WARNING: message3') in warning.getvalue()
+ assert colorize('red', 'WARNING: message3') in warning.getvalue()
node = nodes.Node()
node.source, node.line = ('index.txt', 10)
@@ -200,7 +200,7 @@ def test_warning_location(app, status, warning):
node.source, node.line = (None, None)
logger.warning('message7', location=node)
- assert colorize('darkred', 'WARNING: message7') in warning.getvalue()
+ assert colorize('red', 'WARNING: message7') in warning.getvalue()
def test_pending_warnings(app, status, warning):
@@ -236,7 +236,7 @@ def test_colored_logs(app, status, warning):
assert colorize('darkgray', 'message1') in status.getvalue()
assert 'message2\n' in status.getvalue() # not colored
assert 'message3\n' in status.getvalue() # not colored
- assert colorize('darkred', 'WARNING: message4') in warning.getvalue()
+ assert colorize('red', 'WARNING: message4') in warning.getvalue()
assert 'WARNING: message5\n' in warning.getvalue() # not colored
assert colorize('darkred', 'WARNING: message6') in warning.getvalue()
From 9a6ad38e2d23a7285d15d967e9d1eb5dcd2e2cfb Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 8 Jan 2018 14:12:44 +0900
Subject: [PATCH 52/95] Rename AutoDirective (and make alias)
---
sphinx/ext/autodoc/__init__.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 68f78eeb2..4de899384 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -1477,7 +1477,7 @@ class InstanceAttributeDocumenter(AttributeDocumenter):
AttributeDocumenter.add_content(self, more_content, no_docstring=True)
-class AutoDirective(object):
+class AutodocRegistry(object):
"""
A registry of Documenters and attrgetters.
@@ -1499,6 +1499,9 @@ class AutoDirective(object):
_special_attrgetters = {} # type: Dict[Type, Callable]
+AutoDirective = AutodocRegistry # for backward compatibility
+
+
def add_documenter(cls):
# type: (Type[Documenter]) -> None
"""Register a new Documenter."""
From 2c920d7d78f2ff63836e3eff445d41bed569d49f Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 8 Jan 2018 14:37:03 +0900
Subject: [PATCH 53/95] Fix #1922: Upper characters problem in French HTML
Search
---
sphinx/search/fr.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/search/fr.py b/sphinx/search/fr.py
index 2cf70357e..615a47383 100644
--- a/sphinx/search/fr.py
+++ b/sphinx/search/fr.py
@@ -207,4 +207,4 @@ class SearchFrench(SearchLanguage):
self.stemmer = snowballstemmer.stemmer('french')
def stem(self, word):
- return self.stemmer.stemWord(word)
+ return self.stemmer.stemWord(word.lower())
From f717f2dd64ed427ba68e08728f6757cf34492777 Mon Sep 17 00:00:00 2001
From: Guillaume Gay
Date: Mon, 8 Jan 2018 08:49:22 +0100
Subject: [PATCH 54/95] Fixes bug when evaluating entry to bool
As reported here: https://github.com/rtfd/readthedocs.org/issues/3411 sphinx sometimes fails with the error:
```python
File "/home/docs/checkouts/readthedocs.org/user_builds/drf-yasg/envs/latest/lib/python3.5/site-packages/sphinx/ext/viewcode.py", line 74, in has_tag
if entry is None or entry[0] != code:
TypeError: 'bool' object is not subscriptable
```
This is not critical as whipping the build or even just running it again fixes it, but the error is confusing...
I believe switching the two if statement above should prevent this from happening.
---
sphinx/ext/viewcode.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py
index 2fd4479f8..3df5b86e2 100644
--- a/sphinx/ext/viewcode.py
+++ b/sphinx/ext/viewcode.py
@@ -71,12 +71,12 @@ def doctree_read(app, doctree):
code = analyzer.code.decode(analyzer.encoding)
else:
code = analyzer.code
- if entry is None or entry[0] != code:
+ if entry is False:
+ return
+ elif entry is None or entry[0] != code:
analyzer.find_tags()
entry = code, analyzer.tags, {}, refname
env._viewcode_modules[modname] = entry # type: ignore
- elif entry is False:
- return
_, tags, used, _ = entry
if fullname in tags:
used[fullname] = docname
From 6448d9b1536ea03f614cb3cb6a83b7f9bda63039 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 8 Jan 2018 18:03:48 +0900
Subject: [PATCH 55/95] Refactor autodoc: Use get_documenters()
---
sphinx/ext/autodoc/__init__.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 070a7bb2e..386245d20 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -296,9 +296,7 @@ class Documenter(object):
def documenters(self):
# type: () -> Dict[unicode, Type[Documenter]]
"""Returns registered Documenter classes"""
- classes = dict(AutoDirective._registry) # registered directly
- classes.update(self.env.app.registry.documenters) # registered by API
- return classes
+ return get_documenters(self.env.app)
def add_line(self, line, source, *lineno):
# type: (unicode, unicode, int) -> None
From 8ca490f3996a6babc10b69fb4c2af5766c6a1a85 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 8 Jan 2018 19:01:54 +0900
Subject: [PATCH 56/95] Fix flake8 violation
---
sphinx/ext/autodoc/directive.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py
index d36888f7e..5d17481eb 100644
--- a/sphinx/ext/autodoc/directive.py
+++ b/sphinx/ext/autodoc/directive.py
@@ -12,7 +12,7 @@ from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
from docutils.utils import assemble_option_dict
-from sphinx.ext.autodoc import AutoDirective, get_documenters
+from sphinx.ext.autodoc import get_documenters
from sphinx.util import logging
from sphinx.util.docutils import switch_source_input
from sphinx.util.nodes import nested_parse_with_titles
From f366816968e29ba0783fa493d58d4f0a0a03888e Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 8 Jan 2018 20:31:57 +0900
Subject: [PATCH 57/95] Fix SphinxStandaloneReader.__init__() method has been
duplicated
---
sphinx/io.py | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/sphinx/io.py b/sphinx/io.py
index 416dfde10..66ba8334e 100644
--- a/sphinx/io.py
+++ b/sphinx/io.py
@@ -86,11 +86,8 @@ class SphinxStandaloneReader(SphinxBaseReader):
def __init__(self, app, *args, **kwargs):
# type: (Sphinx, Any, Any) -> None
self.transforms = self.transforms + app.registry.get_transforms()
- SphinxBaseReader.__init__(self, *args, **kwargs) # type: ignore
-
- def __init__(self, app, parsers={}, *args, **kwargs):
- SphinxBaseReader.__init__(self, app, parsers, *args, **kwargs)
self.smart_quotes = app.env.settings['smart_quotes']
+ SphinxBaseReader.__init__(self, *args, **kwargs) # type: ignore
def get_transforms(self):
transforms = SphinxBaseReader.get_transforms(self)
From b6b71d99c9df23ce77d247c5295df4bbd7712c43 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 8 Jan 2018 20:32:37 +0900
Subject: [PATCH 58/95] Initialize settings.smartquotes_locales
---
sphinx/environment/__init__.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index fc5ac691a..c04073163 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -67,6 +67,7 @@ default_settings = {
'sectsubtitle_xform': False,
'halt_level': 5,
'file_insertion_enabled': True,
+ 'smartquotes_locales': [],
}
# This is increased every time an environment attribute is added
From 739022730295c4968ecc212bbb80b03981eeced3 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 8 Jan 2018 21:38:18 +0900
Subject: [PATCH 59/95] Bump to 1.6.6 final
---
CHANGES | 4 ++--
sphinx/__init__.py | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/CHANGES b/CHANGES
index 80f8c4faa..05c1abdba 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,5 @@
-Release 1.6.6 (in development)
-==============================
+Release 1.6.6 (released Jan 08, 2018)
+=====================================
Dependencies
------------
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index b8a62f9e8..332bfe9a2 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -34,13 +34,13 @@ if 'PYTHONWARNINGS' not in os.environ:
warnings.filterwarnings('ignore', "'U' mode is deprecated",
DeprecationWarning, module='docutils.io')
-__version__ = '1.6.6+'
+__version__ = '1.6.6'
__released__ = '1.6.6' # used when Sphinx builds its own docs
# version info for better programmatic use
# possible values for 3rd element: 'alpha', 'beta', 'rc', 'final'
# 'final' has 0 as the last element
-version_info = (1, 6, 6, 'beta', 0)
+version_info = (1, 6, 6, 'final', 0)
package_dir = path.abspath(path.dirname(__file__))
From 562683ea32e687c279c019f9837450ba3d6b54df Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 8 Jan 2018 21:42:09 +0900
Subject: [PATCH 60/95] Bump version
---
CHANGES | 19 ++++++++++++++-----
sphinx/__init__.py | 6 +++---
2 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/CHANGES b/CHANGES
index 05c1abdba..00fcb59a7 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,5 @@
-Release 1.6.6 (released Jan 08, 2018)
-=====================================
+Release 1.6.7 (in development)
+==============================
Dependencies
------------
@@ -13,6 +13,18 @@ Deprecated
Features added
--------------
+Bugs fixed
+----------
+
+Testing
+--------
+
+Release 1.6.6 (released Jan 08, 2018)
+=====================================
+
+Features added
+--------------
+
* #4181: autodoc: Sort dictionary keys when possible
* ``VerbatimHighlightColor`` is a new
:ref:`LaTeX 'sphinxsetup' ` key (refs: #4285)
@@ -46,9 +58,6 @@ Bugs fixed
* Fix links to external option docs with intersphinx (refs: #3769)
* #4091: Private members not documented without :undoc-members:
-Testing
---------
-
Release 1.6.5 (released Oct 23, 2017)
=====================================
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index 332bfe9a2..69775059c 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -34,13 +34,13 @@ if 'PYTHONWARNINGS' not in os.environ:
warnings.filterwarnings('ignore', "'U' mode is deprecated",
DeprecationWarning, module='docutils.io')
-__version__ = '1.6.6'
-__released__ = '1.6.6' # used when Sphinx builds its own docs
+__version__ = '1.6.7+'
+__released__ = '1.6.7' # used when Sphinx builds its own docs
# version info for better programmatic use
# possible values for 3rd element: 'alpha', 'beta', 'rc', 'final'
# 'final' has 0 as the last element
-version_info = (1, 6, 6, 'final', 0)
+version_info = (1, 6, 7, 'beta', 0)
package_dir = path.abspath(path.dirname(__file__))
From 84b4882b31b0af34fd67fd15ceb57582d6951c32 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Tue, 9 Jan 2018 23:01:55 +0900
Subject: [PATCH 61/95] Fix smartquotes has been ignored
---
sphinx/environment/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index c04073163..251a88589 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -665,7 +665,7 @@ class BuildEnvironment(object):
language = self.config.language or 'en'
self.settings['language_code'] = language
if 'smart_quotes' not in self.settings:
- self.settings['smart_quotes'] = True
+ self.settings['smart_quotes'] = self.config.smartquotes
# some conditions exclude smart quotes, overriding smart_quotes
for valname, vallist in iteritems(self.config.smartquotes_excludes):
From e77267dd943330aea66eaa09f2cd62ea4eb7cc68 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Tue, 9 Jan 2018 22:21:49 +0900
Subject: [PATCH 62/95] Add tests for smartquotes
---
tests/roots/test-smartquotes/conf.py | 7 ++
tests/roots/test-smartquotes/index.rst | 4 ++
tests/test_smartquotes.py | 92 ++++++++++++++++++++++++++
3 files changed, 103 insertions(+)
create mode 100644 tests/roots/test-smartquotes/conf.py
create mode 100644 tests/roots/test-smartquotes/index.rst
create mode 100644 tests/test_smartquotes.py
diff --git a/tests/roots/test-smartquotes/conf.py b/tests/roots/test-smartquotes/conf.py
new file mode 100644
index 000000000..31e7a6ed4
--- /dev/null
+++ b/tests/roots/test-smartquotes/conf.py
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+
+master_doc = 'index'
+
+latex_documents = [
+ (master_doc, 'test.tex', 'The basic Sphinx documentation for testing', 'Sphinx', 'report')
+]
diff --git a/tests/roots/test-smartquotes/index.rst b/tests/roots/test-smartquotes/index.rst
new file mode 100644
index 000000000..be0d9b89c
--- /dev/null
+++ b/tests/roots/test-smartquotes/index.rst
@@ -0,0 +1,4 @@
+test-smartquotes
+================
+
+-- "Sphinx" is a tool that makes it easy ...
diff --git a/tests/test_smartquotes.py b/tests/test_smartquotes.py
new file mode 100644
index 000000000..f9ea9d726
--- /dev/null
+++ b/tests/test_smartquotes.py
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+"""
+ test_smartquotes
+ ~~~~~~~~~~~~~~~~
+
+ Test smart quotes.
+
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import pytest
+from sphinx.util import docutils
+
+
+@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True)
+def test_basic(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'index.html').text()
+ assert u'– “Sphinx” is a tool that makes it easy …
' in content
+
+
+@pytest.mark.sphinx(buildername='text', testroot='smartquotes', freshenv=True)
+def test_text_builder(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'index.txt').text()
+ assert u'-- "Sphinx" is a tool that makes it easy ...' in content
+
+
+@pytest.mark.sphinx(buildername='man', testroot='smartquotes', freshenv=True)
+def test_man_builder(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'python.1').text()
+ assert u'\\-\\- "Sphinx" is a tool that makes it easy ...' in content
+
+
+@pytest.mark.sphinx(buildername='latex', testroot='smartquotes', freshenv=True)
+def test_latex_builder(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'test.tex').text()
+ assert u'\\textendash{} “Sphinx” is a tool that makes it easy …' in content
+
+
+@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True,
+ confoverrides={'language': 'ja'})
+def test_ja_html_builder(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'index.html').text()
+ assert u'-- "Sphinx" is a tool that makes it easy ...
' in content
+
+
+@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True,
+ confoverrides={'smartquotes': False})
+def test_smartquotes_disabled(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'index.html').text()
+ assert u'-- "Sphinx" is a tool that makes it easy ...
' in content
+
+
+@pytest.mark.skipif(docutils.__version_info__ < (0, 14),
+ reason='docutils-0.14 or above is required')
+@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True,
+ confoverrides={'smartquotes_action': 'q'})
+def test_smartquotes_action(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'index.html').text()
+ assert u'-- “Sphinx” is a tool that makes it easy ...
' in content
+
+
+@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True,
+ confoverrides={'language': 'ja', 'smartquotes_excludes': {}})
+def test_smartquotes_excludes_language(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'index.html').text()
+ assert u'– 「Sphinx」 is a tool that makes it easy …
' in content
+
+
+@pytest.mark.sphinx(buildername='man', testroot='smartquotes', freshenv=True,
+ confoverrides={'smartquotes_excludes': {}})
+def test_smartquotes_excludes_builders(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'python.1').text()
+ assert u'– “Sphinx” is a tool that makes it easy …' in content
From 5ad20e6406e656871918698327b41f71d06900ee Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Wed, 10 Jan 2018 21:08:51 +0900
Subject: [PATCH 63/95] Allow SphinxTransform on read-phase
---
sphinx/io.py | 15 ++++++++++++++-
sphinx/transforms/i18n.py | 2 +-
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/sphinx/io.py b/sphinx/io.py
index 66ba8334e..471675e0e 100644
--- a/sphinx/io.py
+++ b/sphinx/io.py
@@ -19,6 +19,7 @@ from docutils.writers import UnfilteredWriter
from six import text_type
from typing import Any, Union # NOQA
+from sphinx.transforms import SphinxTransformer
from sphinx.transforms import (
ApplySourceWorkaround, ExtraTranslatableNodes, CitationReferences,
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, SortIds,
@@ -56,6 +57,11 @@ class SphinxBaseReader(standalone.Reader):
This replaces reporter by Sphinx's on generating document.
"""
+ def __init__(self, app, *args, **kwargs):
+ # type: (Sphinx, Any, Any) -> None
+ self.env = app.env
+ standalone.Reader.__init__(self, *args, **kwargs)
+
def get_transforms(self):
# type: () -> List[Transform]
return standalone.Reader.get_transforms(self) + self.transforms
@@ -66,9 +72,16 @@ class SphinxBaseReader(standalone.Reader):
for logging.
"""
document = standalone.Reader.new_document(self)
+
+ # substitute transformer
+ document.transformer = SphinxTransformer(document)
+ document.transformer.set_environment(self.env)
+
+ # substitute reporter
reporter = document.reporter
document.reporter = LoggingReporter.from_reporter(reporter)
document.reporter.set_source(self.source)
+
return document
@@ -87,7 +100,7 @@ class SphinxStandaloneReader(SphinxBaseReader):
# type: (Sphinx, Any, Any) -> None
self.transforms = self.transforms + app.registry.get_transforms()
self.smart_quotes = app.env.settings['smart_quotes']
- SphinxBaseReader.__init__(self, *args, **kwargs) # type: ignore
+ SphinxBaseReader.__init__(self, app, *args, **kwargs) # type: ignore
def get_transforms(self):
transforms = SphinxBaseReader.get_transforms(self)
diff --git a/sphinx/transforms/i18n.py b/sphinx/transforms/i18n.py
index 5ae33d86a..db67aae97 100644
--- a/sphinx/transforms/i18n.py
+++ b/sphinx/transforms/i18n.py
@@ -50,7 +50,7 @@ def publish_msgstr(app, source, source_path, source_line, config, settings):
:rtype: docutils.nodes.document
"""
from sphinx.io import SphinxI18nReader
- reader = SphinxI18nReader()
+ reader = SphinxI18nReader(app)
reader.set_lineno_for_reporter(source_line)
parser = app.registry.create_source_parser(app, '')
doc = reader.read(
From 776bd09f434958b8d87a4ebe4824668c9368bdcf Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Wed, 10 Jan 2018 21:08:51 +0900
Subject: [PATCH 64/95] Merge sphinx_smartquotes_action() to SphinxSmartQuotes
class
---
sphinx/environment/__init__.py | 27 ++++-----------------------
sphinx/transforms/__init__.py | 11 ++++++++++-
2 files changed, 14 insertions(+), 24 deletions(-)
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index 251a88589..578068175 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -19,7 +19,6 @@ import warnings
from os import path
from copy import copy
from collections import defaultdict
-from contextlib import contextmanager
from six import BytesIO, itervalues, class_types, next, iteritems
from six.moves import cPickle as pickle
@@ -41,7 +40,7 @@ from sphinx.util.matching import compile_matchers
from sphinx.util.parallel import ParallelTasks, parallel_available, make_chunks
from sphinx.util.websupport import is_commentable
from sphinx.errors import SphinxError, ExtensionError
-from sphinx.transforms import SphinxTransformer, SphinxSmartQuotes
+from sphinx.transforms import SphinxTransformer
from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.environment.adapters.indexentries import IndexEntries
from sphinx.environment.adapters.toctree import TocTree
@@ -84,22 +83,6 @@ versioning_conditions = {
} # type: Dict[unicode, Union[bool, Callable]]
-@contextmanager
-def sphinx_smartquotes_action(env):
- # type: (BuildEnvironment) -> Generator
- if not hasattr(SphinxSmartQuotes, 'smartquotes_action'):
- # less than docutils-0.14
- yield
- else:
- # docutils-0.14 or above
- try:
- original = SphinxSmartQuotes.smartquotes_action
- SphinxSmartQuotes.smartquotes_action = env.config.smartquotes_action
- yield
- finally:
- SphinxSmartQuotes.smartquotes_action = original
-
-
class NoUri(Exception):
"""Raised by get_relative_uri if there is no URI available."""
pass
@@ -602,8 +585,7 @@ class BuildEnvironment(object):
# remove all inventory entries for that file
app.emit('env-purge-doc', self, docname)
self.clear_doc(docname)
- with sphinx_smartquotes_action(self):
- self.read_doc(docname, app)
+ self.read_doc(docname, app)
def _read_parallel(self, docnames, app, nproc):
# type: (List[unicode], Sphinx, int) -> None
@@ -615,9 +597,8 @@ class BuildEnvironment(object):
def read_process(docs):
# type: (List[unicode]) -> unicode
self.app = app
- with sphinx_smartquotes_action(self):
- for docname in docs:
- self.read_doc(docname, app)
+ for docname in docs:
+ self.read_doc(docname, app)
# allow pickling self to send it back
return BuildEnvironment.dumps(self)
diff --git a/sphinx/transforms/__init__.py b/sphinx/transforms/__init__.py
index acfff6a4d..c2d8d4eb5 100644
--- a/sphinx/transforms/__init__.py
+++ b/sphinx/transforms/__init__.py
@@ -333,12 +333,21 @@ class SphinxContentsFilter(ContentsFilter):
raise nodes.SkipNode
-class SphinxSmartQuotes(SmartQuotes):
+class SphinxSmartQuotes(SmartQuotes, SphinxTransform):
"""
Customized SmartQuotes to avoid transform for some extra node types.
refs: sphinx.parsers.RSTParser
"""
+ @property
+ def smartquotes_action(self):
+ # type: () -> unicode
+ """A smartquotes_action setting for SmartQuotes.
+
+ Users can change this setting through :confval:`smartquotes_action`.
+ """
+ return self.config.smartquotes_action
+
def get_tokens(self, txtnodes):
# A generator that yields ``(texttype, nodetext)`` tuples for a list
# of "Text" nodes (interface to ``smartquotes.educate_tokens()``).
From 24a6578e09d1806d5888819abc1651bb3428b521 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Wed, 10 Jan 2018 21:08:52 +0900
Subject: [PATCH 65/95] Make SphinxSmartQuotes more inteligent
---
sphinx/environment/__init__.py | 30 +++++-----------------------
sphinx/io.py | 9 +--------
sphinx/transforms/__init__.py | 36 +++++++++++++++++++++++++++++++++-
3 files changed, 41 insertions(+), 34 deletions(-)
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index 578068175..17f9667a1 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -20,11 +20,10 @@ from os import path
from copy import copy
from collections import defaultdict
-from six import BytesIO, itervalues, class_types, next, iteritems
+from six import BytesIO, itervalues, class_types, next
from six.moves import cPickle as pickle
-from docutils.utils import Reporter, get_source_line, normalize_language_tag
-from docutils.utils.smartquotes import smartchars
+from docutils.utils import Reporter, get_source_line
from docutils.frontend import OptionParser
from sphinx import addnodes, versioning
@@ -643,29 +642,10 @@ class BuildEnvironment(object):
self.config.trim_footnote_reference_space
self.settings['gettext_compact'] = self.config.gettext_compact
- language = self.config.language or 'en'
- self.settings['language_code'] = language
- if 'smart_quotes' not in self.settings:
- self.settings['smart_quotes'] = self.config.smartquotes
+ self.settings['language_code'] = self.config.language or 'en'
- # some conditions exclude smart quotes, overriding smart_quotes
- for valname, vallist in iteritems(self.config.smartquotes_excludes):
- if valname == 'builders':
- # this will work only for checking first build target
- if self.app.builder.name in vallist:
- self.settings['smart_quotes'] = False
- break
- elif valname == 'languages':
- if self.config.language in vallist:
- self.settings['smart_quotes'] = False
- break
-
- # confirm selected language supports smart_quotes or not
- for tag in normalize_language_tag(language):
- if tag in smartchars.quotes:
- break
- else:
- self.settings['smart_quotes'] = False
+ # Allow to disable by 3rd party extension (workaround)
+ self.settings.setdefault('smart_quotes', True)
def read_doc(self, docname, app=None):
# type: (unicode, Sphinx) -> None
diff --git a/sphinx/io.py b/sphinx/io.py
index 471675e0e..61761f697 100644
--- a/sphinx/io.py
+++ b/sphinx/io.py
@@ -93,21 +93,14 @@ class SphinxStandaloneReader(SphinxBaseReader):
Locale, CitationReferences, DefaultSubstitutions, MoveModuleTargets,
HandleCodeBlocks, AutoNumbering, AutoIndexUpgrader, SortIds,
RemoveTranslatableInline, PreserveTranslatableMessages, FilterSystemMessages,
- RefOnlyBulletListTransform, UnreferencedFootnotesDetector
+ RefOnlyBulletListTransform, UnreferencedFootnotesDetector, SphinxSmartQuotes,
] # type: List[Transform]
def __init__(self, app, *args, **kwargs):
# type: (Sphinx, Any, Any) -> None
self.transforms = self.transforms + app.registry.get_transforms()
- self.smart_quotes = app.env.settings['smart_quotes']
SphinxBaseReader.__init__(self, app, *args, **kwargs) # type: ignore
- def get_transforms(self):
- transforms = SphinxBaseReader.get_transforms(self)
- if self.smart_quotes:
- transforms.append(SphinxSmartQuotes)
- return transforms
-
class SphinxI18nReader(SphinxBaseReader):
"""
diff --git a/sphinx/transforms/__init__.py b/sphinx/transforms/__init__.py
index c2d8d4eb5..4e8c6f0bd 100644
--- a/sphinx/transforms/__init__.py
+++ b/sphinx/transforms/__init__.py
@@ -12,8 +12,9 @@
from docutils import nodes
from docutils.transforms import Transform, Transformer
from docutils.transforms.parts import ContentsFilter
-from docutils.utils import new_document
from docutils.transforms.universal import SmartQuotes
+from docutils.utils import new_document, normalize_language_tag
+from docutils.utils.smartquotes import smartchars
from sphinx import addnodes
from sphinx.locale import _
@@ -339,6 +340,39 @@ class SphinxSmartQuotes(SmartQuotes, SphinxTransform):
refs: sphinx.parsers.RSTParser
"""
+ def apply(self):
+ # type: () -> None
+ if not self.is_available():
+ return
+
+ SmartQuotes.apply(self)
+
+ def is_available(self):
+ # type: () -> bool
+ builders = self.config.smartquotes_excludes.get('builders', [])
+ languages = self.config.smartquotes_excludes.get('languages', [])
+
+ if self.document.settings.smart_quotes is False:
+ # disabled by 3rd party extension (workaround)
+ return False
+ elif self.config.smartquotes is False:
+ # disabled by confval smartquotes
+ return False
+ elif self.app.builder.name in builders:
+ # disabled by confval smartquotes_excludes['builders']
+ return False
+ elif self.config.language in languages:
+ # disabled by confval smartquotes_excludes['languages']
+ return False
+
+ # confirm selected language supports smart_quotes or not
+ language = self.env.settings['language_code']
+ for tag in normalize_language_tag(language):
+ if tag in smartchars.quotes:
+ return True
+ else:
+ return False
+
@property
def smartquotes_action(self):
# type: () -> unicode
From 33fd1f446ab0041ae53d42fa7ed5e7b4aa18d74b Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Wed, 10 Jan 2018 21:35:21 +0900
Subject: [PATCH 66/95] latex: Do not display Release label if
:confval:`release` is not set
---
CHANGES | 1 +
sphinx/writers/latex.py | 2 +-
tests/test_build_latex.py | 18 ++++++++++++++++--
3 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/CHANGES b/CHANGES
index d9350d21a..b3e6283fd 100644
--- a/CHANGES
+++ b/CHANGES
@@ -113,6 +113,7 @@ Bugs fixed
* #4198: autosummary emits multiple 'autodoc-process-docstring' event. Thanks
to Joel Nothman.
* #4081: Warnings and errors colored the same when building
+* latex: Do not display 'Release' label if :confval:`release` is not set
Testing
--------
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index de472b36c..6c86e6174 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -549,7 +549,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
'author': document.settings.author, # treat as a raw LaTeX code
'indexname': _('Index'),
})
- if not self.elements['releasename']:
+ if not self.elements['releasename'] and self.elements['release']:
self.elements.update({
'releasename': _('Release'),
})
diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py
index ab91d7a48..e7b61ad0c 100644
--- a/tests/test_build_latex.py
+++ b/tests/test_build_latex.py
@@ -165,13 +165,15 @@ def test_latex_warnings(app, status, warning):
@pytest.mark.sphinx('latex', testroot='basic')
-def test_latex_title(app, status, warning):
+def test_latex_basic(app, status, warning):
app.builder.build_all()
result = (app.outdir / 'test.tex').text(encoding='utf8')
print(result)
print(status.getvalue())
print(warning.getvalue())
- assert '\\title{The basic Sphinx documentation for testing}' in result
+ assert r'\title{The basic Sphinx documentation for testing}' in result
+ assert r'\release{}' in result
+ assert r'\renewcommand{\releasename}{}' in result
@pytest.mark.sphinx('latex', testroot='latex-title')
@@ -184,6 +186,18 @@ def test_latex_title_after_admonitions(app, status, warning):
assert '\\title{test-latex-title}' in result
+@pytest.mark.sphinx('latex', testroot='basic',
+ confoverrides={'release': '1.0'})
+def test_latex_release(app, status, warning):
+ app.builder.build_all()
+ result = (app.outdir / 'test.tex').text(encoding='utf8')
+ print(result)
+ print(status.getvalue())
+ print(warning.getvalue())
+ assert r'\release{1.0}' in result
+ assert r'\renewcommand{\releasename}{Release}' in result
+
+
@pytest.mark.sphinx('latex', testroot='numfig',
confoverrides={'numfig': True})
def test_numref(app, status, warning):
From 90f7c7ef3fd18b5ceff5eef1361f5f71f68209ae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Antoine=20Beaupr=C3=A9?=
Date: Thu, 11 Jan 2018 13:20:26 -0500
Subject: [PATCH 67/95] add link to manpages in HTML builder
It is useful to have the HTML documentation builder actually link to
real rendered versions of HTML manpages in its output. That way people
can click on manpages to get the full documentation. There are a few
services offering this online, so we do not explicitly enable one by
default, but the Debian manpages repository has a lot of the manpages
pre-rendered, so it is used as an example in the documentation.
The parsing work is done by a transformer class that parses manpage
objects and extract name/section elements. Those then can be used by
writers to cross-reference to actual sites. An implementation is done
in the two HTML writers, but could also apply to ePUB/PDF writers as
well in the future.
This is not enabled by default: the `manpages_url` configuration item
needs to be enabled to point to the chosen site. The `page`, `section`
and `path` parameters are expanded through Python string formatting in
the URL on output.
Unit tests are fairly limited, but should cover most common use-cases.
---
doc/config.rst | 18 ++++++++++++++++++
doc/markup/inline.rst | 3 ++-
sphinx/config.py | 1 +
sphinx/io.py | 6 +++---
sphinx/transforms/__init__.py | 20 ++++++++++++++++++++
sphinx/writers/html.py | 6 ++++++
sphinx/writers/html5.py | 6 ++++++
tests/roots/test-manpage_url/conf.py | 5 +++++
tests/roots/test-manpage_url/index.rst | 3 +++
tests/test_build_html.py | 13 +++++++++++++
10 files changed, 77 insertions(+), 4 deletions(-)
create mode 100644 tests/roots/test-manpage_url/conf.py
create mode 100644 tests/roots/test-manpage_url/index.rst
diff --git a/doc/config.rst b/doc/config.rst
index 9bdd283a9..587d5785b 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -293,6 +293,24 @@ General configuration
.. versionadded:: 1.3
+.. confval:: manpages_url
+
+ A URL to cross-reference :rst:role:`manpage` directives. If this is
+ defined to ``https://manpages.debian.org/{path}``, the
+ :literal:`:manpage:`man(1)`` role will like to
+ . The patterns available are:
+
+ * ``page`` - the manual page (``man``)
+ * ``section`` - the manual section (``1``)
+ * ``path`` - the original manual page and section specified (``man(1)``)
+
+ This also supports manpages specified as ``man.1``.
+
+ .. note:: This currently affects only HTML writers but could be
+ expanded in the future.
+
+ .. versionadded:: 1.7
+
.. confval:: nitpicky
If true, Sphinx will warn about *all* references where the target cannot be
diff --git a/doc/markup/inline.rst b/doc/markup/inline.rst
index 4d14a653d..c8dfb6ff7 100644
--- a/doc/markup/inline.rst
+++ b/doc/markup/inline.rst
@@ -355,7 +355,8 @@ in a different style:
.. rst:role:: manpage
A reference to a Unix manual page including the section,
- e.g. ``:manpage:`ls(1)```.
+ e.g. ``:manpage:`ls(1)```. Creates a hyperlink to an external site
+ rendering the manpage if :confval:`manpages_url` is defined.
.. rst:role:: menuselection
diff --git a/sphinx/config.py b/sphinx/config.py
index c6bf1cc3c..1b3f51a6e 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -125,6 +125,7 @@ class Config(object):
primary_domain = ('py', 'env', [NoneType]),
needs_sphinx = (None, None, string_classes),
needs_extensions = ({}, None),
+ manpages_url = (None, 'env'),
nitpicky = (False, None),
nitpick_ignore = ([], None),
numfig = (False, 'env'),
diff --git a/sphinx/io.py b/sphinx/io.py
index 66ba8334e..3c32c167c 100644
--- a/sphinx/io.py
+++ b/sphinx/io.py
@@ -23,7 +23,7 @@ from sphinx.transforms import (
ApplySourceWorkaround, ExtraTranslatableNodes, CitationReferences,
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, SortIds,
AutoNumbering, AutoIndexUpgrader, FilterSystemMessages,
- UnreferencedFootnotesDetector, SphinxSmartQuotes
+ UnreferencedFootnotesDetector, SphinxSmartQuotes, ManpageLink
)
from sphinx.transforms.compact_bullet_list import RefOnlyBulletListTransform
from sphinx.transforms.i18n import (
@@ -80,7 +80,7 @@ class SphinxStandaloneReader(SphinxBaseReader):
Locale, CitationReferences, DefaultSubstitutions, MoveModuleTargets,
HandleCodeBlocks, AutoNumbering, AutoIndexUpgrader, SortIds,
RemoveTranslatableInline, PreserveTranslatableMessages, FilterSystemMessages,
- RefOnlyBulletListTransform, UnreferencedFootnotesDetector
+ RefOnlyBulletListTransform, UnreferencedFootnotesDetector, ManpageLink
] # type: List[Transform]
def __init__(self, app, *args, **kwargs):
@@ -110,7 +110,7 @@ class SphinxI18nReader(SphinxBaseReader):
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks,
AutoNumbering, SortIds, RemoveTranslatableInline,
FilterSystemMessages, RefOnlyBulletListTransform,
- UnreferencedFootnotesDetector]
+ UnreferencedFootnotesDetector, ManpageLink]
def set_lineno_for_reporter(self, lineno):
# type: (int) -> None
diff --git a/sphinx/transforms/__init__.py b/sphinx/transforms/__init__.py
index acfff6a4d..ceb8de364 100644
--- a/sphinx/transforms/__init__.py
+++ b/sphinx/transforms/__init__.py
@@ -9,6 +9,8 @@
:license: BSD, see LICENSE for details.
"""
+import re
+
from docutils import nodes
from docutils.transforms import Transform, Transformer
from docutils.transforms.parts import ContentsFilter
@@ -348,3 +350,21 @@ class SphinxSmartQuotes(SmartQuotes):
for txtnode in txtnodes:
notsmartquotable = not is_smartquotable(txtnode)
yield (texttype[notsmartquotable], txtnode.astext())
+
+
+class ManpageLink(SphinxTransform):
+ """Find manpage section numbers and names"""
+ default_priority = 999
+
+ def apply(self):
+ for node in self.document.traverse(addnodes.manpage):
+ manpage = ' '.join([str(x) for x in node.children
+ if isinstance(x, nodes.Text)])
+ pattern = r'^(?P(?P.+)[\(\.](?P[1-9]\w*)?\)?)$' # noqa
+ info = {'path': manpage,
+ 'page': manpage,
+ 'section': ''}
+ r = re.match(pattern, manpage)
+ if r:
+ info = r.groupdict()
+ node.attributes.update(info)
diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py
index b3d27e31a..84e7bfbc9 100644
--- a/sphinx/writers/html.py
+++ b/sphinx/writers/html.py
@@ -79,6 +79,7 @@ class HTMLTranslator(BaseTranslator):
self.highlightopts = builder.config.highlight_options
self.highlightlinenothreshold = sys.maxsize
self.docnames = [builder.current_docname] # for singlehtml builder
+ self.manpages_url = builder.config.manpages_url
self.protect_literal_text = 0
self.permalink_text = builder.config.html_add_permalinks
# support backwards-compatible setting to a bool
@@ -816,9 +817,14 @@ class HTMLTranslator(BaseTranslator):
def visit_manpage(self, node):
# type: (nodes.Node) -> None
self.visit_literal_emphasis(node)
+ if self.manpages_url:
+ node['refuri'] = self.manpages_url.format(**node.attributes)
+ self.visit_reference(node)
def depart_manpage(self, node):
# type: (nodes.Node) -> None
+ if self.manpages_url:
+ self.depart_reference(node)
self.depart_literal_emphasis(node)
# overwritten to add even/odd classes
diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py
index a47fee77e..50bf2ea8c 100644
--- a/sphinx/writers/html5.py
+++ b/sphinx/writers/html5.py
@@ -49,6 +49,7 @@ class HTML5Translator(BaseTranslator):
self.highlightopts = builder.config.highlight_options
self.highlightlinenothreshold = sys.maxsize
self.docnames = [builder.current_docname] # for singlehtml builder
+ self.manpages_url = builder.config.manpages_url
self.protect_literal_text = 0
self.permalink_text = builder.config.html_add_permalinks
# support backwards-compatible setting to a bool
@@ -758,9 +759,14 @@ class HTML5Translator(BaseTranslator):
def visit_manpage(self, node):
# type: (nodes.Node) -> None
self.visit_literal_emphasis(node)
+ if self.manpages_url:
+ node['refuri'] = self.manpages_url.format(**dict(node))
+ self.visit_reference(node)
def depart_manpage(self, node):
# type: (nodes.Node) -> None
+ if self.manpages_url:
+ self.depart_reference(node)
self.depart_literal_emphasis(node)
# overwritten to add even/odd classes
diff --git a/tests/roots/test-manpage_url/conf.py b/tests/roots/test-manpage_url/conf.py
new file mode 100644
index 000000000..c46e40773
--- /dev/null
+++ b/tests/roots/test-manpage_url/conf.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+
+master_doc = 'index'
+html_theme = 'classic'
+exclude_patterns = ['_build']
diff --git a/tests/roots/test-manpage_url/index.rst b/tests/roots/test-manpage_url/index.rst
new file mode 100644
index 000000000..50d3b042e
--- /dev/null
+++ b/tests/roots/test-manpage_url/index.rst
@@ -0,0 +1,3 @@
+ * :manpage:`man(1)`
+ * :manpage:`ls.1`
+ * :manpage:`sphinx`
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 8265c8471..153ff5165 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -1243,3 +1243,16 @@ def test_html_sidebar(app, status, warning):
assert 'Related Topics
' not in result
assert 'This Page
' not in result
assert 'Quick search
' not in result
+
+
+@pytest.mark.parametrize('fname,expect', flat_dict({
+ 'index.html': [(".//em/a[@href='https://example.com/man.1']", "", True),
+ (".//em/a[@href='https://example.com/ls.1']", "", True),
+ (".//em/a[@href='https://example.com/sphinx.']", "", True)]
+ }))
+@pytest.mark.sphinx('html', testroot='manpage_url', confoverrides={
+ 'manpages_url': 'https://example.com/{page}.{section}'})
+@pytest.mark.test_params(shared_result='test_build_html_manpage_url')
+def test_html_manpage(app, cached_etree_parse, fname, expect):
+ app.build()
+ check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
From a83e8bab7d03d209f76cc92ffe29a9b89289cf6f Mon Sep 17 00:00:00 2001
From: Stephen Finucane
Date: Thu, 28 Dec 2017 20:45:02 +0000
Subject: [PATCH 68/95] builders: Add 'Builder.epilog' option
This allows builders to emit a final epilog message containing
information such as where resulting files can be found. This is only
emitted if the build was successful.
This allows us to remove this content from the 'make_mode' tool and
the legacy 'Makefile' and 'make.bat' templates. There's room for more
dramatic simplification of the former, but this will come later.
Signed-off-by: Stephen Finucane
---
doc/extdev/builderapi.rst | 1 +
sphinx/application.py | 7 +++
sphinx/builders/__init__.py | 5 +++
sphinx/builders/applehelp.py | 4 ++
sphinx/builders/changes.py | 1 +
sphinx/builders/devhelp.py | 4 ++
sphinx/builders/dummy.py | 2 +
sphinx/builders/epub3.py | 1 +
sphinx/builders/gettext.py | 1 +
sphinx/builders/html.py | 12 ++++-
sphinx/builders/htmlhelp.py | 2 +
sphinx/builders/latex.py | 6 +++
sphinx/builders/linkcheck.py | 2 +
sphinx/builders/manpage.py | 2 +
sphinx/builders/qthelp.py | 5 +++
sphinx/builders/texinfo.py | 7 +++
sphinx/builders/text.py | 2 +
sphinx/builders/xml.py | 4 ++
sphinx/ext/coverage.py | 6 ++-
sphinx/ext/doctest.py | 2 +
sphinx/make_mode.py | 61 --------------------------
sphinx/templates/quickstart/Makefile_t | 61 --------------------------
sphinx/templates/quickstart/make.bat_t | 50 ---------------------
23 files changed, 73 insertions(+), 175 deletions(-)
diff --git a/doc/extdev/builderapi.rst b/doc/extdev/builderapi.rst
index 668f46698..b8ff0595b 100644
--- a/doc/extdev/builderapi.rst
+++ b/doc/extdev/builderapi.rst
@@ -15,6 +15,7 @@ Builder API
.. autoattribute:: name
.. autoattribute:: format
+ .. autoattribute:: epilog
.. autoattribute:: supported_image_types
These methods are predefined and will be called from the application:
diff --git a/sphinx/application.py b/sphinx/application.py
index e76f101a3..8a22a9e6d 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -338,6 +338,13 @@ class Sphinx(object):
(status, self._warncount)))
else:
logger.info(bold(__('build %s.') % status))
+
+ if self.statuscode == 0 and self.builder.epilog:
+ logger.info('')
+ logger.info(self.builder.epilog % {
+ 'outdir': path.relpath(self.outdir),
+ 'project': self.config.project
+ })
except Exception as err:
# delete the saved env to force a fresh build next time
envfile = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index a1e360d2f..51578a1d6 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -54,6 +54,11 @@ class Builder(object):
name = '' # type: unicode
#: The builder's output format, or '' if no document output is produced.
format = '' # type: unicode
+ #: The message emitted upon successful build completion. This can be a
+ #: printf-style template string with the following keys: ``outdir``,
+ #: ``project``
+ epilog = '' # type: unicode
+
# default translator class for the builder. This will be overrided by
# ``app.set_translator()``.
default_translator_class = None # type: nodes.NodeVisitor
diff --git a/sphinx/builders/applehelp.py b/sphinx/builders/applehelp.py
index 52ba2ce5c..0426be331 100644
--- a/sphinx/builders/applehelp.py
+++ b/sphinx/builders/applehelp.py
@@ -75,6 +75,10 @@ class AppleHelpBuilder(StandaloneHTMLBuilder):
on the ``hiutil`` command line tool.
"""
name = 'applehelp'
+ epilog = ('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.')
# don't copy the reST source
copysource = False
diff --git a/sphinx/builders/changes.py b/sphinx/builders/changes.py
index 5309649c6..ff80250a3 100644
--- a/sphinx/builders/changes.py
+++ b/sphinx/builders/changes.py
@@ -38,6 +38,7 @@ class ChangesBuilder(Builder):
Write a summary with all versionadded/changed directives.
"""
name = 'changes'
+ epilog = 'The overview file is in %(outdir)s.'
def init(self):
# type: () -> None
diff --git a/sphinx/builders/devhelp.py b/sphinx/builders/devhelp.py
index 88a9be219..c5e9eb6ea 100644
--- a/sphinx/builders/devhelp.py
+++ b/sphinx/builders/devhelp.py
@@ -43,6 +43,10 @@ class DevhelpBuilder(StandaloneHTMLBuilder):
Builder that also outputs GNOME Devhelp file.
"""
name = 'devhelp'
+ epilog = ('To view the help file:\n'
+ '$ mkdir -p $HOME/.local/share/devhelp/%(project)s\n'
+ '$ ln -s %(outdir)s $HOME/.local/share/devhelp/%(project)s\n'
+ '$ devhelp')
# don't copy the reST source
copysource = False
diff --git a/sphinx/builders/dummy.py b/sphinx/builders/dummy.py
index 74a3d4187..08d99a584 100644
--- a/sphinx/builders/dummy.py
+++ b/sphinx/builders/dummy.py
@@ -21,6 +21,8 @@ if False:
class DummyBuilder(Builder):
name = 'dummy'
+ epilog = 'The dummy builder generates no files.'
+
allow_parallel = True
def init(self):
diff --git a/sphinx/builders/epub3.py b/sphinx/builders/epub3.py
index 92c55c880..c98c4b853 100644
--- a/sphinx/builders/epub3.py
+++ b/sphinx/builders/epub3.py
@@ -63,6 +63,7 @@ class Epub3Builder(_epub_base.EpubBuilder):
an epub file.
"""
name = 'epub'
+ epilog = 'The ePub file is in %(outdir)s.'
supported_remote_images = False
template_dir = path.join(package_dir, 'templates', 'epub3')
diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py
index 464d574cc..f7f0d6811 100644
--- a/sphinx/builders/gettext.py
+++ b/sphinx/builders/gettext.py
@@ -214,6 +214,7 @@ class MessageCatalogBuilder(I18nBuilder):
Builds gettext-style message catalogs (.pot files).
"""
name = 'gettext'
+ epilog = 'The message catalogs are in %(outdir)s.'
def init(self):
# type: () -> None
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index f9c9420c2..dcbc59280 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -153,6 +153,8 @@ class StandaloneHTMLBuilder(Builder):
"""
name = 'html'
format = 'html'
+ epilog = 'The HTML pages are in %(outdir)s.'
+
copysource = True
allow_parallel = True
out_suffix = '.html'
@@ -1066,6 +1068,8 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
HTML page.
"""
name = 'singlehtml'
+ epilog = 'The HTML page is in %(outdir)s.'
+
copysource = False
def get_outdated_docs(self): # type: ignore
@@ -1328,12 +1332,14 @@ 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
- name = 'pickle'
out_suffix = '.fpickle'
globalcontext_filename = 'globalcontext.pickle'
searchindex_filename = 'searchindex.pickle'
@@ -1347,11 +1353,13 @@ 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
- name = 'json'
out_suffix = '.fjson'
globalcontext_filename = 'globalcontext.json'
searchindex_filename = 'searchindex.json'
diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py
index 0b45601e3..63fba05f3 100644
--- a/sphinx/builders/htmlhelp.py
+++ b/sphinx/builders/htmlhelp.py
@@ -174,6 +174,8 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder):
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
diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py
index 8fdb2fa49..088f5d9ef 100644
--- a/sphinx/builders/latex.py
+++ b/sphinx/builders/latex.py
@@ -49,6 +49,12 @@ class LaTeXBuilder(Builder):
"""
name = 'latex'
format = 'latex'
+ epilog = 'The LaTeX files are in %(outdir)s.'
+ if os.name == 'posix':
+ epilog += ("\nRun 'make' in that directory to run these through "
+ "(pdf)latex\n"
+ "(use `make latexpdf' here to do that automatically).")
+
supported_image_types = ['application/pdf', 'image/png', 'image/jpeg']
supported_remote_images = False
default_translator_class = LaTeXTranslator
diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py
index ca62b9fe1..c1a47607b 100644
--- a/sphinx/builders/linkcheck.py
+++ b/sphinx/builders/linkcheck.py
@@ -90,6 +90,8 @@ class CheckExternalLinksBuilder(Builder):
Checks for broken external links.
"""
name = 'linkcheck'
+ epilog = ('Look for any errors in the above output or in '
+ '%(outdir)s/output.txt')
def init(self):
# type: () -> None
diff --git a/sphinx/builders/manpage.py b/sphinx/builders/manpage.py
index b57a756ee..8f7800846 100644
--- a/sphinx/builders/manpage.py
+++ b/sphinx/builders/manpage.py
@@ -40,6 +40,8 @@ class ManualPageBuilder(Builder):
"""
name = 'man'
format = 'man'
+ epilog = 'The manual pages are in %(outdir)s.'
+
default_translator_class = ManualPageTranslator
supported_image_types = [] # type: List[unicode]
diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py
index 2f56792a9..9d08df2a3 100644
--- a/sphinx/builders/qthelp.py
+++ b/sphinx/builders/qthelp.py
@@ -108,6 +108,11 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
Builder that also outputs Qt help project, contents and index files.
"""
name = 'qthelp'
+ epilog = ('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')
# don't copy the reST source
copysource = False
diff --git a/sphinx/builders/texinfo.py b/sphinx/builders/texinfo.py
index 82c6f1b9d..39653c117 100644
--- a/sphinx/builders/texinfo.py
+++ b/sphinx/builders/texinfo.py
@@ -9,6 +9,7 @@
:license: BSD, see LICENSE for details.
"""
+import os
from os import path
from docutils import nodes
@@ -97,6 +98,12 @@ class TexinfoBuilder(Builder):
"""
name = 'texinfo'
format = 'texinfo'
+ epilog = 'The Texinfo files are in %(outdir)s.'
+ if os.name == 'posix':
+ epilog += ("\nRun 'make' in that directory to run these through "
+ "makeinfo\n"
+ "(use 'make info' here to do that automatically).")
+
supported_image_types = ['image/png', 'image/jpeg',
'image/gif']
default_translator_class = TexinfoTranslator
diff --git a/sphinx/builders/text.py b/sphinx/builders/text.py
index 7b977b1b9..a7ecd0e68 100644
--- a/sphinx/builders/text.py
+++ b/sphinx/builders/text.py
@@ -31,6 +31,8 @@ logger = logging.getLogger(__name__)
class TextBuilder(Builder):
name = 'text'
format = 'text'
+ epilog = 'The text files are in %(outdir)s.'
+
out_suffix = '.txt'
allow_parallel = True
default_translator_class = TextTranslator
diff --git a/sphinx/builders/xml.py b/sphinx/builders/xml.py
index 599530ac1..80d7723aa 100644
--- a/sphinx/builders/xml.py
+++ b/sphinx/builders/xml.py
@@ -35,6 +35,8 @@ class XMLBuilder(Builder):
"""
name = 'xml'
format = 'xml'
+ epilog = 'The XML files are in %(outdir)s.'
+
out_suffix = '.xml'
allow_parallel = True
@@ -108,6 +110,8 @@ class PseudoXMLBuilder(XMLBuilder):
"""
name = 'pseudoxml'
format = 'pseudoxml'
+ epilog = 'The pseudo-XML files are in %(outdir)s.'
+
out_suffix = '.pseudoxml'
_writer_class = PseudoXMLWriter
diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py
index 476a0ed46..74d004ad1 100644
--- a/sphinx/ext/coverage.py
+++ b/sphinx/ext/coverage.py
@@ -50,8 +50,12 @@ def compile_regex_list(name, exps):
class CoverageBuilder(Builder):
-
+ """
+ Evaluates coverage of code in the documentation.
+ """
name = 'coverage'
+ epilog = ('Testing of coverage in the sources finished, look at the '
+ 'results in %(outdir)s/python.txt.')
def init(self):
# type: () -> None
diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py
index e0ce050f7..b104cfc08 100644
--- a/sphinx/ext/doctest.py
+++ b/sphinx/ext/doctest.py
@@ -278,6 +278,8 @@ class DocTestBuilder(Builder):
Runs test snippets in the documentation.
"""
name = 'doctest'
+ epilog = ('Testing of doctests in the sources finished, look at the '
+ 'results in %(outdir)s/output.txt.')
def init(self):
# type: () -> None
diff --git a/sphinx/make_mode.py b/sphinx/make_mode.py
index 0bc1a797d..64268c2ec 100644
--- a/sphinx/make_mode.py
+++ b/sphinx/make_mode.py
@@ -101,95 +101,60 @@ class Make(object):
# type: () -> int
if self.run_generic_build('html') > 0:
return 1
- print()
- print('Build finished. The HTML pages are in %s.' % self.builddir_join('html'))
return 0
def build_dirhtml(self):
# type: () -> int
if self.run_generic_build('dirhtml') > 0:
return 1
- print()
- print('Build finished. The HTML pages are in %s.' %
- self.builddir_join('dirhtml'))
return 0
def build_singlehtml(self):
# type: () -> int
if self.run_generic_build('singlehtml') > 0:
return 1
- print()
- print('Build finished. The HTML page is in %s.' %
- self.builddir_join('singlehtml'))
return 0
def build_pickle(self):
# type: () -> int
if self.run_generic_build('pickle') > 0:
return 1
- print()
- print('Build finished; now you can process the pickle files.')
return 0
def build_json(self):
# type: () -> int
if self.run_generic_build('json') > 0:
return 1
- print()
- print('Build finished; now you can process the JSON files.')
return 0
def build_htmlhelp(self):
# type: () -> int
if self.run_generic_build('htmlhelp') > 0:
return 1
- print()
- print('Build finished; now you can run HTML Help Workshop with the '
- '.hhp project file in %s.' % self.builddir_join('htmlhelp'))
return 0
def build_qthelp(self):
# type: () -> int
if self.run_generic_build('qthelp') > 0:
return 1
- print()
- print('Build finished; now you can run "qcollectiongenerator" with the '
- '.qhcp project file in %s, like this:' % self.builddir_join('qthelp'))
- print('$ qcollectiongenerator %s.qhcp' % self.builddir_join('qthelp', proj_name))
- print('To view the help file:')
- print('$ assistant -collectionFile %s.qhc' %
- self.builddir_join('qthelp', proj_name))
return 0
def build_devhelp(self):
# type: () -> int
if self.run_generic_build('devhelp') > 0:
return 1
- print()
- print("Build finished.")
- print("To view the help file:")
- print("$ mkdir -p $HOME/.local/share/devhelp/" + proj_name)
- print("$ ln -s %s $HOME/.local/share/devhelp/%s" %
- (self.builddir_join('devhelp'), proj_name))
- print("$ devhelp")
return 0
def build_epub(self):
# type: () -> int
if self.run_generic_build('epub') > 0:
return 1
- print()
- print('Build finished. The ePub file is in %s.' % self.builddir_join('epub'))
return 0
def build_latex(self):
# type: () -> int
if self.run_generic_build('latex') > 0:
return 1
- print("Build finished; the LaTeX files are in %s." % self.builddir_join('latex'))
- if os.name == 'posix':
- print("Run `make' in that directory to run these through (pdf)latex")
- print("(use `make latexpdf' here to do that automatically).")
return 0
def build_latexpdf(self):
@@ -210,19 +175,12 @@ class Make(object):
# type: () -> int
if self.run_generic_build('text') > 0:
return 1
- print()
- print('Build finished. The text files are in %s.' % self.builddir_join('text'))
return 0
def build_texinfo(self):
# type: () -> int
if self.run_generic_build('texinfo') > 0:
return 1
- print("Build finished; the Texinfo files are in %s." %
- self.builddir_join('texinfo'))
- if os.name == 'posix':
- print("Run `make' in that directory to run these through makeinfo")
- print("(use `make info' here to do that automatically).")
return 0
def build_info(self):
@@ -237,33 +195,22 @@ class Make(object):
dtdir = self.builddir_join('gettext', '.doctrees')
if self.run_generic_build('gettext', doctreedir=dtdir) > 0:
return 1
- print()
- print('Build finished. The message catalogs are in %s.' %
- self.builddir_join('gettext'))
return 0
def build_changes(self):
# type: () -> int
if self.run_generic_build('changes') > 0:
return 1
- print()
- print('Build finished. The overview file is in %s.' %
- self.builddir_join('changes'))
return 0
def build_linkcheck(self):
# type: () -> int
res = self.run_generic_build('linkcheck')
- print()
- print('Link check complete; look for any errors in the above output '
- 'or in %s.' % self.builddir_join('linkcheck', 'output.txt'))
return res
def build_doctest(self):
# type: () -> int
res = self.run_generic_build('doctest')
- print("Testing of doctests in the sources finished, look at the "
- "results in %s." % self.builddir_join('doctest', 'output.txt'))
return res
def build_coverage(self):
@@ -271,26 +218,18 @@ class Make(object):
if self.run_generic_build('coverage') > 0:
print("Has the coverage extension been enabled?")
return 1
- print()
- print("Testing of coverage in the sources finished, look at the "
- "results in %s." % self.builddir_join('coverage'))
return 0
def build_xml(self):
# type: () -> int
if self.run_generic_build('xml') > 0:
return 1
- print()
- print('Build finished. The XML files are in %s.' % self.builddir_join('xml'))
return 0
def build_pseudoxml(self):
# type: () -> int
if self.run_generic_build('pseudoxml') > 0:
return 1
- print()
- print('Build finished. The pseudo-XML files are in %s.' %
- self.builddir_join('pseudoxml'))
return 0
def run_generic_build(self, builder, doctreedir=None):
diff --git a/sphinx/templates/quickstart/Makefile_t b/sphinx/templates/quickstart/Makefile_t
index 2858d9bf7..77ce4afe8 100644
--- a/sphinx/templates/quickstart/Makefile_t
+++ b/sphinx/templates/quickstart/Makefile_t
@@ -52,82 +52,46 @@ clean:
.PHONY: html
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
.PHONY: dirhtml
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
.PHONY: singlehtml
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
- @echo
- @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
.PHONY: pickle
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
- @echo
- @echo "Build finished; now you can process the pickle files."
.PHONY: json
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
- @echo
- @echo "Build finished; now you can process the JSON files."
.PHONY: htmlhelp
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
- @echo
- @echo "Build finished; now you can run HTML Help Workshop with the" \
- ".hhp project file in $(BUILDDIR)/htmlhelp."
.PHONY: qthelp
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
- @echo
- @echo "Build finished; now you can run "qcollectiongenerator" with the" \
- ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
- @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/{{ project_fn }}.qhcp"
- @echo "To view the help file:"
- @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/{{ project_fn }}.qhc"
.PHONY: applehelp
applehelp:
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
- @echo
- @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
- @echo "N.B. You won't be able to view it unless you put it in" \
- "~/Library/Documentation/Help or install it in your application" \
- "bundle."
.PHONY: devhelp
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
- @echo
- @echo "Build finished."
- @echo "To view the help file:"
- @echo "# mkdir -p $$HOME/.local/share/devhelp/{{ project_fn }}"
- @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/{{ project_fn }}"
- @echo "# devhelp"
.PHONY: epub
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
- @echo
- @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
.PHONY: latex
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo
- @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
- @echo "Run \`make' in that directory to run these through (pdf)latex" \
- "(use \`make latexpdf' here to do that automatically)."
.PHONY: latexpdf
latexpdf:
@@ -160,22 +124,14 @@ xelatexpdf:
.PHONY: text
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
- @echo
- @echo "Build finished. The text files are in $(BUILDDIR)/text."
.PHONY: man
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
- @echo
- @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
.PHONY: texinfo
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo
- @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
- @echo "Run \`make' in that directory to run these through makeinfo" \
- "(use \`make info' here to do that automatically)."
.PHONY: info
info:
@@ -187,49 +143,32 @@ info:
.PHONY: gettext
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
- @echo
- @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
.PHONY: changes
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
- @echo
- @echo "The overview file is in $(BUILDDIR)/changes."
.PHONY: linkcheck
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
- @echo
- @echo "Link check complete; look for any errors in the above output " \
- "or in $(BUILDDIR)/linkcheck/output.txt."
.PHONY: doctest
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
- @echo "Testing of doctests in the sources finished, look at the " \
- "results in $(BUILDDIR)/doctest/output.txt."
.PHONY: coverage
coverage:
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
- @echo "Testing of coverage in the sources finished, look at the " \
- "results in $(BUILDDIR)/coverage/python.txt."
.PHONY: xml
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
- @echo
- @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
.PHONY: pseudoxml
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
- @echo
- @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
.PHONY: dummy
dummy:
$(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
- @echo
- @echo "Build finished. Dummy builder generates no files."
diff --git a/sphinx/templates/quickstart/make.bat_t b/sphinx/templates/quickstart/make.bat_t
index 230977488..94d28461b 100644
--- a/sphinx/templates/quickstart/make.bat_t
+++ b/sphinx/templates/quickstart/make.bat_t
@@ -78,85 +78,60 @@ if errorlevel 9009 (
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run HTML Help Workshop with the ^
-.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run "qcollectiongenerator" with the ^
-.qhcp project file in %BUILDDIR%/qthelp, like this:
- echo.^> qcollectiongenerator %BUILDDIR%\qthelp\{{ project_fn }}.qhcp
- echo.To view the help file:
- echo.^> assistant -collectionFile %BUILDDIR%\qthelp\{{ project_fn }}.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
if errorlevel 1 exit /b 1
- echo.
- echo.Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
@@ -183,91 +158,66 @@ if "%1" == "latexpdfja" (
if "%1" == "text" (
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The text files are in %BUILDDIR%/text.
goto end
)
if "%1" == "man" (
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The manual pages are in %BUILDDIR%/man.
goto end
)
if "%1" == "texinfo" (
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
goto end
)
if "%1" == "gettext" (
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
if errorlevel 1 exit /b 1
- echo.
- echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
if errorlevel 1 exit /b 1
- echo.
- echo.Link check complete; look for any errors in the above output ^
-or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
if errorlevel 1 exit /b 1
- echo.
- echo.Testing of doctests in the sources finished, look at the ^
-results in %BUILDDIR%/doctest/output.txt.
goto end
)
if "%1" == "coverage" (
%SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
if errorlevel 1 exit /b 1
- echo.
- echo.Testing of coverage in the sources finished, look at the ^
-results in %BUILDDIR%/coverage/python.txt.
goto end
)
if "%1" == "xml" (
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The XML files are in %BUILDDIR%/xml.
goto end
)
if "%1" == "pseudoxml" (
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
goto end
)
if "%1" == "dummy" (
%SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy
if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. Dummy builder generates no files.
goto end
)
From 221dffda3c00f5abf64057e350959d123c50ccac Mon Sep 17 00:00:00 2001
From: Stephen Finucane
Date: Thu, 28 Dec 2017 20:45:24 +0000
Subject: [PATCH 69/95] make_mode: Remove unnecessary 'make_*' functions
These are handled by the default case.
Signed-off-by: Stephen Finucane
---
sphinx/make_mode.py | 107 --------------------------------------------
1 file changed, 107 deletions(-)
diff --git a/sphinx/make_mode.py b/sphinx/make_mode.py
index 64268c2ec..4b325160f 100644
--- a/sphinx/make_mode.py
+++ b/sphinx/make_mode.py
@@ -97,66 +97,6 @@ class Make(object):
if not osname or os.name == osname:
print(' %s %s' % (blue(bname.ljust(10)), description))
- def build_html(self):
- # type: () -> int
- if self.run_generic_build('html') > 0:
- return 1
- return 0
-
- def build_dirhtml(self):
- # type: () -> int
- if self.run_generic_build('dirhtml') > 0:
- return 1
- return 0
-
- def build_singlehtml(self):
- # type: () -> int
- if self.run_generic_build('singlehtml') > 0:
- return 1
- return 0
-
- def build_pickle(self):
- # type: () -> int
- if self.run_generic_build('pickle') > 0:
- return 1
- return 0
-
- def build_json(self):
- # type: () -> int
- if self.run_generic_build('json') > 0:
- return 1
- return 0
-
- def build_htmlhelp(self):
- # type: () -> int
- if self.run_generic_build('htmlhelp') > 0:
- return 1
- return 0
-
- def build_qthelp(self):
- # type: () -> int
- if self.run_generic_build('qthelp') > 0:
- return 1
- return 0
-
- def build_devhelp(self):
- # type: () -> int
- if self.run_generic_build('devhelp') > 0:
- return 1
- return 0
-
- def build_epub(self):
- # type: () -> int
- if self.run_generic_build('epub') > 0:
- return 1
- return 0
-
- def build_latex(self):
- # type: () -> int
- if self.run_generic_build('latex') > 0:
- return 1
- return 0
-
def build_latexpdf(self):
# type: () -> int
if self.run_generic_build('latex') > 0:
@@ -171,18 +111,6 @@ class Make(object):
with cd(self.builddir_join('latex')):
return subprocess.call([self.makecmd, 'all-pdf-ja'])
- def build_text(self):
- # type: () -> int
- if self.run_generic_build('text') > 0:
- return 1
- return 0
-
- def build_texinfo(self):
- # type: () -> int
- if self.run_generic_build('texinfo') > 0:
- return 1
- return 0
-
def build_info(self):
# type: () -> int
if self.run_generic_build('texinfo') > 0:
@@ -197,41 +125,6 @@ class Make(object):
return 1
return 0
- def build_changes(self):
- # type: () -> int
- if self.run_generic_build('changes') > 0:
- return 1
- return 0
-
- def build_linkcheck(self):
- # type: () -> int
- res = self.run_generic_build('linkcheck')
- return res
-
- def build_doctest(self):
- # type: () -> int
- res = self.run_generic_build('doctest')
- return res
-
- def build_coverage(self):
- # type: () -> int
- if self.run_generic_build('coverage') > 0:
- print("Has the coverage extension been enabled?")
- return 1
- return 0
-
- def build_xml(self):
- # type: () -> int
- if self.run_generic_build('xml') > 0:
- return 1
- return 0
-
- def build_pseudoxml(self):
- # type: () -> int
- if self.run_generic_build('pseudoxml') > 0:
- return 1
- return 0
-
def run_generic_build(self, builder, doctreedir=None):
# type: (unicode, unicode) -> int
# compatibility with old Makefile
From aa2c5c906566076d3c52c684070b57950b65c7b9 Mon Sep 17 00:00:00 2001
From: Stephen Finucane
Date: Thu, 28 Dec 2017 20:45:27 +0000
Subject: [PATCH 70/95] Makefile: Remove unnecessary targets
Most of these are not necessary now that we're not printing different
messages for various builders.
Signed-off-by: Stephen Finucane
---
sphinx/templates/quickstart/Makefile_t | 92 ++------------------------
1 file changed, 7 insertions(+), 85 deletions(-)
diff --git a/sphinx/templates/quickstart/Makefile_t b/sphinx/templates/quickstart/Makefile_t
index 77ce4afe8..70925c471 100644
--- a/sphinx/templates/quickstart/Makefile_t
+++ b/sphinx/templates/quickstart/Makefile_t
@@ -10,9 +10,10 @@ BUILDDIR = {{ rbuilddir }}
# Internal variables.
PAPEROPT_a4 = -D latex_elements.papersize=a4
PAPEROPT_letter = -D latex_elements.papersize=letter
-ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) {{ rsrcdir }}
+# $(O) is meant as a shortcut for $(SPHINXOPTS)
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(O) {{ rsrcdir }}
# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) {{ rsrcdir }}
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(O) {{ rsrcdir }}
.PHONY: help
help:
@@ -49,50 +50,6 @@ help:
clean:
rm -rf $(BUILDDIR)/*
-.PHONY: html
-html:
- $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
-
-.PHONY: dirhtml
-dirhtml:
- $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
-
-.PHONY: singlehtml
-singlehtml:
- $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
-
-.PHONY: pickle
-pickle:
- $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
-
-.PHONY: json
-json:
- $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
-
-.PHONY: htmlhelp
-htmlhelp:
- $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
-
-.PHONY: qthelp
-qthelp:
- $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
-
-.PHONY: applehelp
-applehelp:
- $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
-
-.PHONY: devhelp
-devhelp:
- $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
-
-.PHONY: epub
-epub:
- $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
-
-.PHONY: latex
-latex:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-
.PHONY: latexpdf
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@@ -121,18 +78,6 @@ xelatexpdf:
$(MAKE) PDFLATEX=xelatex -C $(BUILDDIR)/latex all-pdf
@echo "xelatex finished; the PDF files are in $(BUILDDIR)/latex."
-.PHONY: text
-text:
- $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
-
-.PHONY: man
-man:
- $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
-
-.PHONY: texinfo
-texinfo:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
-
.PHONY: info
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@@ -144,31 +89,8 @@ info:
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
-.PHONY: changes
-changes:
- $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
-
-.PHONY: linkcheck
-linkcheck:
- $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
-
-.PHONY: doctest
-doctest:
- $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
-
-.PHONY: coverage
-coverage:
- $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
-
-.PHONY: xml
-xml:
- $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
-
-.PHONY: pseudoxml
-pseudoxml:
- $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
-
-.PHONY: dummy
-dummy:
- $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
+# Catch-all target: route all unknown targets to Sphinx
+.PHONY: Makefile
+%: Makefile
+ $(SPHINXBUILD) -b "$@" $(ALLSPHINXOPTS) "$(BUILDDIR)/$@"
From 6f9a262f45a6c7f297bf0c6c4b8a642d0bb8f02a Mon Sep 17 00:00:00 2001
From: Stephen Finucane
Date: Thu, 28 Dec 2017 20:45:43 +0000
Subject: [PATCH 71/95] make.bat: Remove unnecessary targets
As with the Makefile previously, these are not necessary now that we're
not printing anything different for various builders.
Signed-off-by: Stephen Finucane
---
sphinx/templates/quickstart/make.bat_t | 123 +------------------------
1 file changed, 2 insertions(+), 121 deletions(-)
diff --git a/sphinx/templates/quickstart/make.bat_t b/sphinx/templates/quickstart/make.bat_t
index 94d28461b..4af8fb8c2 100644
--- a/sphinx/templates/quickstart/make.bat_t
+++ b/sphinx/templates/quickstart/make.bat_t
@@ -50,7 +50,6 @@ if "%1" == "clean" (
goto end
)
-
REM Check if sphinx-build is available and fallback to Python version if any
%SPHINXBUILD% 1>NUL 2>NUL
if errorlevel 9009 goto sphinx_python
@@ -74,67 +73,6 @@ if errorlevel 9009 (
:sphinx_ok
-
-if "%1" == "html" (
- %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
- if errorlevel 1 exit /b 1
- goto end
-)
-
-if "%1" == "dirhtml" (
- %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
- if errorlevel 1 exit /b 1
- goto end
-)
-
-if "%1" == "singlehtml" (
- %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
- if errorlevel 1 exit /b 1
- goto end
-)
-
-if "%1" == "pickle" (
- %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
- if errorlevel 1 exit /b 1
- goto end
-)
-
-if "%1" == "json" (
- %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
- if errorlevel 1 exit /b 1
- goto end
-)
-
-if "%1" == "htmlhelp" (
- %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
- if errorlevel 1 exit /b 1
- goto end
-)
-
-if "%1" == "qthelp" (
- %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
- if errorlevel 1 exit /b 1
- goto end
-)
-
-if "%1" == "devhelp" (
- %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
- if errorlevel 1 exit /b 1
- goto end
-)
-
-if "%1" == "epub" (
- %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
- if errorlevel 1 exit /b 1
- goto end
-)
-
-if "%1" == "latex" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- if errorlevel 1 exit /b 1
- goto end
-)
-
if "%1" == "latexpdf" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
cd %BUILDDIR%/latex
@@ -155,71 +93,14 @@ if "%1" == "latexpdfja" (
goto end
)
-if "%1" == "text" (
- %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
- if errorlevel 1 exit /b 1
- goto end
-)
-
-if "%1" == "man" (
- %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
- if errorlevel 1 exit /b 1
- goto end
-)
-
-if "%1" == "texinfo" (
- %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
- if errorlevel 1 exit /b 1
- goto end
-)
-
if "%1" == "gettext" (
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
if errorlevel 1 exit /b 1
goto end
)
-if "%1" == "changes" (
- %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
- if errorlevel 1 exit /b 1
- goto end
-)
-
-if "%1" == "linkcheck" (
- %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
- if errorlevel 1 exit /b 1
- goto end
-)
-
-if "%1" == "doctest" (
- %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
- if errorlevel 1 exit /b 1
- goto end
-)
-
-if "%1" == "coverage" (
- %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
- if errorlevel 1 exit /b 1
- goto end
-)
-
-if "%1" == "xml" (
- %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
- if errorlevel 1 exit /b 1
- goto end
-)
-
-if "%1" == "pseudoxml" (
- %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
- if errorlevel 1 exit /b 1
- goto end
-)
-
-if "%1" == "dummy" (
- %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy
- if errorlevel 1 exit /b 1
- goto end
-)
+%SPHINXBUILD% -b %1 %ALLSPHINXOPTS% %BUILDDIR%/%1
+goto end
:end
popd
From f6a045d1714f122d9579f6bd71d0599b8a6d7acb Mon Sep 17 00:00:00 2001
From: Stephen Finucane
Date: Thu, 28 Dec 2017 20:45:50 +0000
Subject: [PATCH 72/95] Makefile: Make SOURCEDIR configurable
It's unlikely that anyone will need to do this but at least give them
the opportunity.
Signed-off-by: Stephen Finucane
---
sphinx/templates/quickstart/Makefile_t | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/sphinx/templates/quickstart/Makefile_t b/sphinx/templates/quickstart/Makefile_t
index 70925c471..bf752404e 100644
--- a/sphinx/templates/quickstart/Makefile_t
+++ b/sphinx/templates/quickstart/Makefile_t
@@ -5,15 +5,16 @@
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
PAPER ?=
+SOURCEDIR = {{ rsrcdir }}
BUILDDIR = {{ rbuilddir }}
# Internal variables.
PAPEROPT_a4 = -D latex_elements.papersize=a4
PAPEROPT_letter = -D latex_elements.papersize=letter
# $(O) is meant as a shortcut for $(SPHINXOPTS)
-ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(O) {{ rsrcdir }}
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(O) $(SOURCEDIR)
# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(O) {{ rsrcdir }}
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(O) $(SOURCEDIR)
.PHONY: help
help:
From b16fd2ce0191ada6158b87faf1473bd00315ec3d Mon Sep 17 00:00:00 2001
From: Stephen Finucane
Date: Thu, 28 Dec 2017 21:00:38 +0000
Subject: [PATCH 73/95] make.bat: Make SOURCEDIR configurable
It's unlikely that anyone will need to do this but at least give them
the opportunity.
Signed-off-by: Stephen Finucane
---
sphinx/templates/quickstart/make.bat_t | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/sphinx/templates/quickstart/make.bat_t b/sphinx/templates/quickstart/make.bat_t
index 4af8fb8c2..6e8665a49 100644
--- a/sphinx/templates/quickstart/make.bat_t
+++ b/sphinx/templates/quickstart/make.bat_t
@@ -8,8 +8,9 @@ if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR={{ rbuilddir }}
-set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% {{ rsrcdir }}
-set I18NSPHINXOPTS=%SPHINXOPTS% {{ rsrcdir }}
+set SOURCEDIR={{ rsrcdir }}
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% %SOURCEDIR%
+set I18NSPHINXOPTS=%SPHINXOPTS% %SOURCEDIR%
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_elements.papersize=%PAPER% %ALLSPHINXOPTS%
set I18NSPHINXOPTS=-D latex_elements.papersize=%PAPER% %I18NSPHINXOPTS%
From 32aa664bdf892817f876a9689d7c31da168cf393 Mon Sep 17 00:00:00 2001
From: deoren
Date: Thu, 11 Jan 2018 15:19:04 -0600
Subject: [PATCH 74/95] Small label/comma fix
---
doc/intro.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/intro.rst b/doc/intro.rst
index d3b191700..a789145fe 100644
--- a/doc/intro.rst
+++ b/doc/intro.rst
@@ -3,7 +3,7 @@ Introduction
This is the documentation for the Sphinx documentation builder. Sphinx is a
tool that translates a set of reStructuredText_ source files into various output
-formats, automatically producing cross-references, indices etc. That is, if
+formats, automatically producing cross-references, indices, etc. That is, if
you have a directory containing a bunch of reST-formatted documents (and
possibly subdirectories of docs in there as well), Sphinx can generate a
nicely-organized arrangement of HTML files (in some other directory) for easy
@@ -38,7 +38,7 @@ to reStructuredText/Sphinx from other documentation systems.
code to convert Python-doc-style LaTeX markup to Sphinx reST.
* Marcin Wojdyr has written a script to convert Docbook to reST with Sphinx
- markup; it is at `Google Code `_.
+ markup; it is at `GitHub `_.
* Christophe de Vienne wrote a tool to convert from Open/LibreOffice documents
to Sphinx: `odt2sphinx `_.
From 8f2d4bae86164456a32c68d4dda70b84bd26b303 Mon Sep 17 00:00:00 2001
From: jfbu
Date: Fri, 12 Jan 2018 12:53:07 +0100
Subject: [PATCH 75/95] Eliminate comma from PDF header when project has no
release number
---
sphinx/texinputs/sphinx.sty | 33 ++++++++++++++++++++++-----------
1 file changed, 22 insertions(+), 11 deletions(-)
diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty
index 2b41673db..19d0a2e2d 100644
--- a/sphinx/texinputs/sphinx.sty
+++ b/sphinx/texinputs/sphinx.sty
@@ -504,7 +504,7 @@
\fancyfoot[LE,RO]{{\py@HeaderFamily\thepage}}
\fancyfoot[LO]{{\py@HeaderFamily\nouppercase{\rightmark}}}
\fancyfoot[RE]{{\py@HeaderFamily\nouppercase{\leftmark}}}
- \fancyhead[LE,RO]{{\py@HeaderFamily \@title, \py@release}}
+ \fancyhead[LE,RO]{{\py@HeaderFamily \@title\sphinxheadercomma\py@release}}
\renewcommand{\headrulewidth}{0.4pt}
\renewcommand{\footrulewidth}{0.4pt}
% define chaptermark with \@chappos when \@chappos is available for Japanese
@@ -1401,18 +1401,29 @@
% \date{}. This allows the date to reflect the document's date and
% release to specify the release that is documented.
%
-\newcommand{\py@release}{}
-\newcommand{\version}{}
+\newcommand{\py@release}{\releasename\space\version}
+\newcommand{\version}{}% part of \py@release, used by title page and headers
+% these two are not used and not documented:
\newcommand{\shortversion}{}
+\newcommand{\setshortversion}[1]{\renewcommand{\shortversion}{#1}}
+% this one is not documented, but used in sphinxmanual.cls and sphinxhowto.cls
\newcommand{\releaseinfo}{}
-\newcommand{\releasename}{Release}
-\newcommand{\release}[1]{%
- \renewcommand{\py@release}{\releasename\space\version}%
- \renewcommand{\version}{#1}}
-\newcommand{\setshortversion}[1]{%
- \renewcommand{\shortversion}{#1}}
-\newcommand{\setreleaseinfo}[1]{%
- \renewcommand{\releaseinfo}{#1}}
+\newcommand{\setreleaseinfo}[1]{\renewcommand{\releaseinfo}{#1}}% not used
+% this is inserted via template and #1=release config variable
+\newcommand{\release}[1]{\renewcommand{\version}{#1}}
+% this is defined by template to 'releasename' latex_elements key
+\newcommand{\releasename}{}
+% Fix issue in case release and releasename deliberately left blank
+\newcommand{\sphinxheadercomma}{, }% used in fancyhdr header definition
+\newcommand{\sphinxifemptyorblank}[1]{%
+% test after one expansion of macro #1 if contents is empty or spaces
+ \if&\expandafter\@firstofone\detokenize\expandafter{#1}&%
+ \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi}%
+\AtBeginDocument {%
+ \sphinxifemptyorblank{\releasename}
+ {\sphinxifemptyorblank{\version}{\let\sphinxheadercomma\empty}{}}
+ {}%
+}%
% Allow specification of the author's address separately from the
% author's name. This can be used to format them differently, which
From 12a8d5c0bdd4c5c784846e76b20dc5b596802be5 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sat, 13 Jan 2018 11:09:27 +0900
Subject: [PATCH 76/95] doc: make looks understandable
---
doc/ext/inheritance.rst | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/doc/ext/inheritance.rst b/doc/ext/inheritance.rst
index 87bfd9eef..0062a8afa 100644
--- a/doc/ext/inheritance.rst
+++ b/doc/ext/inheritance.rst
@@ -74,9 +74,8 @@ It adds this directive:
If you have specified a module in the inheritance diagram like this::
- .. inheritance-diagram::
- dummy.test
- :top-classes: dummy.test.B, dummy.test.C
+ .. inheritance-diagram:: dummy.test
+ :top-classes: dummy.test.B, dummy.test.C
any base classes which are ancestors to ``top-classes`` and are also defined
in the same module will be rendered as stand alone nodes. In this example
@@ -86,11 +85,8 @@ It adds this directive:
If you don't want class A (or any other ancestors) to be visible then specify
only the classes you would like to generate the diagram for like this::
- .. inheritance-diagram::
- dummy.test.D
- dummy.test.E
- dummy.test.F
- :top-classes: dummy.test.B, dummy.test.C
+ .. inheritance-diagram:: dummy.test.D dummy.test.E dummy.test.F
+ :top-classes: dummy.test.B, dummy.test.C
.. versionchanged:: 1.7
Added ``top-classes`` option to limit the scope of inheritance graphs.
From 7292386a03bc23f1dc31da3606af5268d949efe1 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sat, 13 Jan 2018 11:23:33 +0900
Subject: [PATCH 77/95] Fix #3570: autodoc: Do not display typing. module for
type hints
---
CHANGES | 1 +
sphinx/util/inspect.py | 14 +++++++++++---
tests/test_util_inspect.py | 12 ++++++------
3 files changed, 18 insertions(+), 9 deletions(-)
diff --git a/CHANGES b/CHANGES
index cbecd1df1..9f1684579 100644
--- a/CHANGES
+++ b/CHANGES
@@ -67,6 +67,7 @@ Features added
* #4093: sphinx-build creates empty directories for unknown targets/builders
* Add ``top-classes`` option for the ``sphinx.ext.inheritance_diagram``
extension to limit the scope of inheritance graphs.
+* #3570: autodoc: Do not display 'typing.' module for type hints
Features removed
----------------
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index 62cd1a7e9..8c10b7aa5 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -404,10 +404,18 @@ class Signature(object):
if annotation == Ellipsis:
return '...'
if not isinstance(annotation, type):
- return repr(annotation)
+ qualified_name = repr(annotation)
+ if qualified_name.startswith('typing.'): # for typing.Union
+ return qualified_name.split('.', 1)[1]
+ else:
+ return qualified_name
- qualified_name = (annotation.__module__ + '.' + annotation.__qualname__ # type: ignore
- if annotation else repr(annotation))
+ if not annotation:
+ qualified_name = repr(annotation)
+ elif annotation.__module__ == 'typing':
+ qualified_name = annotation.__qualname__ # type: ignore
+ else:
+ qualified_name = (annotation.__module__ + '.' + annotation.__qualname__) # type: ignore # NOQA
if annotation.__module__ == 'builtins':
return annotation.__qualname__ # type: ignore
diff --git a/tests/test_util_inspect.py b/tests/test_util_inspect.py
index f0188cafa..b5d50ed71 100644
--- a/tests/test_util_inspect.py
+++ b/tests/test_util_inspect.py
@@ -211,15 +211,15 @@ def test_Signature_annotations():
# Generic types with concrete parameters
sig = inspect.Signature(f1).format_args()
- assert sig == '(x: typing.List[int]) -> typing.List[int]'
+ assert sig == '(x: List[int]) -> List[int]'
# TypeVars and generic types with TypeVars
sig = inspect.Signature(f2).format_args()
- assert sig == '(x: typing.List[T], y: typing.List[T_co], z: T) -> typing.List[T_contra]'
+ assert sig == '(x: List[T], y: List[T_co], z: T) -> List[T_contra]'
# Union types
sig = inspect.Signature(f3).format_args()
- assert sig == '(x: typing.Union[str, numbers.Integral]) -> None'
+ assert sig == '(x: Union[str, numbers.Integral]) -> None'
# Quoted annotations
sig = inspect.Signature(f4).format_args()
@@ -239,14 +239,14 @@ def test_Signature_annotations():
# Callable types
sig = inspect.Signature(f8).format_args()
- assert sig == '(x: typing.Callable[[int, str], int]) -> None'
+ assert sig == '(x: Callable[[int, str], int]) -> None'
sig = inspect.Signature(f9).format_args()
- assert sig == '(x: typing.Callable) -> None'
+ assert sig == '(x: Callable) -> None'
# Tuple types
sig = inspect.Signature(f10).format_args()
- assert sig == '(x: typing.Tuple[int, str], y: typing.Tuple[int, ...]) -> None'
+ assert sig == '(x: Tuple[int, str], y: Tuple[int, ...]) -> None'
# Instance annotations
sig = inspect.Signature(f11).format_args()
From 2324dee1432b757abe106851484356e4703d4005 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sat, 13 Jan 2018 14:29:54 +0900
Subject: [PATCH 78/95] Update CHANGES for PR #4235
---
CHANGES | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGES b/CHANGES
index 19c56f35c..9a435bfd4 100644
--- a/CHANGES
+++ b/CHANGES
@@ -73,6 +73,7 @@ Features added
* Add ``top-classes`` option for the ``sphinx.ext.inheritance_diagram``
extension to limit the scope of inheritance graphs.
* #4183: doctest: ``:pyversion:`` option also follows PEP-440 specification
+* #4235: html: Add :confval:`manpages_url` to make manpage roles to hyperlinks
Features removed
----------------
From f2bc93859aea6e076420e8e7696cba1c42e8af06 Mon Sep 17 00:00:00 2001
From: jfbu
Date: Sat, 13 Jan 2018 08:53:16 +0100
Subject: [PATCH 79/95] Remove unused and undocumented LaTeX macro
``\shortversion``
---
CHANGES | 2 ++
sphinx/texinputs/sphinx.sty | 7 ++-----
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/CHANGES b/CHANGES
index b3e6283fd..a769e7211 100644
--- a/CHANGES
+++ b/CHANGES
@@ -95,6 +95,8 @@ Features removed
* LaTeX environment ``notice``, use ``sphinxadmonition`` instead
* LaTeX ``\sphinxstylethead``, use ``\sphinxstyletheadfamily``
* C++, support of function concepts. Thanks to mickk-on-cpp.
+* Not used and previously not documented LaTeX macros ``\shortversion``
+ and ``\setshortversion``
Bugs fixed
diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty
index 19d0a2e2d..075ae408b 100644
--- a/sphinx/texinputs/sphinx.sty
+++ b/sphinx/texinputs/sphinx.sty
@@ -1403,12 +1403,9 @@
%
\newcommand{\py@release}{\releasename\space\version}
\newcommand{\version}{}% part of \py@release, used by title page and headers
-% these two are not used and not documented:
-\newcommand{\shortversion}{}
-\newcommand{\setshortversion}[1]{\renewcommand{\shortversion}{#1}}
-% this one is not documented, but used in sphinxmanual.cls and sphinxhowto.cls
+% \releaseinfo is used on titlepage (sphinxmanual.cls, sphinxhowto.cls)
\newcommand{\releaseinfo}{}
-\newcommand{\setreleaseinfo}[1]{\renewcommand{\releaseinfo}{#1}}% not used
+\newcommand{\setreleaseinfo}[1]{\renewcommand{\releaseinfo}{#1}}
% this is inserted via template and #1=release config variable
\newcommand{\release}[1]{\renewcommand{\version}{#1}}
% this is defined by template to 'releasename' latex_elements key
From db361c385156e9ae3b0bd91b6ed3f8524011c22d Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sat, 13 Jan 2018 18:42:48 +0900
Subject: [PATCH 80/95] Update CHANGES for PR #4354
---
CHANGES | 2 ++
1 file changed, 2 insertions(+)
diff --git a/CHANGES b/CHANGES
index c76b3f1d9..fbb607b2a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -75,6 +75,8 @@ Features added
* #4183: doctest: ``:pyversion:`` option also follows PEP-440 specification
* #4235: html: Add :confval:`manpages_url` to make manpage roles to hyperlinks
* #3570: autodoc: Do not display 'typing.' module for type hints
+* #4354: `sphinx-build` now emits finish message. Builders can modify it
+ through ``Builder.epilog`` attribute
Features removed
----------------
From f57634a8b8f6f656266b2a967cc3e5626ba70659 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sat, 13 Jan 2018 19:08:23 +0900
Subject: [PATCH 81/95] Fix mark up
---
CHANGES | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/CHANGES b/CHANGES
index fbb607b2a..2c05a81fa 100644
--- a/CHANGES
+++ b/CHANGES
@@ -75,8 +75,8 @@ Features added
* #4183: doctest: ``:pyversion:`` option also follows PEP-440 specification
* #4235: html: Add :confval:`manpages_url` to make manpage roles to hyperlinks
* #3570: autodoc: Do not display 'typing.' module for type hints
-* #4354: `sphinx-build` now emits finish message. Builders can modify it
- through ``Builder.epilog`` attribute
+* #4354: sphinx-build now emits finish message. Builders can modify it through
+ ``Builder.epilog`` attribute
Features removed
----------------
From 4d040abafb9e0626dcf7a420bbe32cbfa0896df6 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sat, 13 Jan 2018 15:18:09 +0900
Subject: [PATCH 82/95] Fix #4079: Add notranslate class to let Google
Translate know they are not translatable
---
CHANGES | 2 ++
sphinx/ext/jsmath.py | 6 +++---
sphinx/ext/mathjax.py | 4 ++--
sphinx/writers/html.py | 6 +++---
sphinx/writers/html5.py | 6 +++---
tests/test_build_html.py | 16 ++++++++--------
tests/test_build_html5.py | 16 ++++++++--------
tests/test_ext_intersphinx.py | 3 ++-
tests/test_ext_math.py | 18 +++++++++---------
tests/test_markup.py | 11 ++++++-----
10 files changed, 46 insertions(+), 42 deletions(-)
diff --git a/CHANGES b/CHANGES
index 2c05a81fa..eb91109a5 100644
--- a/CHANGES
+++ b/CHANGES
@@ -77,6 +77,8 @@ Features added
* #3570: autodoc: Do not display 'typing.' module for type hints
* #4354: sphinx-build now emits finish message. Builders can modify it through
``Builder.epilog`` attribute
+* #4079: html: Add ``notranslate`` class to each code-blocks, literals and maths
+ to let Google Translate know they are not translatable
Features removed
----------------
diff --git a/sphinx/ext/jsmath.py b/sphinx/ext/jsmath.py
index a74f0641a..0858e4d5d 100644
--- a/sphinx/ext/jsmath.py
+++ b/sphinx/ext/jsmath.py
@@ -20,14 +20,14 @@ from sphinx.ext.mathbase import get_node_equation_number
def html_visit_math(self, node):
- self.body.append(self.starttag(node, 'span', '', CLASS='math'))
+ self.body.append(self.starttag(node, 'span', '', CLASS='math notranslate'))
self.body.append(self.encode(node['latex']) + '')
raise nodes.SkipNode
def html_visit_displaymath(self, node):
if node['nowrap']:
- self.body.append(self.starttag(node, 'div', CLASS='math'))
+ self.body.append(self.starttag(node, 'div', CLASS='math notranslate'))
self.body.append(self.encode(node['latex']))
self.body.append('')
raise nodes.SkipNode
@@ -40,7 +40,7 @@ def html_visit_displaymath(self, node):
self.body.append('(%s)' % number)
self.add_permalink_ref(node, _('Permalink to this equation'))
self.body.append('')
- self.body.append(self.starttag(node, 'div', CLASS='math'))
+ self.body.append(self.starttag(node, 'div', CLASS='math notranslate'))
else:
# but only once!
self.body.append('')
diff --git a/sphinx/ext/mathjax.py b/sphinx/ext/mathjax.py
index 8698e2801..bfbd34979 100644
--- a/sphinx/ext/mathjax.py
+++ b/sphinx/ext/mathjax.py
@@ -21,7 +21,7 @@ from sphinx.ext.mathbase import get_node_equation_number
def html_visit_math(self, node):
- self.body.append(self.starttag(node, 'span', '', CLASS='math'))
+ self.body.append(self.starttag(node, 'span', '', CLASS='math notranslate'))
self.body.append(self.builder.config.mathjax_inline[0] +
self.encode(node['latex']) +
self.builder.config.mathjax_inline[1] + '')
@@ -29,7 +29,7 @@ def html_visit_math(self, node):
def html_visit_displaymath(self, node):
- self.body.append(self.starttag(node, 'div', CLASS='math'))
+ self.body.append(self.starttag(node, 'div', CLASS='math notranslate'))
if node['nowrap']:
self.body.append(self.encode(node['latex']))
self.body.append('
')
diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py
index 84e7bfbc9..16fc69bea 100644
--- a/sphinx/writers/html.py
+++ b/sphinx/writers/html.py
@@ -444,7 +444,7 @@ class HTMLTranslator(BaseTranslator):
location=(self.builder.current_docname, node.line), **highlight_args
)
starttag = self.starttag(node, 'div', suffix='',
- CLASS='highlight-%s' % lang)
+ CLASS='highlight-%s notranslate' % lang)
self.body.append(starttag + highlighted + '\n')
raise nodes.SkipNode
@@ -494,10 +494,10 @@ class HTMLTranslator(BaseTranslator):
# type: (nodes.Node) -> None
if 'kbd' in node['classes']:
self.body.append(self.starttag(node, 'kbd', '',
- CLASS='docutils literal'))
+ CLASS='docutils literal notranslate'))
else:
self.body.append(self.starttag(node, 'code', '',
- CLASS='docutils literal'))
+ CLASS='docutils literal notranslate'))
self.protect_literal_text += 1
def depart_literal(self, node):
diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py
index 50bf2ea8c..c2810a898 100644
--- a/sphinx/writers/html5.py
+++ b/sphinx/writers/html5.py
@@ -390,7 +390,7 @@ class HTML5Translator(BaseTranslator):
location=(self.builder.current_docname, node.line), **highlight_args
)
starttag = self.starttag(node, 'div', suffix='',
- CLASS='highlight-%s' % lang)
+ CLASS='highlight-%s notranslate' % lang)
self.body.append(starttag + highlighted + '\n')
raise nodes.SkipNode
@@ -440,10 +440,10 @@ class HTML5Translator(BaseTranslator):
# type: (nodes.Node) -> None
if 'kbd' in node['classes']:
self.body.append(self.starttag(node, 'kbd', '',
- CLASS='docutils literal'))
+ CLASS='docutils literal notranslate'))
else:
self.body.append(self.starttag(node, 'code', '',
- CLASS='docutils literal'))
+ CLASS='docutils literal notranslate'))
self.protect_literal_text += 1
def depart_literal(self, node):
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 153ff5165..2388b06ec 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -163,21 +163,21 @@ def test_html_warnings(app, warning):
(".//pre/span", u'"quotes"'),
(".//pre/span", u"'included'"),
(".//pre/span[@class='s2']", u'üöä'),
- (".//div[@class='inc-pyobj1 highlight-text']//pre",
+ (".//div[@class='inc-pyobj1 highlight-text notranslate']//pre",
r'^class Foo:\n pass\n\s*$'),
- (".//div[@class='inc-pyobj2 highlight-text']//pre",
+ (".//div[@class='inc-pyobj2 highlight-text notranslate']//pre",
r'^ def baz\(\):\n pass\n\s*$'),
- (".//div[@class='inc-lines highlight-text']//pre",
+ (".//div[@class='inc-lines highlight-text notranslate']//pre",
r'^class Foo:\n pass\nclass Bar:\n$'),
- (".//div[@class='inc-startend highlight-text']//pre",
+ (".//div[@class='inc-startend highlight-text notranslate']//pre",
u'^foo = "Including Unicode characters: üöä"\\n$'),
- (".//div[@class='inc-preappend highlight-text']//pre",
+ (".//div[@class='inc-preappend highlight-text notranslate']//pre",
r'(?m)^START CODE$'),
- (".//div[@class='inc-pyobj-dedent highlight-python']//span",
+ (".//div[@class='inc-pyobj-dedent highlight-python notranslate']//span",
r'def'),
- (".//div[@class='inc-tab3 highlight-text']//pre",
+ (".//div[@class='inc-tab3 highlight-text notranslate']//pre",
r'-| |-'),
- (".//div[@class='inc-tab8 highlight-python']//pre/span",
+ (".//div[@class='inc-tab8 highlight-python notranslate']//pre/span",
r'-| |-'),
],
'autodoc.html': [
diff --git a/tests/test_build_html5.py b/tests/test_build_html5.py
index 4ac70be51..168e516cf 100644
--- a/tests/test_build_html5.py
+++ b/tests/test_build_html5.py
@@ -72,21 +72,21 @@ def cached_etree_parse():
(".//pre/span", u'"quotes"'),
(".//pre/span", u"'included'"),
(".//pre/span[@class='s2']", u'üöä'),
- (".//div[@class='inc-pyobj1 highlight-text']//pre",
+ (".//div[@class='inc-pyobj1 highlight-text notranslate']//pre",
r'^class Foo:\n pass\n\s*$'),
- (".//div[@class='inc-pyobj2 highlight-text']//pre",
+ (".//div[@class='inc-pyobj2 highlight-text notranslate']//pre",
r'^ def baz\(\):\n pass\n\s*$'),
- (".//div[@class='inc-lines highlight-text']//pre",
+ (".//div[@class='inc-lines highlight-text notranslate']//pre",
r'^class Foo:\n pass\nclass Bar:\n$'),
- (".//div[@class='inc-startend highlight-text']//pre",
+ (".//div[@class='inc-startend highlight-text notranslate']//pre",
u'^foo = "Including Unicode characters: üöä"\\n$'),
- (".//div[@class='inc-preappend highlight-text']//pre",
+ (".//div[@class='inc-preappend highlight-text notranslate']//pre",
r'(?m)^START CODE$'),
- (".//div[@class='inc-pyobj-dedent highlight-python']//span",
+ (".//div[@class='inc-pyobj-dedent highlight-python notranslate']//span",
r'def'),
- (".//div[@class='inc-tab3 highlight-text']//pre",
+ (".//div[@class='inc-tab3 highlight-text notranslate']//pre",
r'-| |-'),
- (".//div[@class='inc-tab8 highlight-python']//pre/span",
+ (".//div[@class='inc-tab8 highlight-python notranslate']//pre/span",
r'-| |-'),
],
'autodoc.html': [
diff --git a/tests/test_ext_intersphinx.py b/tests/test_ext_intersphinx.py
index 19f8613c6..aef495d30 100644
--- a/tests/test_ext_intersphinx.py
+++ b/tests/test_ext_intersphinx.py
@@ -236,7 +236,8 @@ def test_missing_reference_cppdomain(tempdir, app, status, warning):
html = (app.outdir / 'index.html').text()
assert (''
+ ' title="(in foo v2.0)">'
+ ''
'Bar
' in html)
assert ('\na^2 + b^2 = c^2' in content
- assert '\n\\begin{split}a + 1 < b\\end{split}
' in content
+ assert '\na^2 + b^2 = c^2
' in content
+ assert '\n\\begin{split}a + 1 < b\\end{split}
' in content
assert (u'(1)'
- u'\ne^{i\\pi} = 1
' in content)
+ u'\ne^{i\\pi} = 1
' in content)
assert (u'(2)'
- u'\n'
+ u'
\n'
u'e^{ix} = \\cos x + i\\sin x
' in content)
- assert '
\nn \\in \\mathbb N
' in content
- assert '
\na + 1 < b
' in content
+ assert '
\nn \\in \\mathbb N
' in content
+ assert '
\na + 1 < b
' in content
@pytest.mark.skipif(not has_binary('dvipng'),
@@ -89,7 +89,7 @@ def test_mathjax_align(app, status, warning):
app.builder.build_all()
content = (app.outdir / 'index.html').text()
- html = (r'
\s*'
+ html = (r'
\s*'
r'\\\[ \\begin\{align\}\\begin\{aligned\}S \&= \\pi r\^2\\\\'
r'V \&= \\frac\{4\}\{3\} \\pi r\^3\\end\{aligned\}\\end\{align\} \\\]
')
assert re.search(html, content, re.S)
@@ -102,7 +102,7 @@ def test_math_number_all_mathjax(app, status, warning):
app.builder.build_all()
content = (app.outdir / 'index.html').text()
- html = (r'
\s*'
+ html = (r'
\s*'
r'
\(1\)\xb6\\\[a\^2\+b\^2=c\^2\\\]
')
assert re.search(html, content, re.S)
@@ -167,7 +167,7 @@ def test_mathjax_numfig_html(app, status, warning):
app.builder.build_all()
content = (app.outdir / 'math.html').text()
- html = ('
\n'
+ html = ('
\n'
'
(1.2)')
assert html in content
html = ('Referencing equation '
+ (''
'code sample
'),
r'\\sphinxcode{\\sphinxupquote{code sample}}',
),
@@ -141,7 +141,7 @@ def get_verifier(verify, verify_re):
# correct interpretation of code with whitespace
'verify_re',
':samp:`code sample`',
- (''
+ (''
'code sample
'),
r'\\sphinxcode{\\sphinxupquote{code sample}}',
),
@@ -149,7 +149,8 @@ def get_verifier(verify, verify_re):
# interpolation of braces in samp and file roles (HTML only)
'verify',
':samp:`a{b}c`',
- ('a'
+ (''
+ 'a'
'b'
'c
'),
'\\sphinxcode{\\sphinxupquote{a\\sphinxstyleemphasis{b}c}}',
@@ -173,7 +174,7 @@ def get_verifier(verify, verify_re):
# non-interpolation of dashes in option role
'verify_re',
':option:`--with-option`',
- (''
+ (''
'--with-option
$'),
r'\\sphinxcode{\\sphinxupquote{-{-}with-option}}$',
),
@@ -188,7 +189,7 @@ def get_verifier(verify, verify_re):
# ... but not in literal text
'verify',
'``"John"``',
- (''
+ (''
'"John"
'),
'\\sphinxcode{\\sphinxupquote{"John"}}',
),
From fa51a637a82d3cdd0ae58933013414347c341e93 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sat, 13 Jan 2018 21:00:13 +0900
Subject: [PATCH 83/95] shebang line is removed from generated conf.py (refs:
#4385)
---
CHANGES | 1 +
sphinx/templates/quickstart/conf.py_t | 3 ---
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/CHANGES b/CHANGES
index 2c05a81fa..a74447a48 100644
--- a/CHANGES
+++ b/CHANGES
@@ -21,6 +21,7 @@ Incompatible changes
* #4389: output directory will be created after loading extensions
* autodoc does not generate warnings messages to the generated document even if
:confval:`keep_warnings` is True. They are only emitted to stderr.
+* shebang line is removed from generated conf.py
Deprecated
----------
diff --git a/sphinx/templates/quickstart/conf.py_t b/sphinx/templates/quickstart/conf.py_t
index a1c00f8c7..2583b9891 100644
--- a/sphinx/templates/quickstart/conf.py_t
+++ b/sphinx/templates/quickstart/conf.py_t
@@ -1,6 +1,3 @@
-{% if PY3 -%}
-#!/usr/bin/env python3
-{% endif -%}
# -*- coding: utf-8 -*-
#
# {{ project }} documentation build configuration file, created by
From 3663275755ac5e79cce72573d9cea5be88c29ae2 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sat, 13 Jan 2018 21:42:18 +0900
Subject: [PATCH 84/95] Update CHANGES for PR #4245
---
CHANGES | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGES b/CHANGES
index a74447a48..8096e7d55 100644
--- a/CHANGES
+++ b/CHANGES
@@ -78,6 +78,7 @@ Features added
* #3570: autodoc: Do not display 'typing.' module for type hints
* #4354: sphinx-build now emits finish message. Builders can modify it through
``Builder.epilog`` attribute
+* #4245: html themes: Add ``language`` to javascript vars list
Features removed
----------------
From 87b03bf76fa40d2e3f85ce781dd97727cdd0b5ce Mon Sep 17 00:00:00 2001
From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
Date: Sat, 13 Jan 2018 15:52:50 +0100
Subject: [PATCH 85/95] minor conf.py cleanup
---
sphinx/templates/quickstart/conf.py_t | 27 +++++++++++++--------------
1 file changed, 13 insertions(+), 14 deletions(-)
diff --git a/sphinx/templates/quickstart/conf.py_t b/sphinx/templates/quickstart/conf.py_t
index 2583b9891..2f3f71b1e 100644
--- a/sphinx/templates/quickstart/conf.py_t
+++ b/sphinx/templates/quickstart/conf.py_t
@@ -1,13 +1,12 @@
# -*- coding: utf-8 -*-
#
-# {{ project }} documentation build configuration file, created by
-# sphinx-quickstart on {{ now }}.
+# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/stable/config
-# -- Path setup -----------------------------------------------------------
+# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
@@ -27,7 +26,7 @@ sys.path.insert(0, u'{{ module_path }}')
{% endif -%}
{% endif %}
-# -- Project information --------------------------------------------------
+# -- Project information -----------------------------------------------------
project = u'{{ project_str }}'
copyright = u'{{ copyright_str }}'
@@ -39,7 +38,7 @@ version = u'{{ version_str }}'
release = u'{{ release_str }}'
-# -- General configuration ------------------------------------------------
+# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
@@ -82,7 +81,7 @@ exclude_patterns = [{{ exclude_patterns }}]
pygments_style = 'sphinx'
-# -- Options for HTML output ----------------------------------------------
+# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
@@ -111,13 +110,13 @@ html_static_path = ['{{ dot }}static']
# html_sidebars = {}
-# -- Options for HTMLHelp output ------------------------------------------
+# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = '{{ project_fn }}doc'
-# -- Options for LaTeX output ---------------------------------------------
+# -- Options for LaTeX output ------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
@@ -146,7 +145,7 @@ latex_documents = [
]
-# -- Options for manual page output ---------------------------------------
+# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
@@ -156,7 +155,7 @@ man_pages = [
]
-# -- Options for Texinfo output -------------------------------------------
+# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
@@ -169,7 +168,7 @@ texinfo_documents = [
{%- if epub %}
-# -- Options for Epub output ----------------------------------------------
+# -- Options for Epub output -------------------------------------------------
# Bibliographic Dublin Core info.
epub_title = project
@@ -192,18 +191,18 @@ epub_exclude_files = ['search.html']
{%- if extensions %}
-# -- Extension configuration ----------------------------------------------
+# -- Extension configuration -------------------------------------------------
{%- endif %}
{%- if 'sphinx.ext.intersphinx' in extensions %}
-# -- Options for intersphinx extension ------------------------------------
+# -- Options for intersphinx extension ---------------------------------------
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'https://docs.python.org/': None}
{%- endif %}
{%- if 'sphinx.ext.todo' in extensions %}
-# -- Options for todo extension -------------------------------------------
+# -- Options for todo extension ----------------------------------------------
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
From c24dffc5a8e67e87fb6ad79120427f19e5103cf3 Mon Sep 17 00:00:00 2001
From: Robin Jarry
Date: Thu, 11 Jan 2018 20:53:35 +0100
Subject: [PATCH 86/95] autodoc: only mock specified modules with their
descendants
Do not mock the ancestors of the specified modules in
autodoc_mock_imports. Only mock the modules themselves and their
descendants (as specified in the docs).
Fix the test configs accordingly.
Signed-off-by: Robin Jarry
---
sphinx/ext/autodoc/importer.py | 17 ++++++-----------
.../roots/test-root/autodoc_missing_imports.py | 4 ++++
tests/roots/test-root/conf.py | 7 ++++---
3 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py
index 101cb930f..cea1c12bd 100644
--- a/sphinx/ext/autodoc/importer.py
+++ b/sphinx/ext/autodoc/importer.py
@@ -23,7 +23,7 @@ from sphinx.util.inspect import isenumclass, safe_getattr
if False:
# For type annotation
- from typing import Any, Callable, Dict, Generator, List, Optional, Set # NOQA
+ from typing import Any, Callable, Dict, Generator, List, Optional # NOQA
logger = logging.getLogger(__name__)
@@ -84,13 +84,7 @@ class _MockModule(ModuleType):
class _MockImporter(object):
def __init__(self, names):
# type: (List[str]) -> None
- self.base_packages = set() # type: Set[str]
- for n in names:
- # Convert module names:
- # ['a.b.c', 'd.e']
- # to a set of base packages:
- # set(['a', 'd'])
- self.base_packages.add(n.split('.')[0])
+ self.names = names
self.mocked_modules = [] # type: List[str]
# enable hook by adding itself to meta_path
sys.meta_path = sys.meta_path + [self]
@@ -106,9 +100,10 @@ class _MockImporter(object):
def find_module(self, name, path=None):
# type: (str, str) -> Any
- base_package = name.split('.')[0]
- if base_package in self.base_packages:
- return self
+ # check if name is (or is a descendant of) one of our base_packages
+ for n in self.names:
+ if n == name or name.startswith(n + '.'):
+ return self
return None
def load_module(self, name):
diff --git a/tests/roots/test-root/autodoc_missing_imports.py b/tests/roots/test-root/autodoc_missing_imports.py
index 0901ce8e2..19d4c6a05 100644
--- a/tests/roots/test-root/autodoc_missing_imports.py
+++ b/tests/roots/test-root/autodoc_missing_imports.py
@@ -4,6 +4,8 @@ from missing_module import missing_name
import missing_package1.missing_module1
from missing_package2 import missing_module2
from missing_package3.missing_module3 import missing_name
+import sphinx.missing_module4
+from sphinx.missing_module4 import missing_name2
@missing_name
def decoratedFunction():
@@ -16,3 +18,5 @@ class TestAutodoc(object):
def decoratedMethod(self):
"""TestAutodoc::decoratedMethod docstring"""
return None
+
+sphinx.missing_module4.missing_function(len(missing_name2))
diff --git a/tests/roots/test-root/conf.py b/tests/roots/test-root/conf.py
index 0753fe19c..04cd87d7b 100644
--- a/tests/roots/test-root/conf.py
+++ b/tests/roots/test-root/conf.py
@@ -69,9 +69,10 @@ extlinks = {'issue': ('http://bugs.python.org/issue%s', 'issue '),
autodoc_mock_imports = [
'missing_module',
- 'missing_package1.missing_module1',
- 'missing_package2.missing_module2',
- 'missing_package3.missing_module3',
+ 'missing_package1',
+ 'missing_package2',
+ 'missing_package3',
+ 'sphinx.missing_module4',
]
# modify tags from conf.py
From 8b74d93ad9c6b6f7dfe129c2481dd1676562b75e Mon Sep 17 00:00:00 2001
From: jfbu
Date: Wed, 3 May 2017 23:52:29 +0200
Subject: [PATCH 87/95] Remove unneeded coding from
LaTeXTranslator.visit_figure()
---
sphinx/writers/latex.py | 24 +++++-------------------
1 file changed, 5 insertions(+), 19 deletions(-)
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index 6c86e6174..74b1c52a6 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -1853,28 +1853,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
(node['align'] == 'right' and 'r' or 'l', length or '0pt'))
self.context.append(ids + '\\end{wrapfigure}\n')
elif self.in_minipage:
- if ('align' not in node.attributes or
- node.attributes['align'] == 'center'):
- self.body.append('\n\\begin{center}')
- self.context.append('\\end{center}\n')
- else:
- self.body.append('\n\\begin{flush%s}' % node.attributes['align'])
- self.context.append('\\end{flush%s}\n' % node.attributes['align'])
+ self.body.append('\n\\begin{center}')
+ self.context.append('\\end{center}\n')
else:
- if ('align' not in node.attributes or
- node.attributes['align'] == 'center'):
- # centering does not add vertical space like center.
- align = '\n\\centering'
- align_end = ''
- else:
- # TODO non vertical space for other alignments.
- align = '\\begin{flush%s}' % node.attributes['align']
- align_end = '\\end{flush%s}' % node.attributes['align']
- self.body.append('\n\\begin{figure}[%s]%s\n' % (
- self.elements['figure_align'], align))
+ self.body.append('\n\\begin{figure}[%s]\n\\centering\n' %
+ self.elements['figure_align'])
if any(isinstance(child, nodes.caption) for child in node):
self.body.append('\\capstart\n')
- self.context.append(ids + align_end + '\\end{figure}\n')
+ self.context.append(ids + '\\end{figure}\n')
def depart_figure(self, node):
# type: (nodes.Node) -> None
From c399357f38a081cf5f75e98c9627b80928a1c035 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Tue, 31 Oct 2017 22:59:01 +0900
Subject: [PATCH 88/95] Show a notice if both tabularcolumns and :widths: are
given (refs: #4196)
---
doc/markup/misc.rst | 5 +++++
sphinx/writers/latex.py | 3 +++
2 files changed, 8 insertions(+)
diff --git a/doc/markup/misc.rst b/doc/markup/misc.rst
index 35ed6375d..51e3a405c 100644
--- a/doc/markup/misc.rst
+++ b/doc/markup/misc.rst
@@ -323,6 +323,11 @@ following directive exists:
Sphinx's merged cells interact well with ``p{width}``, ``\X{a}{b}``, ``Y{f}``
and tabulary's columns.
+ .. note::
+
+ :rst:dir:`tabularcolumns` conflicts with ``:widths:`` option of table
+ directives. If both are specified, ``:widths:`` option will be ignored.
+
Math
----
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index 6c86e6174..4741de92b 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -1376,6 +1376,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.table = Table(node)
if self.next_table_colspec:
self.table.colspec = '{%s}\n' % self.next_table_colspec
+ if 'colwidths-given' in node.get('classes', []):
+ logger.info('both tabularcolumns and :widths: option are given. '
+ ':widths: is ignored.', location=node)
self.next_table_colspec = None
def depart_table(self, node):
From 0d61b251c5a6d55eb8a0ce6977e72078664e0a06 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sun, 14 Jan 2018 23:10:04 +0900
Subject: [PATCH 89/95] Update CHANGES for PR #4413
---
CHANGES | 3 +++
1 file changed, 3 insertions(+)
diff --git a/CHANGES b/CHANGES
index fe964bb2e..41d512023 100644
--- a/CHANGES
+++ b/CHANGES
@@ -22,6 +22,9 @@ Incompatible changes
* autodoc does not generate warnings messages to the generated document even if
:confval:`keep_warnings` is True. They are only emitted to stderr.
* shebang line is removed from generated conf.py
+* #2557: autodoc: :confval:`autodoc_mock_imports` only mocks specified modules
+ with their descendants. It does not mock their ancestors. If you want to
+ mock them, please specify the name of ancestors implicitly.
Deprecated
----------
From de023f2dc0972fc398975c5614caf89149a2de16 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Sun, 14 Jan 2018 23:31:30 +0900
Subject: [PATCH 90/95] Fix #3620: html theme: move DOCUMENTATION_OPTIONS to
independent JavaScript file (refs: #3620)
---
CHANGES | 2 ++
sphinx/themes/basic/documentation_options.js_t | 9 +++++++++
sphinx/themes/basic/layout.html | 12 +-----------
3 files changed, 12 insertions(+), 11 deletions(-)
create mode 100644 sphinx/themes/basic/documentation_options.js_t
diff --git a/CHANGES b/CHANGES
index 41d512023..26849effa 100644
--- a/CHANGES
+++ b/CHANGES
@@ -25,6 +25,8 @@ Incompatible changes
* #2557: autodoc: :confval:`autodoc_mock_imports` only mocks specified modules
with their descendants. It does not mock their ancestors. If you want to
mock them, please specify the name of ancestors implicitly.
+* #3620: html theme: move DOCUMENTATION_OPTIONS to independent JavaScript file
+ (refs: #4295)
Deprecated
----------
diff --git a/sphinx/themes/basic/documentation_options.js_t b/sphinx/themes/basic/documentation_options.js_t
new file mode 100644
index 000000000..e76f55a4e
--- /dev/null
+++ b/sphinx/themes/basic/documentation_options.js_t
@@ -0,0 +1,9 @@
+var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '{{ url_root }}',
+ VERSION: '{{ release|e }}',
+ LANGUAGE: '{{ language }}',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}',
+ HAS_SOURCE: {{ has_source|lower }},
+ SOURCELINK_SUFFIX: '{{ sourcelink_suffix }}'
+};
diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html
index 050de15df..dc05e980d 100644
--- a/sphinx/themes/basic/layout.html
+++ b/sphinx/themes/basic/layout.html
@@ -87,17 +87,7 @@
{%- endmacro %}
{%- macro script() %}
-
+
{%- for scriptfile in script_files %}
{%- endfor %}
From eebf9d0a0ecf7fa5bac9da8a6808c38047ca882a Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Thu, 12 Oct 2017 22:57:20 +0900
Subject: [PATCH 91/95] Fix #4137: doctest: Make doctest and testcode blocks
highlighted
---
CHANGES | 2 ++
sphinx/ext/doctest.py | 6 +++++-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/CHANGES b/CHANGES
index 26849effa..924773f76 100644
--- a/CHANGES
+++ b/CHANGES
@@ -86,6 +86,8 @@ Features added
* #4245: html themes: Add ``language`` to javascript vars list
* #4079: html: Add ``notranslate`` class to each code-blocks, literals and maths
to let Google Translate know they are not translatable
+* #4137: doctest: doctest block is always highlighted as python console (pycon)
+* #4137: doctest: testcode block is always highlighted as python
Features removed
----------------
diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py
index 1a1c05998..cd35e789a 100644
--- a/sphinx/ext/doctest.py
+++ b/sphinx/ext/doctest.py
@@ -116,7 +116,11 @@ class TestDirective(Directive):
if test is not None:
# only save if it differs from code
node['test'] = test
- if self.name == 'testoutput':
+ if self.name == 'doctest':
+ node['language'] = 'pycon'
+ elif self.name == 'testcode':
+ node['language'] = 'python'
+ elif self.name == 'testoutput':
# don't try to highlight output
node['language'] = 'none'
node['options'] = {}
From 18dfe37f8c64fe43ff6b7aa0e4f42f0d9d7e9e7c Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 15 Jan 2018 00:37:32 +0900
Subject: [PATCH 92/95] Enable text_add_secnumbers by default (refs: #4218)
---
CHANGES | 4 ++--
doc/config.rst | 2 +-
sphinx/builders/text.py | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/CHANGES b/CHANGES
index 69764b902..e15890e32 100644
--- a/CHANGES
+++ b/CHANGES
@@ -88,8 +88,8 @@ Features added
to let Google Translate know they are not translatable
* #4137: doctest: doctest block is always highlighted as python console (pycon)
* #4137: doctest: testcode block is always highlighted as python
-* #3998: text: Add new config values :confval:`text_add_secnumbers` and
- :confval:`text_secnumber_suffix`
+* #3998: text: Assign section numbers by default. You can control it using
+ :confval:`text_add_secnumbers` and :confval:`text_secnumber_suffix`
Features removed
----------------
diff --git a/doc/config.rst b/doc/config.rst
index 56d673d29..830fb69a0 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -2042,7 +2042,7 @@ These options influence text output.
.. confval:: text_add_secnumbers
A boolean that decides whether section numbers are included in text output.
- Default is ``False``.
+ Default is ``True``.
.. versionadded:: 1.7
diff --git a/sphinx/builders/text.py b/sphinx/builders/text.py
index e894f9936..babb6ccee 100644
--- a/sphinx/builders/text.py
+++ b/sphinx/builders/text.py
@@ -97,7 +97,7 @@ def setup(app):
app.add_config_value('text_sectionchars', '*=-~"+`', 'env')
app.add_config_value('text_newlines', 'unix', 'env')
- app.add_config_value('text_add_secnumbers', False, 'env')
+ app.add_config_value('text_add_secnumbers', True, 'env')
app.add_config_value('text_secnumber_suffix', '. ', 'env')
return {
From dc3d03da1bb826e822f90f502d2b7aca6a41ef6f Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 15 Jan 2018 01:01:13 +0900
Subject: [PATCH 93/95] test: Fix broken testcases
---
tests/test_build_text.py | 53 ++++++++++++++++++------------------
tests/test_intl.py | 58 ++++++++++++++++++++--------------------
2 files changed, 55 insertions(+), 56 deletions(-)
diff --git a/tests/test_build_text.py b/tests/test_build_text.py
index 353925466..85b518007 100644
--- a/tests/test_build_text.py
+++ b/tests/test_build_text.py
@@ -63,7 +63,7 @@ def test_lineblock(app, status, warning):
def test_nonascii_title_line(app, status, warning):
app.builder.build_update()
result = (app.outdir / 'nonascii_title.txt').text(encoding='utf-8')
- expect_underline = '******'
+ expect_underline = '*********'
result_underline = result.splitlines()[1].strip()
assert expect_underline == result_underline
@@ -114,32 +114,6 @@ def test_list_items_in_admonition(app, status, warning):
@with_text_app()
def test_secnums(app, status, warning):
- app.builder.build_all()
- contents = (app.outdir / 'contents.txt').text(encoding='utf8')
- lines = contents.splitlines()
- assert lines[0] == "* Section A"
- assert lines[1] == ""
- assert lines[2] == "* Section B"
- assert lines[3] == ""
- assert lines[4] == " * Sub Ba"
- assert lines[5] == ""
- assert lines[6] == " * Sub Bb"
- doc2 = (app.outdir / 'doc2.txt').text(encoding='utf8')
- expect = (
- "Section B\n"
- "*********\n"
- "\n"
- "\n"
- "Sub Ba\n"
- "======\n"
- "\n"
- "\n"
- "Sub Bb\n"
- "======\n"
- )
- assert doc2 == expect
-
- app.config.text_add_secnumbers = True
app.builder.build_all()
contents = (app.outdir / 'contents.txt').text(encoding='utf8')
lines = contents.splitlines()
@@ -191,3 +165,28 @@ def test_secnums(app, status, warning):
)
assert doc2 == expect
+ app.config.text_add_secnumbers = False
+ app.builder.build_all()
+ contents = (app.outdir / 'contents.txt').text(encoding='utf8')
+ lines = contents.splitlines()
+ assert lines[0] == "* Section A"
+ assert lines[1] == ""
+ assert lines[2] == "* Section B"
+ assert lines[3] == ""
+ assert lines[4] == " * Sub Ba"
+ assert lines[5] == ""
+ assert lines[6] == " * Sub Bb"
+ doc2 = (app.outdir / 'doc2.txt').text(encoding='utf8')
+ expect = (
+ "Section B\n"
+ "*********\n"
+ "\n"
+ "\n"
+ "Sub Ba\n"
+ "======\n"
+ "\n"
+ "\n"
+ "Sub Bb\n"
+ "======\n"
+ )
+ assert doc2 == expect
diff --git a/tests/test_intl.py b/tests/test_intl.py
index cb13b00f3..0923aa4f8 100644
--- a/tests/test_intl.py
+++ b/tests/test_intl.py
@@ -143,8 +143,8 @@ def test_text_warning_node(app):
app.build()
# test warnings in translation
result = (app.outdir / 'warnings.txt').text(encoding='utf-8')
- expect = (u"I18N WITH REST WARNINGS"
- u"\n***********************\n"
+ expect = (u"3. I18N WITH REST WARNINGS"
+ u"\n**************************\n"
u"\nLINE OF >>``<
Date: Mon, 15 Jan 2018 01:11:58 +0900
Subject: [PATCH 94/95] Fix mypy violation
---
sphinx/io.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/io.py b/sphinx/io.py
index 61761f697..39d653a19 100644
--- a/sphinx/io.py
+++ b/sphinx/io.py
@@ -99,7 +99,7 @@ class SphinxStandaloneReader(SphinxBaseReader):
def __init__(self, app, *args, **kwargs):
# type: (Sphinx, Any, Any) -> None
self.transforms = self.transforms + app.registry.get_transforms()
- SphinxBaseReader.__init__(self, app, *args, **kwargs) # type: ignore
+ SphinxBaseReader.__init__(self, app, *args, **kwargs)
class SphinxI18nReader(SphinxBaseReader):
From 2398bc9f7752bcd429f49340af80196133091547 Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA
Date: Mon, 15 Jan 2018 10:12:53 +0900
Subject: [PATCH 95/95] Update CHANGES for PR #4396
---
CHANGES | 3 +++
1 file changed, 3 insertions(+)
diff --git a/CHANGES b/CHANGES
index 00fcb59a7..184a2b7a9 100644
--- a/CHANGES
+++ b/CHANGES
@@ -16,6 +16,9 @@ Features added
Bugs fixed
----------
+* #1922: html search: Upper characters problem in French
+
+
Testing
--------