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" %} {%- 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%}

+
- - + class="subscribeform"> + +
+

{%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)
\xb6' - u'
\ne^{i\\pi} = 1
' in content) + u'
\ne^{i\\pi} = 1
' in content) assert (u'(2)\xb6' - 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 --------