From ab43f821d25c794d904ad3f7f3c6747b3a4d8df3 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 2 Jun 2019 12:03:21 +0900 Subject: [PATCH 01/84] Fix #1125: html theme: scrollbar is hard to see on classic theme and macOS --- CHANGES | 2 ++ sphinx/themes/classic/static/classic.css_t | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/CHANGES b/CHANGES index 07846fede..cc9c98e8b 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,8 @@ Features added Bugs fixed ---------- +* #1125: html theme: scrollbar is hard to see on classic theme and macOS + Testing -------- diff --git a/sphinx/themes/classic/static/classic.css_t b/sphinx/themes/classic/static/classic.css_t index 1ee0d2298..a062d6d14 100644 --- a/sphinx/themes/classic/static/classic.css_t +++ b/sphinx/themes/classic/static/classic.css_t @@ -13,6 +13,11 @@ /* -- page layout ----------------------------------------------------------- */ +html { + /* CSS hack for macOS's scrollbar (see #1125) */ + background-color: #FFFFFF; +} + body { font-family: {{ theme_bodyfont }}; font-size: 100%; From 84df4c6c2d69daafebcebcd148209c191d91f39b Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 2 Jun 2019 22:50:59 +0900 Subject: [PATCH 02/84] Fix py domain: duplicated warning does not point the location of source code --- CHANGES | 2 ++ sphinx/domains/python.py | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 07846fede..3e067886f 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,8 @@ Features added Bugs fixed ---------- +* py domain: duplicated warning does not point the location of source code + Testing -------- diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index dd6652c19..2a9a120ef 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -337,7 +337,8 @@ class PyObject(ObjectDescription): self.state.document.note_explicit_target(signode) domain = cast(PythonDomain, self.env.get_domain('py')) - domain.note_object(fullname, self.objtype) + domain.note_object(fullname, self.objtype, + location=(self.env.docname, self.lineno)) indextext = self.get_index_text(modname, name_cls) if indextext: @@ -752,7 +753,7 @@ class PyModule(SphinxDirective): self.options.get('synopsis', ''), self.options.get('platform', ''), 'deprecated' in self.options) - domain.note_object(modname, 'module') + domain.note_object(modname, 'module', location=(self.env.docname, self.lineno)) targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True) From 6cbee7de3c5c5a97a54ecd01660c7c7a3cd34253 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 3 Jun 2019 00:35:05 +0900 Subject: [PATCH 03/84] test: Move descriptor example to independent file --- .../roots/test-ext-autodoc/target/__init__.py | 31 ------------ .../test-ext-autodoc/target/descriptor.py | 31 ++++++++++++ tests/test_autodoc.py | 49 +++++++++---------- 3 files changed, 54 insertions(+), 57 deletions(-) create mode 100644 tests/roots/test-ext-autodoc/target/descriptor.py diff --git a/tests/roots/test-ext-autodoc/target/__init__.py b/tests/roots/test-ext-autodoc/target/__init__.py index c60bab20b..771d45c4c 100644 --- a/tests/roots/test-ext-autodoc/target/__init__.py +++ b/tests/roots/test-ext-autodoc/target/__init__.py @@ -22,31 +22,6 @@ class CustomEx(Exception): """Exception method.""" -class CustomDataDescriptor(object): - """Descriptor class docstring.""" - - def __init__(self, doc): - self.__doc__ = doc - - def __get__(self, obj, type=None): - if obj is None: - return self - return 42 - - def meth(self): - """Function.""" - return "The Answer" - - -class CustomDataDescriptorMeta(type): - """Descriptor metaclass docstring.""" - - -class CustomDataDescriptor2(CustomDataDescriptor): - """Descriptor class with custom metaclass docstring.""" - __metaclass__ = CustomDataDescriptorMeta - - def _funky_classmethod(name, b, c, d, docstring=None): """Generates a classmethod for a class from a template by filling out some arguments.""" @@ -81,8 +56,6 @@ class Derived(Base): class Class(Base): """Class to document.""" - descr = CustomDataDescriptor("Descriptor instance docstring.") - def meth(self): """Function.""" @@ -101,10 +74,6 @@ class Class(Base): #: should be documented -- süß attr = 'bar' - @property - def prop(self): - """Property.""" - docattr = 'baz' """should likewise be documented -- süß""" diff --git a/tests/roots/test-ext-autodoc/target/descriptor.py b/tests/roots/test-ext-autodoc/target/descriptor.py new file mode 100644 index 000000000..63d179b65 --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/descriptor.py @@ -0,0 +1,31 @@ +class CustomDataDescriptor(object): + """Descriptor class docstring.""" + + def __init__(self, doc): + self.__doc__ = doc + + def __get__(self, obj, type=None): + if obj is None: + return self + return 42 + + def meth(self): + """Function.""" + return "The Answer" + + +class CustomDataDescriptorMeta(type): + """Descriptor metaclass docstring.""" + + +class CustomDataDescriptor2(CustomDataDescriptor): + """Descriptor class with custom metaclass docstring.""" + __metaclass__ = CustomDataDescriptorMeta + + +class Class: + descr = CustomDataDescriptor("Descriptor instance docstring.") + + @property + def prop(self): + """Property.""" diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index ad6a56d33..967eb30b3 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -752,7 +752,6 @@ def test_autodoc_undoc_members(app): assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Class(arg)', ' .. py:attribute:: Class.attr', - ' .. py:attribute:: Class.descr', ' .. py:attribute:: Class.docattr', ' .. py:method:: Class.excludemeth()', ' .. py:attribute:: Class.inst_attr_comment', @@ -761,7 +760,6 @@ def test_autodoc_undoc_members(app): ' .. py:attribute:: Class.mdocattr', ' .. py:method:: Class.meth()', ' .. py:method:: Class.moore(a, e, f) -> happiness', - ' .. py:method:: Class.prop', ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', ' .. py:attribute:: Class.skipattr', ' .. py:method:: Class.skipmeth()', @@ -782,7 +780,6 @@ def test_autodoc_inherited_members(app): ' .. py:method:: Class.inheritedstaticmeth(cls)', ' .. py:method:: Class.meth()', ' .. py:method:: Class.moore(a, e, f) -> happiness', - ' .. py:method:: Class.prop', ' .. py:method:: Class.skipmeth()' ] @@ -833,7 +830,6 @@ def test_autodoc_special_members(app): ' .. py:method:: Class.__special1__()', ' .. py:method:: Class.__special2__()', ' .. py:attribute:: Class.attr', - ' .. py:attribute:: Class.descr', ' .. py:attribute:: Class.docattr', ' .. py:method:: Class.excludemeth()', ' .. py:attribute:: Class.inst_attr_comment', @@ -842,7 +838,6 @@ def test_autodoc_special_members(app): ' .. py:attribute:: Class.mdocattr', ' .. py:method:: Class.meth()', ' .. py:method:: Class.moore(a, e, f) -> happiness', - ' .. py:method:: Class.prop', ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', ' .. py:attribute:: Class.skipattr', ' .. py:method:: Class.skipmeth()', @@ -866,9 +861,6 @@ def test_autodoc_ignore_module_all(app): actual = do_autodoc(app, 'module', 'target', options) assert list(filter(lambda l: 'class::' in l, actual)) == [ '.. py:class:: Class(arg)', - '.. py:class:: CustomDataDescriptor(doc)', - '.. py:class:: CustomDataDescriptor2(doc)', - '.. py:class:: CustomDataDescriptorMeta', '.. py:class:: CustomDict', '.. py:class:: InstAttCls()', '.. py:class:: Outer', @@ -991,14 +983,27 @@ def test_autodoc_staticmethod(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_descriptor(app): - actual = do_autodoc(app, 'attribute', 'target.Class.descr') + options = {"members": None, + "undoc-members": True} + actual = do_autodoc(app, 'class', 'target.descriptor.Class', options) assert list(actual) == [ '', - '.. py:attribute:: Class.descr', - ' :module: target', + '.. py:class:: Class', + ' :module: target.descriptor', '', - ' Descriptor instance docstring.', - ' ' + ' ', + ' .. py:attribute:: Class.descr', + ' :module: target.descriptor', + ' ', + ' Descriptor instance docstring.', + ' ', + ' ', + ' .. py:method:: Class.prop', + ' :module: target.descriptor', + ' :property:', + ' ', + ' Property.', + ' ' ] @@ -1027,14 +1032,12 @@ def test_autodoc_member_order(app): actual = do_autodoc(app, 'class', 'target.Class', options) assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Class(arg)', - ' .. py:attribute:: Class.descr', ' .. py:method:: Class.meth()', ' .. py:method:: Class.undocmeth()', ' .. py:method:: Class.skipmeth()', ' .. py:method:: Class.excludemeth()', ' .. py:attribute:: Class.skipattr', ' .. py:attribute:: Class.attr', - ' .. py:method:: Class.prop', ' .. py:attribute:: Class.docattr', ' .. py:attribute:: Class.udocattr', ' .. py:attribute:: Class.mdocattr', @@ -1062,13 +1065,11 @@ def test_autodoc_member_order(app): ' .. py:method:: Class.undocmeth()', ' .. py:attribute:: Class._private_inst_attr', ' .. py:attribute:: Class.attr', - ' .. py:attribute:: Class.descr', ' .. py:attribute:: Class.docattr', ' .. py:attribute:: Class.inst_attr_comment', ' .. py:attribute:: Class.inst_attr_inline', ' .. py:attribute:: Class.inst_attr_string', ' .. py:attribute:: Class.mdocattr', - ' .. py:method:: Class.prop', ' .. py:attribute:: Class.skipattr', ' .. py:attribute:: Class.udocattr' ] @@ -1082,7 +1083,6 @@ def test_autodoc_member_order(app): '.. py:class:: Class(arg)', ' .. py:attribute:: Class._private_inst_attr', ' .. py:attribute:: Class.attr', - ' .. py:attribute:: Class.descr', ' .. py:attribute:: Class.docattr', ' .. py:method:: Class.excludemeth()', ' .. py:attribute:: Class.inst_attr_comment', @@ -1091,7 +1091,6 @@ def test_autodoc_member_order(app): ' .. py:attribute:: Class.mdocattr', ' .. py:method:: Class.meth()', ' .. py:method:: Class.moore(a, e, f) -> happiness', - ' .. py:method:: Class.prop', ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', ' .. py:attribute:: Class.skipattr', ' .. py:method:: Class.skipmeth()', @@ -1422,26 +1421,26 @@ def test_enum_class(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_descriptor_class(app): options = {"members": 'CustomDataDescriptor,CustomDataDescriptor2'} - actual = do_autodoc(app, 'module', 'target', options) + actual = do_autodoc(app, 'module', 'target.descriptor', options) assert list(actual) == [ '', - '.. py:module:: target', + '.. py:module:: target.descriptor', '', '', '.. py:class:: CustomDataDescriptor(doc)', - ' :module: target', + ' :module: target.descriptor', '', ' Descriptor class docstring.', ' ', ' ', ' .. py:method:: CustomDataDescriptor.meth()', - ' :module: target', + ' :module: target.descriptor', ' ', ' Function.', ' ', '', '.. py:class:: CustomDataDescriptor2(doc)', - ' :module: target', + ' :module: target.descriptor', '', ' Descriptor class with custom metaclass docstring.', ' ' @@ -1888,12 +1887,10 @@ def test_autodoc_default_options_with_values(app): actual = do_autodoc(app, 'class', 'target.Class') assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Class(arg)', - ' .. py:attribute:: Class.descr', ' .. py:method:: Class.meth()', ' .. py:method:: Class.skipmeth()', ' .. py:method:: Class.excludemeth()', ' .. py:attribute:: Class.attr', - ' .. py:method:: Class.prop', ' .. py:attribute:: Class.docattr', ' .. py:attribute:: Class.udocattr', ' .. py:attribute:: Class.mdocattr', From 3f617a4a9b6c1a628a0be3f6d0a5cf4f27cfac7e Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 3 Jun 2019 01:07:17 +0900 Subject: [PATCH 04/84] test: Update testcase for between() --- .../target/process_docstring.py | 8 +++ tests/test_autodoc.py | 52 +++++++++++-------- 2 files changed, 37 insertions(+), 23 deletions(-) create mode 100644 tests/roots/test-ext-autodoc/target/process_docstring.py diff --git a/tests/roots/test-ext-autodoc/target/process_docstring.py b/tests/roots/test-ext-autodoc/target/process_docstring.py new file mode 100644 index 000000000..6005943b6 --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/process_docstring.py @@ -0,0 +1,8 @@ +def func(): + """ + first line + --- + second line + --- + third line + """ diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 967eb30b3..99eb2eb86 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -506,32 +506,38 @@ def test_docstring_processing(): assert process('function', 'f', f) == ['second line', ''] app.disconnect(lid) - lid = app.connect('autodoc-process-docstring', between('---', ['function'])) - def g(): - """ - first line - --- - second line - --- - third line - """ - assert process('function', 'g', g) == ['second line', ''] - app.disconnect(lid) +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_between(app): + app.connect('autodoc-process-docstring', + between('---', ['function'])) - lid = app.connect('autodoc-process-docstring', - between('---', ['function'], exclude=True)) + actual = do_autodoc(app, 'function', 'target.process_docstring.func') + assert list(actual) == [ + '', + '.. py:function:: func()', + ' :module: target.process_docstring', + '', + ' second line', + ' ' + ] - def h(): - """ - first line - --- - second line - --- - third line - """ - assert process('function', 'h', h) == ['first line', 'third line', ''] - app.disconnect(lid) + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_between_exclude(app): + app.connect('autodoc-process-docstring', + between('---', ['function'], exclude=True)) + + actual = do_autodoc(app, 'function', 'target.process_docstring.func') + assert list(actual) == [ + '', + '.. py:function:: func()', + ' :module: target.process_docstring', + '', + ' first line', + ' third line', + ' ' + ] @pytest.mark.sphinx('html', testroot='ext-autodoc') From e274ae87a5fca3900153a79f48d8c52653a08527 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 3 Jun 2019 01:09:24 +0900 Subject: [PATCH 05/84] test: Update testcase for cut_lines() --- tests/test_autodoc.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 99eb2eb86..34ccf6bc2 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -494,17 +494,21 @@ def test_docstring_processing(): # docstring processing by event handler assert process('class', 'bar', E) == ['Init docstring', '', '42', ''] - lid = app.connect('autodoc-process-docstring', - cut_lines(1, 1, ['function'])) - def f(): - """ - first line - second line - third line - """ - assert process('function', 'f', f) == ['second line', ''] - app.disconnect(lid) +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_cut_lines(app): + app.connect('autodoc-process-docstring', + cut_lines(2, 2, ['function'])) + + actual = do_autodoc(app, 'function', 'target.process_docstring.func') + assert list(actual) == [ + '', + '.. py:function:: func()', + ' :module: target.process_docstring', + '', + ' second line', + ' ' + ] @pytest.mark.sphinx('html', testroot='ext-autodoc') From 8da90bbe24cea8c1a81bbdc5707093e1c123a057 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 3 Jun 2019 01:13:46 +0900 Subject: [PATCH 06/84] test: Update testcase for autodoc-process-docstring --- tests/test_autodoc.py | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 34ccf6bc2..b9ad2c981 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -58,7 +58,6 @@ def setup_module(rootdir, sphinx_test_tempdir): app = SphinxTestApp(srcdir=srcdir) app.builder.env.app = app app.builder.env.temp_data['docname'] = 'dummy' - app.connect('autodoc-process-docstring', process_docstring) app.connect('autodoc-process-signature', process_signature) app.connect('autodoc-skip-member', skip_member) yield @@ -73,7 +72,7 @@ directive = options = None @pytest.fixture def setup_test(): global options, directive - global processed_docstrings, processed_signatures + global processed_signatures options = Options( inherited_members = False, @@ -102,7 +101,6 @@ def setup_test(): ) directive.state.document.settings.tab_width = 8 - processed_docstrings = [] processed_signatures = [] app._status.truncate(0) @@ -113,16 +111,9 @@ def setup_test(): app.registry.autodoc_attrgettrs.clear() -processed_docstrings = [] processed_signatures = [] -def process_docstring(app, what, name, obj, options, lines): - processed_docstrings.append((what, name)) - if name == 'bar': - lines.extend(['42', '']) - - def process_signature(app, what, name, obj, options, args, retann): processed_signatures.append((what, name)) if name == 'bar': @@ -479,20 +470,22 @@ def test_get_doc(): assert getdocl('method', Derived.inheritedmeth) == ['Inherited function.'] -@pytest.mark.usefixtures('setup_test') -def test_docstring_processing(): - def process(objtype, name, obj): - inst = app.registry.documenters[objtype](directive, name) - inst.object = obj - inst.fullname = name - return list(inst.process_doc(inst.get_doc())) +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_process_docstring(app): + def on_process_docstring(app, what, name, obj, options, lines): + lines.clear() + lines.append('my docstring') - class E: - def __init__(self): - """Init docstring""" + app.connect('autodoc-process-docstring', on_process_docstring) - # docstring processing by event handler - assert process('class', 'bar', E) == ['Init docstring', '', '42', ''] + actual = do_autodoc(app, 'function', 'target.process_docstring.func') + assert list(actual) == [ + '', + '.. py:function:: func()', + ' :module: target.process_docstring', + '', + ' my docstring' + ] @pytest.mark.sphinx('html', testroot='ext-autodoc') From 7de8c63407b11ad59ba1df9baf2fffd174bee02e Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 3 Jun 2019 01:28:56 +0900 Subject: [PATCH 07/84] test: Move testcases for autodoc configurations to test_ext_autodoc_configs --- tests/test_autodoc.py | 376 +---------------------------- tests/test_ext_autodoc_configs.py | 386 ++++++++++++++++++++++++++++++ 2 files changed, 387 insertions(+), 375 deletions(-) create mode 100644 tests/test_ext_autodoc_configs.py diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index b9ad2c981..3cd39538a 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -9,7 +9,6 @@ :license: BSD, see LICENSE for details. """ -import platform import sys from unittest.mock import Mock from warnings import catch_warnings @@ -17,10 +16,7 @@ from warnings import catch_warnings import pytest from docutils.statemachine import ViewList -from sphinx.ext.autodoc import ( - ModuleLevelDocumenter, cut_lines, between, ALL, - merge_autodoc_default_flags, Options -) +from sphinx.ext.autodoc import ModuleLevelDocumenter, cut_lines, between, ALL, Options from sphinx.ext.autodoc.directive import DocumenterBridge, process_documenter_options from sphinx.testing.util import SphinxTestApp, Struct # NOQA from sphinx.util import logging @@ -28,8 +24,6 @@ from sphinx.util.docutils import LoggingReporter app = None -IS_PYPY = platform.python_implementation() == 'PyPy' - def do_autodoc(app, objtype, name, options=None): if options is None: @@ -1133,95 +1127,6 @@ def test_autodoc_class_scope(app): ] -@pytest.mark.sphinx('html', testroot='ext-autodoc') -def test_autodoc_docstring_signature(app): - options = {"members": None} - actual = do_autodoc(app, 'class', 'target.DocstringSig', options) - assert list(actual) == [ - '', - '.. py:class:: DocstringSig', - ' :module: target', - '', - ' ', - ' .. py:method:: DocstringSig.meth(FOO, BAR=1) -> BAZ', - ' :module: target', - ' ', - ' First line of docstring', - ' ', - ' rest of docstring', - ' ', - ' ', - ' .. py:method:: DocstringSig.meth2()', - ' :module: target', - ' ', - ' First line, no signature', - ' Second line followed by indentation::', - ' ', - ' indented line', - ' ', - ' ', - ' .. py:method:: DocstringSig.prop1', - ' :module: target', - ' :property:', - ' ', - ' First line of docstring', - ' ', - ' ', - ' .. py:method:: DocstringSig.prop2', - ' :module: target', - ' :property:', - ' ', - ' First line of docstring', - ' Second line of docstring', - ' ' - ] - - # disable autodoc_docstring_signature - app.config.autodoc_docstring_signature = False - actual = do_autodoc(app, 'class', 'target.DocstringSig', options) - assert list(actual) == [ - '', - '.. py:class:: DocstringSig', - ' :module: target', - '', - ' ', - ' .. py:method:: DocstringSig.meth()', - ' :module: target', - ' ', - ' meth(FOO, BAR=1) -> BAZ', - ' First line of docstring', - ' ', - ' rest of docstring', - ' ', - ' ', - ' ', - ' .. py:method:: DocstringSig.meth2()', - ' :module: target', - ' ', - ' First line, no signature', - ' Second line followed by indentation::', - ' ', - ' indented line', - ' ', - ' ', - ' .. py:method:: DocstringSig.prop1', - ' :module: target', - ' :property:', - ' ', - ' DocstringSig.prop1(self)', - ' First line of docstring', - ' ', - ' ', - ' .. py:method:: DocstringSig.prop2', - ' :module: target', - ' :property:', - ' ', - ' First line of docstring', - ' Second line of docstring', - ' ' - ] - - @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_class_attributes(app): options = {"members": None, @@ -1476,57 +1381,6 @@ def test_autofunction_for_method(app): ] -@pytest.mark.sphinx('html', testroot='ext-autodoc') -def test_mocked_module_imports(app, warning): - # no autodoc_mock_imports - options = {"members": 'TestAutodoc,decoratedFunction,func'} - actual = do_autodoc(app, 'module', 'target.need_mocks', options) - assert list(actual) == [] - assert "autodoc: failed to import module 'need_mocks'" in warning.getvalue() - - # with autodoc_mock_imports - app.config.autodoc_mock_imports = [ - 'missing_module', - 'missing_package1', - 'missing_package2', - 'missing_package3', - 'sphinx.missing_module4', - ] - - warning.truncate(0) - actual = do_autodoc(app, 'module', 'target.need_mocks', options) - assert list(actual) == [ - '', - '.. py:module:: target.need_mocks', - '', - '', - '.. py:class:: TestAutodoc', - ' :module: target.need_mocks', - '', - ' TestAutodoc docstring.', - ' ', - ' ', - ' .. py:method:: TestAutodoc.decoratedMethod()', - ' :module: target.need_mocks', - ' ', - ' TestAutodoc::decoratedMethod docstring', - ' ', - '', - '.. py:function:: decoratedFunction()', - ' :module: target.need_mocks', - '', - ' decoratedFunction docstring', - ' ', - '', - '.. py:function:: func(arg: missing_module.Class)', - ' :module: target.need_mocks', - '', - ' a function takes mocked object as an argument', - ' ' - ] - assert warning.getvalue() == '' - - @pytest.mark.usefixtures('setup_test') def test_abstractmethods(): options = {"members": None, @@ -1715,234 +1569,6 @@ def test_module_variables(): ] -@pytest.mark.sphinx('html', testroot='ext-autodoc') -def test_autodoc_typehints_signature(app): - app.config.autodoc_typehints = "signature" - - options = {"members": None, - "undoc-members": True} - actual = do_autodoc(app, 'module', 'target.typehints', options) - assert list(actual) == [ - '', - '.. py:module:: target.typehints', - '', - '', - '.. py:class:: Math(s: str, o: object = None)', - ' :module: target.typehints', - '', - ' ', - ' .. py:method:: Math.incr(a: int, b: int = 1) -> int', - ' :module: target.typehints', - ' ', - '', - '.. py:function:: incr(a: int, b: int = 1) -> int', - ' :module: target.typehints', - '' - ] - - -@pytest.mark.sphinx('html', testroot='ext-autodoc') -def test_autodoc_typehints_none(app): - app.config.autodoc_typehints = "none" - - options = {"members": None, - "undoc-members": True} - actual = do_autodoc(app, 'module', 'target.typehints', options) - assert list(actual) == [ - '', - '.. py:module:: target.typehints', - '', - '', - '.. py:class:: Math(s, o = None)', - ' :module: target.typehints', - '', - ' ', - ' .. py:method:: Math.incr(a, b = 1) -> int', - ' :module: target.typehints', - ' ', - '', - '.. py:function:: incr(a, b = 1) -> int', - ' :module: target.typehints', - '' - ] - - -@pytest.mark.sphinx('html', testroot='ext-autodoc') -@pytest.mark.filterwarnings('ignore:autodoc_default_flags is now deprecated.') -def test_merge_autodoc_default_flags1(app): - app.config.autodoc_default_flags = ['members', 'undoc-members'] - merge_autodoc_default_flags(app, app.config) - assert app.config.autodoc_default_options == {'members': None, - 'undoc-members': None} - - -@pytest.mark.sphinx('html', testroot='ext-autodoc') -@pytest.mark.filterwarnings('ignore:autodoc_default_flags is now deprecated.') -def test_merge_autodoc_default_flags2(app): - app.config.autodoc_default_flags = ['members', 'undoc-members'] - app.config.autodoc_default_options = {'members': 'this,that,order', - 'inherited-members': 'this'} - merge_autodoc_default_flags(app, app.config) - assert app.config.autodoc_default_options == {'members': None, - 'undoc-members': None, - 'inherited-members': 'this'} - - -@pytest.mark.sphinx('html', testroot='ext-autodoc') -def test_autodoc_default_options(app): - # no settings - actual = do_autodoc(app, 'class', 'target.enum.EnumCls') - assert ' .. py:attribute:: EnumCls.val1' not in actual - assert ' .. py:attribute:: EnumCls.val4' not in actual - actual = do_autodoc(app, 'class', 'target.CustomIter') - assert ' .. py:method:: target.CustomIter' not in actual - actual = do_autodoc(app, 'module', 'target') - assert '.. py:function:: save_traceback(app: Sphinx) -> str' not in actual - - # with :members: - app.config.autodoc_default_options = {'members': None} - actual = do_autodoc(app, 'class', 'target.enum.EnumCls') - assert ' .. py:attribute:: EnumCls.val1' in actual - assert ' .. py:attribute:: EnumCls.val4' not in actual - - # with :members: = True - app.config.autodoc_default_options = {'members': True} - actual = do_autodoc(app, 'class', 'target.enum.EnumCls') - assert ' .. py:attribute:: EnumCls.val1' in actual - assert ' .. py:attribute:: EnumCls.val4' not in actual - - # with :members: and :undoc-members: - app.config.autodoc_default_options = { - 'members': None, - 'undoc-members': None, - } - actual = do_autodoc(app, 'class', 'target.enum.EnumCls') - assert ' .. py:attribute:: EnumCls.val1' in actual - assert ' .. py:attribute:: EnumCls.val4' in actual - - # with :special-members: - # Note that :members: must be *on* for :special-members: to work. - app.config.autodoc_default_options = { - 'members': None, - 'special-members': None - } - actual = do_autodoc(app, 'class', 'target.CustomIter') - assert ' .. py:method:: CustomIter.__init__()' in actual - assert ' Create a new `CustomIter`.' in actual - assert ' .. py:method:: CustomIter.__iter__()' in actual - assert ' Iterate squares of each value.' in actual - if not IS_PYPY: - assert ' .. py:attribute:: CustomIter.__weakref__' in actual - assert ' list of weak references to the object (if defined)' in actual - - # :exclude-members: None - has no effect. Unlike :members:, - # :special-members:, etc. where None == "include all", here None means - # "no/false/off". - app.config.autodoc_default_options = { - 'members': None, - 'exclude-members': None, - } - actual = do_autodoc(app, 'class', 'target.enum.EnumCls') - assert ' .. py:attribute:: EnumCls.val1' in actual - assert ' .. py:attribute:: EnumCls.val4' not in actual - app.config.autodoc_default_options = { - 'members': None, - 'special-members': None, - 'exclude-members': None, - } - actual = do_autodoc(app, 'class', 'target.CustomIter') - assert ' .. py:method:: CustomIter.__init__()' in actual - assert ' Create a new `CustomIter`.' in actual - assert ' .. py:method:: CustomIter.__iter__()' in actual - assert ' Iterate squares of each value.' in actual - if not IS_PYPY: - assert ' .. py:attribute:: CustomIter.__weakref__' in actual - assert ' list of weak references to the object (if defined)' in actual - assert ' .. py:method:: CustomIter.snafucate()' in actual - assert ' Makes this snafucated.' in actual - - # with :imported-members: - app.config.autodoc_default_options = { - 'members': None, - 'imported-members': None, - 'ignore-module-all': None, - } - actual = do_autodoc(app, 'module', 'target') - print('\n'.join(actual)) - assert '.. py:function:: save_traceback(app: Sphinx) -> str' in actual - - -@pytest.mark.sphinx('html', testroot='ext-autodoc') -def test_autodoc_default_options_with_values(app): - # with :members: - app.config.autodoc_default_options = {'members': 'val1,val2'} - actual = do_autodoc(app, 'class', 'target.enum.EnumCls') - assert ' .. py:attribute:: EnumCls.val1' in actual - assert ' .. py:attribute:: EnumCls.val2' in actual - assert ' .. py:attribute:: EnumCls.val3' not in actual - assert ' .. py:attribute:: EnumCls.val4' not in actual - - # with :member-order: - app.config.autodoc_default_options = { - 'members': None, - 'member-order': 'bysource', - } - actual = do_autodoc(app, 'class', 'target.Class') - assert list(filter(lambda l: '::' in l, actual)) == [ - '.. py:class:: Class(arg)', - ' .. py:method:: Class.meth()', - ' .. py:method:: Class.skipmeth()', - ' .. py:method:: Class.excludemeth()', - ' .. py:attribute:: Class.attr', - ' .. py:attribute:: Class.docattr', - ' .. py:attribute:: Class.udocattr', - ' .. py:attribute:: Class.mdocattr', - ' .. py:method:: Class.moore(a, e, f) -> happiness', - ' .. py:attribute:: Class.inst_attr_inline', - ' .. py:attribute:: Class.inst_attr_comment', - ' .. py:attribute:: Class.inst_attr_string', - ] - - # with :special-members: - app.config.autodoc_default_options = { - 'special-members': '__init__,__iter__', - } - actual = do_autodoc(app, 'class', 'target.CustomIter') - assert ' .. py:method:: CustomIter.__init__()' in actual - assert ' Create a new `CustomIter`.' in actual - assert ' .. py:method:: CustomIter.__iter__()' in actual - assert ' Iterate squares of each value.' in actual - if not IS_PYPY: - assert ' .. py:attribute:: CustomIter.__weakref__' not in actual - assert ' list of weak references to the object (if defined)' not in actual - - # with :exclude-members: - app.config.autodoc_default_options = { - 'members': None, - 'exclude-members': 'val1' - } - actual = do_autodoc(app, 'class', 'target.enum.EnumCls') - assert ' .. py:attribute:: EnumCls.val1' not in actual - assert ' .. py:attribute:: EnumCls.val2' in actual - assert ' .. py:attribute:: EnumCls.val3' in actual - assert ' .. py:attribute:: EnumCls.val4' not in actual - app.config.autodoc_default_options = { - 'members': None, - 'special-members': None, - 'exclude-members': '__weakref__,snafucate', - } - actual = do_autodoc(app, 'class', 'target.CustomIter') - assert ' .. py:method:: CustomIter.__init__()' in actual - assert ' Create a new `CustomIter`.' in actual - assert ' .. py:method:: CustomIter.__iter__()' in actual - assert ' Iterate squares of each value.' in actual - if not IS_PYPY: - assert ' .. py:attribute:: CustomIter.__weakref__' not in actual - assert ' list of weak references to the object (if defined)' not in actual - assert ' .. py:method:: CustomIter.snafucate()' not in actual - assert ' Makes this snafucated.' not in actual - - @pytest.mark.sphinx('html', testroot='pycode-egg') def test_autodoc_for_egged_code(app): options = {"members": None, diff --git a/tests/test_ext_autodoc_configs.py b/tests/test_ext_autodoc_configs.py new file mode 100644 index 000000000..cc8f9dbc1 --- /dev/null +++ b/tests/test_ext_autodoc_configs.py @@ -0,0 +1,386 @@ +""" + test_ext_autodoc_configs + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Test the autodoc extension. This tests mainly for config variables + + :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import platform + +import pytest + +from sphinx.ext.autodoc import merge_autodoc_default_flags +from test_autodoc import do_autodoc + +IS_PYPY = platform.python_implementation() == 'PyPy' + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_docstring_signature(app): + options = {"members": None} + actual = do_autodoc(app, 'class', 'target.DocstringSig', options) + assert list(actual) == [ + '', + '.. py:class:: DocstringSig', + ' :module: target', + '', + ' ', + ' .. py:method:: DocstringSig.meth(FOO, BAR=1) -> BAZ', + ' :module: target', + ' ', + ' First line of docstring', + ' ', + ' rest of docstring', + ' ', + ' ', + ' .. py:method:: DocstringSig.meth2()', + ' :module: target', + ' ', + ' First line, no signature', + ' Second line followed by indentation::', + ' ', + ' indented line', + ' ', + ' ', + ' .. py:method:: DocstringSig.prop1', + ' :module: target', + ' :property:', + ' ', + ' First line of docstring', + ' ', + ' ', + ' .. py:method:: DocstringSig.prop2', + ' :module: target', + ' :property:', + ' ', + ' First line of docstring', + ' Second line of docstring', + ' ' + ] + + # disable autodoc_docstring_signature + app.config.autodoc_docstring_signature = False + actual = do_autodoc(app, 'class', 'target.DocstringSig', options) + assert list(actual) == [ + '', + '.. py:class:: DocstringSig', + ' :module: target', + '', + ' ', + ' .. py:method:: DocstringSig.meth()', + ' :module: target', + ' ', + ' meth(FOO, BAR=1) -> BAZ', + ' First line of docstring', + ' ', + ' rest of docstring', + ' ', + ' ', + ' ', + ' .. py:method:: DocstringSig.meth2()', + ' :module: target', + ' ', + ' First line, no signature', + ' Second line followed by indentation::', + ' ', + ' indented line', + ' ', + ' ', + ' .. py:method:: DocstringSig.prop1', + ' :module: target', + ' :property:', + ' ', + ' DocstringSig.prop1(self)', + ' First line of docstring', + ' ', + ' ', + ' .. py:method:: DocstringSig.prop2', + ' :module: target', + ' :property:', + ' ', + ' First line of docstring', + ' Second line of docstring', + ' ' + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_mocked_module_imports(app, warning): + # no autodoc_mock_imports + options = {"members": 'TestAutodoc,decoratedFunction,func'} + actual = do_autodoc(app, 'module', 'target.need_mocks', options) + assert list(actual) == [] + assert "autodoc: failed to import module 'need_mocks'" in warning.getvalue() + + # with autodoc_mock_imports + app.config.autodoc_mock_imports = [ + 'missing_module', + 'missing_package1', + 'missing_package2', + 'missing_package3', + 'sphinx.missing_module4', + ] + + warning.truncate(0) + actual = do_autodoc(app, 'module', 'target.need_mocks', options) + assert list(actual) == [ + '', + '.. py:module:: target.need_mocks', + '', + '', + '.. py:class:: TestAutodoc', + ' :module: target.need_mocks', + '', + ' TestAutodoc docstring.', + ' ', + ' ', + ' .. py:method:: TestAutodoc.decoratedMethod()', + ' :module: target.need_mocks', + ' ', + ' TestAutodoc::decoratedMethod docstring', + ' ', + '', + '.. py:function:: decoratedFunction()', + ' :module: target.need_mocks', + '', + ' decoratedFunction docstring', + ' ', + '', + '.. py:function:: func(arg: missing_module.Class)', + ' :module: target.need_mocks', + '', + ' a function takes mocked object as an argument', + ' ' + ] + assert warning.getvalue() == '' + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_typehints_signature(app): + app.config.autodoc_typehints = "signature" + + options = {"members": None, + "undoc-members": True} + actual = do_autodoc(app, 'module', 'target.typehints', options) + assert list(actual) == [ + '', + '.. py:module:: target.typehints', + '', + '', + '.. py:class:: Math(s: str, o: object = None)', + ' :module: target.typehints', + '', + ' ', + ' .. py:method:: Math.incr(a: int, b: int = 1) -> int', + ' :module: target.typehints', + ' ', + '', + '.. py:function:: incr(a: int, b: int = 1) -> int', + ' :module: target.typehints', + '' + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_typehints_none(app): + app.config.autodoc_typehints = "none" + + options = {"members": None, + "undoc-members": True} + actual = do_autodoc(app, 'module', 'target.typehints', options) + assert list(actual) == [ + '', + '.. py:module:: target.typehints', + '', + '', + '.. py:class:: Math(s, o = None)', + ' :module: target.typehints', + '', + ' ', + ' .. py:method:: Math.incr(a, b = 1) -> int', + ' :module: target.typehints', + ' ', + '', + '.. py:function:: incr(a, b = 1) -> int', + ' :module: target.typehints', + '' + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +@pytest.mark.filterwarnings('ignore:autodoc_default_flags is now deprecated.') +def test_merge_autodoc_default_flags1(app): + app.config.autodoc_default_flags = ['members', 'undoc-members'] + merge_autodoc_default_flags(app, app.config) + assert app.config.autodoc_default_options == {'members': None, + 'undoc-members': None} + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +@pytest.mark.filterwarnings('ignore:autodoc_default_flags is now deprecated.') +def test_merge_autodoc_default_flags2(app): + app.config.autodoc_default_flags = ['members', 'undoc-members'] + app.config.autodoc_default_options = {'members': 'this,that,order', + 'inherited-members': 'this'} + merge_autodoc_default_flags(app, app.config) + assert app.config.autodoc_default_options == {'members': None, + 'undoc-members': None, + 'inherited-members': 'this'} + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_default_options(app): + # no settings + actual = do_autodoc(app, 'class', 'target.enum.EnumCls') + assert ' .. py:attribute:: EnumCls.val1' not in actual + assert ' .. py:attribute:: EnumCls.val4' not in actual + actual = do_autodoc(app, 'class', 'target.CustomIter') + assert ' .. py:method:: target.CustomIter' not in actual + actual = do_autodoc(app, 'module', 'target') + assert '.. py:function:: save_traceback(app: Sphinx) -> str' not in actual + + # with :members: + app.config.autodoc_default_options = {'members': None} + actual = do_autodoc(app, 'class', 'target.enum.EnumCls') + assert ' .. py:attribute:: EnumCls.val1' in actual + assert ' .. py:attribute:: EnumCls.val4' not in actual + + # with :members: = True + app.config.autodoc_default_options = {'members': True} + actual = do_autodoc(app, 'class', 'target.enum.EnumCls') + assert ' .. py:attribute:: EnumCls.val1' in actual + assert ' .. py:attribute:: EnumCls.val4' not in actual + + # with :members: and :undoc-members: + app.config.autodoc_default_options = { + 'members': None, + 'undoc-members': None, + } + actual = do_autodoc(app, 'class', 'target.enum.EnumCls') + assert ' .. py:attribute:: EnumCls.val1' in actual + assert ' .. py:attribute:: EnumCls.val4' in actual + + # with :special-members: + # Note that :members: must be *on* for :special-members: to work. + app.config.autodoc_default_options = { + 'members': None, + 'special-members': None + } + actual = do_autodoc(app, 'class', 'target.CustomIter') + assert ' .. py:method:: CustomIter.__init__()' in actual + assert ' Create a new `CustomIter`.' in actual + assert ' .. py:method:: CustomIter.__iter__()' in actual + assert ' Iterate squares of each value.' in actual + if not IS_PYPY: + assert ' .. py:attribute:: CustomIter.__weakref__' in actual + assert ' list of weak references to the object (if defined)' in actual + + # :exclude-members: None - has no effect. Unlike :members:, + # :special-members:, etc. where None == "include all", here None means + # "no/false/off". + app.config.autodoc_default_options = { + 'members': None, + 'exclude-members': None, + } + actual = do_autodoc(app, 'class', 'target.enum.EnumCls') + assert ' .. py:attribute:: EnumCls.val1' in actual + assert ' .. py:attribute:: EnumCls.val4' not in actual + app.config.autodoc_default_options = { + 'members': None, + 'special-members': None, + 'exclude-members': None, + } + actual = do_autodoc(app, 'class', 'target.CustomIter') + assert ' .. py:method:: CustomIter.__init__()' in actual + assert ' Create a new `CustomIter`.' in actual + assert ' .. py:method:: CustomIter.__iter__()' in actual + assert ' Iterate squares of each value.' in actual + if not IS_PYPY: + assert ' .. py:attribute:: CustomIter.__weakref__' in actual + assert ' list of weak references to the object (if defined)' in actual + assert ' .. py:method:: CustomIter.snafucate()' in actual + assert ' Makes this snafucated.' in actual + + # with :imported-members: + app.config.autodoc_default_options = { + 'members': None, + 'imported-members': None, + 'ignore-module-all': None, + } + actual = do_autodoc(app, 'module', 'target') + print('\n'.join(actual)) + assert '.. py:function:: save_traceback(app: Sphinx) -> str' in actual + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_default_options_with_values(app): + # with :members: + app.config.autodoc_default_options = {'members': 'val1,val2'} + actual = do_autodoc(app, 'class', 'target.enum.EnumCls') + assert ' .. py:attribute:: EnumCls.val1' in actual + assert ' .. py:attribute:: EnumCls.val2' in actual + assert ' .. py:attribute:: EnumCls.val3' not in actual + assert ' .. py:attribute:: EnumCls.val4' not in actual + + # with :member-order: + app.config.autodoc_default_options = { + 'members': None, + 'member-order': 'bysource', + } + actual = do_autodoc(app, 'class', 'target.Class') + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Class(arg)', + ' .. py:method:: Class.meth()', + ' .. py:method:: Class.skipmeth()', + ' .. py:method:: Class.excludemeth()', + ' .. py:attribute:: Class.attr', + ' .. py:attribute:: Class.docattr', + ' .. py:attribute:: Class.udocattr', + ' .. py:attribute:: Class.mdocattr', + ' .. py:method:: Class.moore(a, e, f) -> happiness', + ' .. py:attribute:: Class.inst_attr_inline', + ' .. py:attribute:: Class.inst_attr_comment', + ' .. py:attribute:: Class.inst_attr_string', + ] + + # with :special-members: + app.config.autodoc_default_options = { + 'special-members': '__init__,__iter__', + } + actual = do_autodoc(app, 'class', 'target.CustomIter') + assert ' .. py:method:: CustomIter.__init__()' in actual + assert ' Create a new `CustomIter`.' in actual + assert ' .. py:method:: CustomIter.__iter__()' in actual + assert ' Iterate squares of each value.' in actual + if not IS_PYPY: + assert ' .. py:attribute:: CustomIter.__weakref__' not in actual + assert ' list of weak references to the object (if defined)' not in actual + + # with :exclude-members: + app.config.autodoc_default_options = { + 'members': None, + 'exclude-members': 'val1' + } + actual = do_autodoc(app, 'class', 'target.enum.EnumCls') + assert ' .. py:attribute:: EnumCls.val1' not in actual + assert ' .. py:attribute:: EnumCls.val2' in actual + assert ' .. py:attribute:: EnumCls.val3' in actual + assert ' .. py:attribute:: EnumCls.val4' not in actual + app.config.autodoc_default_options = { + 'members': None, + 'special-members': None, + 'exclude-members': '__weakref__,snafucate', + } + actual = do_autodoc(app, 'class', 'target.CustomIter') + assert ' .. py:method:: CustomIter.__init__()' in actual + assert ' Create a new `CustomIter`.' in actual + assert ' .. py:method:: CustomIter.__iter__()' in actual + assert ' Iterate squares of each value.' in actual + if not IS_PYPY: + assert ' .. py:attribute:: CustomIter.__weakref__' not in actual + assert ' list of weak references to the object (if defined)' not in actual + assert ' .. py:method:: CustomIter.snafucate()' not in actual + assert ' Makes this snafucated.' not in actual From c8554e7673a97dd2eb4984877cbcfffdcc739e5c Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 3 Jun 2019 01:32:01 +0900 Subject: [PATCH 08/84] test: Move testcases for autodoc events to test_ext_autodoc_events --- tests/test_autodoc.py | 69 +-------------------------- tests/test_ext_autodoc_events.py | 81 ++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 68 deletions(-) create mode 100644 tests/test_ext_autodoc_events.py diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 3cd39538a..877c8a204 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -16,7 +16,7 @@ from warnings import catch_warnings import pytest from docutils.statemachine import ViewList -from sphinx.ext.autodoc import ModuleLevelDocumenter, cut_lines, between, ALL, Options +from sphinx.ext.autodoc import ModuleLevelDocumenter, ALL, Options from sphinx.ext.autodoc.directive import DocumenterBridge, process_documenter_options from sphinx.testing.util import SphinxTestApp, Struct # NOQA from sphinx.util import logging @@ -464,73 +464,6 @@ def test_get_doc(): assert getdocl('method', Derived.inheritedmeth) == ['Inherited function.'] -@pytest.mark.sphinx('html', testroot='ext-autodoc') -def test_process_docstring(app): - def on_process_docstring(app, what, name, obj, options, lines): - lines.clear() - lines.append('my docstring') - - app.connect('autodoc-process-docstring', on_process_docstring) - - actual = do_autodoc(app, 'function', 'target.process_docstring.func') - assert list(actual) == [ - '', - '.. py:function:: func()', - ' :module: target.process_docstring', - '', - ' my docstring' - ] - - -@pytest.mark.sphinx('html', testroot='ext-autodoc') -def test_cut_lines(app): - app.connect('autodoc-process-docstring', - cut_lines(2, 2, ['function'])) - - actual = do_autodoc(app, 'function', 'target.process_docstring.func') - assert list(actual) == [ - '', - '.. py:function:: func()', - ' :module: target.process_docstring', - '', - ' second line', - ' ' - ] - - -@pytest.mark.sphinx('html', testroot='ext-autodoc') -def test_between(app): - app.connect('autodoc-process-docstring', - between('---', ['function'])) - - actual = do_autodoc(app, 'function', 'target.process_docstring.func') - assert list(actual) == [ - '', - '.. py:function:: func()', - ' :module: target.process_docstring', - '', - ' second line', - ' ' - ] - - -@pytest.mark.sphinx('html', testroot='ext-autodoc') -def test_between_exclude(app): - app.connect('autodoc-process-docstring', - between('---', ['function'], exclude=True)) - - actual = do_autodoc(app, 'function', 'target.process_docstring.func') - assert list(actual) == [ - '', - '.. py:function:: func()', - ' :module: target.process_docstring', - '', - ' first line', - ' third line', - ' ' - ] - - @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_new_documenter(app): class MyDocumenter(ModuleLevelDocumenter): diff --git a/tests/test_ext_autodoc_events.py b/tests/test_ext_autodoc_events.py new file mode 100644 index 000000000..647def3d7 --- /dev/null +++ b/tests/test_ext_autodoc_events.py @@ -0,0 +1,81 @@ +""" + test_ext_autodoc_events + ~~~~~~~~~~~~~~~~~~~~~~~ + + Test the autodoc extension. This tests mainly for autodoc events + + :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import pytest + +from sphinx.ext.autodoc import between, cut_lines +from test_autodoc import do_autodoc + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_process_docstring(app): + def on_process_docstring(app, what, name, obj, options, lines): + lines.clear() + lines.append('my docstring') + + app.connect('autodoc-process-docstring', on_process_docstring) + + actual = do_autodoc(app, 'function', 'target.process_docstring.func') + assert list(actual) == [ + '', + '.. py:function:: func()', + ' :module: target.process_docstring', + '', + ' my docstring' + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_cut_lines(app): + app.connect('autodoc-process-docstring', + cut_lines(2, 2, ['function'])) + + actual = do_autodoc(app, 'function', 'target.process_docstring.func') + assert list(actual) == [ + '', + '.. py:function:: func()', + ' :module: target.process_docstring', + '', + ' second line', + ' ' + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_between(app): + app.connect('autodoc-process-docstring', + between('---', ['function'])) + + actual = do_autodoc(app, 'function', 'target.process_docstring.func') + assert list(actual) == [ + '', + '.. py:function:: func()', + ' :module: target.process_docstring', + '', + ' second line', + ' ' + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_between_exclude(app): + app.connect('autodoc-process-docstring', + between('---', ['function'], exclude=True)) + + actual = do_autodoc(app, 'function', 'target.process_docstring.func') + assert list(actual) == [ + '', + '.. py:function:: func()', + ' :module: target.process_docstring', + '', + ' first line', + ' third line', + ' ' + ] From 20f2845e218ffee6461868a4c92f0ccd98f6b3ba Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 2 Jun 2019 21:47:16 +0900 Subject: [PATCH 09/84] Migrate to py3 style type annotation: sphinx.util.inventory --- sphinx/util/inventory.py | 47 +++++++++++++++------------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/sphinx/util/inventory.py b/sphinx/util/inventory.py index 0fdc3e833..43a868e95 100644 --- a/sphinx/util/inventory.py +++ b/sphinx/util/inventory.py @@ -10,20 +10,20 @@ import os import re import zlib +from typing import Callable, IO, Iterator from sphinx.util import logging - -if False: - # For type annotation - from typing import Callable, IO, Iterator # NOQA - from sphinx.builders import Builder # NOQA - from sphinx.environment import BuildEnvironment # NOQA - from sphinx.util.typing import Inventory # NOQA +from sphinx.util.typing import Inventory BUFSIZE = 16 * 1024 logger = logging.getLogger(__name__) +if False: + # For type annotation + from sphinx.builders import Builder + from sphinx.environment import BuildEnvironment + class InventoryFileReader: """A file reader for inventory file. @@ -31,21 +31,18 @@ class InventoryFileReader: This reader supports mixture of texts and compressed texts. """ - def __init__(self, stream): - # type: (IO) -> None + def __init__(self, stream: IO) -> None: self.stream = stream self.buffer = b'' self.eof = False - def read_buffer(self): - # type: () -> None + def read_buffer(self) -> None: chunk = self.stream.read(BUFSIZE) if chunk == b'': self.eof = True self.buffer += chunk - def readline(self): - # type: () -> str + def readline(self) -> str: pos = self.buffer.find(b'\n') if pos != -1: line = self.buffer[:pos].decode() @@ -59,15 +56,13 @@ class InventoryFileReader: return line - def readlines(self): - # type: () -> Iterator[str] + def readlines(self) -> Iterator[str]: while not self.eof: line = self.readline() if line: yield line - def read_compressed_chunks(self): - # type: () -> Iterator[bytes] + def read_compressed_chunks(self) -> Iterator[bytes]: decompressor = zlib.decompressobj() while not self.eof: self.read_buffer() @@ -75,8 +70,7 @@ class InventoryFileReader: self.buffer = b'' yield decompressor.flush() - def read_compressed_lines(self): - # type: () -> Iterator[str] + def read_compressed_lines(self) -> Iterator[str]: buf = b'' for chunk in self.read_compressed_chunks(): buf += chunk @@ -89,8 +83,7 @@ class InventoryFileReader: class InventoryFile: @classmethod - def load(cls, stream, uri, joinfunc): - # type: (IO, str, Callable) -> Inventory + def load(cls, stream: IO, uri: str, joinfunc: Callable) -> Inventory: reader = InventoryFileReader(stream) line = reader.readline().rstrip() if line == '# Sphinx inventory version 1': @@ -101,8 +94,7 @@ class InventoryFile: raise ValueError('invalid inventory header: %s' % line) @classmethod - def load_v1(cls, stream, uri, join): - # type: (InventoryFileReader, str, Callable) -> Inventory + def load_v1(cls, stream: InventoryFileReader, uri: str, join: Callable) -> Inventory: invdata = {} # type: Inventory projname = stream.readline().rstrip()[11:] version = stream.readline().rstrip()[11:] @@ -120,8 +112,7 @@ class InventoryFile: return invdata @classmethod - def load_v2(cls, stream, uri, join): - # type: (InventoryFileReader, str, Callable) -> Inventory + def load_v2(cls, stream: InventoryFileReader, uri: str, join: Callable) -> Inventory: invdata = {} # type: Inventory projname = stream.readline().rstrip()[11:] version = stream.readline().rstrip()[11:] @@ -150,10 +141,8 @@ class InventoryFile: return invdata @classmethod - def dump(cls, filename, env, builder): - # type: (str, BuildEnvironment, Builder) -> None - def escape(string): - # type: (str) -> str + def dump(cls, filename: str, env: "BuildEnvironment", builder: "Builder") -> None: + def escape(string: str) -> str: return re.sub("\\s+", " ", string) with open(os.path.join(filename), 'wb') as f: From d9469c08ed02634df11cf9dc7a3df3e512cedb8b Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 2 Jun 2019 21:49:09 +0900 Subject: [PATCH 10/84] Migrate to py3 style type annotation: sphinx.util.jsdump --- sphinx/util/jsdump.py | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/sphinx/util/jsdump.py b/sphinx/util/jsdump.py index 0bab2f014..bfdba170b 100644 --- a/sphinx/util/jsdump.py +++ b/sphinx/util/jsdump.py @@ -10,10 +10,7 @@ """ import re - -if False: - # For type annotation - from typing import Any, Dict, IO, List, Match, Union # NOQA +from typing import Any, Dict, IO, List, Match, Union _str_re = re.compile(r'"(\\\\|\\"|[^"])*"') _int_re = re.compile(r'\d+') @@ -35,10 +32,8 @@ ESCAPE_DICT = { ESCAPED = re.compile(r'\\u.{4}|\\.') -def encode_string(s): - # type: (str) -> str - def replace(match): - # type: (Match) -> str +def encode_string(s: str) -> str: + def replace(match: Match) -> str: s = match.group(0) try: return ESCAPE_DICT[s] @@ -55,8 +50,7 @@ def encode_string(s): return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"' -def decode_string(s): - # type: (str) -> str +def decode_string(s: str) -> str: return ESCAPED.sub(lambda m: eval('"' + m.group() + '"'), s) @@ -78,8 +72,7 @@ do import static with double in super""".split()) -def dumps(obj, key=False): - # type: (Any, bool) -> str +def dumps(obj: Any, key: bool = False) -> str: if key: if not isinstance(obj, str): obj = str(obj) @@ -107,13 +100,11 @@ def dumps(obj, key=False): raise TypeError(type(obj)) -def dump(obj, f): - # type: (Any, IO) -> None +def dump(obj: Any, f: IO) -> None: f.write(dumps(obj)) -def loads(x): - # type: (str) -> Any +def loads(x: str) -> Any: """Loader that can read the JS subset the indexer produces.""" nothing = object() i = 0 @@ -205,6 +196,5 @@ def loads(x): return obj -def load(f): - # type: (IO) -> Any +def load(f: IO) -> Any: return loads(f.read()) From d59e362f5f7d1df333ad6c3b1c24fabd000230cc Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 2 Jun 2019 21:49:21 +0900 Subject: [PATCH 11/84] Migrate to py3 style type annotation: sphinx.util.jsonimpl --- sphinx/util/jsonimpl.py | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/sphinx/util/jsonimpl.py b/sphinx/util/jsonimpl.py index 4fb8e1f5d..c5336a195 100644 --- a/sphinx/util/jsonimpl.py +++ b/sphinx/util/jsonimpl.py @@ -11,13 +11,10 @@ import json import warnings from collections import UserString +from typing import Any, IO from sphinx.deprecation import RemovedInSphinx40Warning -if False: - # For type annotation - from typing import Any, IO # NOQA - warnings.warn('sphinx.util.jsonimpl is deprecated', RemovedInSphinx40Warning, stacklevel=2) @@ -25,30 +22,25 @@ warnings.warn('sphinx.util.jsonimpl is deprecated', class SphinxJSONEncoder(json.JSONEncoder): """JSONEncoder subclass that forces translation proxies.""" - def default(self, obj): - # type: (Any) -> str + def default(self, obj: Any) -> str: if isinstance(obj, UserString): return str(obj) return super().default(obj) -def dump(obj, fp, *args, **kwds): - # type: (Any, IO, Any, Any) -> None +def dump(obj: Any, fp: IO, *args, **kwds) -> None: kwds['cls'] = SphinxJSONEncoder json.dump(obj, fp, *args, **kwds) -def dumps(obj, *args, **kwds): - # type: (Any, Any, Any) -> str +def dumps(obj: Any, *args, **kwds) -> str: kwds['cls'] = SphinxJSONEncoder return json.dumps(obj, *args, **kwds) -def load(*args, **kwds): - # type: (Any, Any) -> Any +def load(*args, **kwds) -> Any: return json.load(*args, **kwds) -def loads(*args, **kwds): - # type: (Any, Any) -> Any +def loads(*args, **kwds) -> Any: return json.loads(*args, **kwds) From 47d9035bca9e83d6db30a0726a02dc9265bd66b1 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 19 Apr 2019 13:22:17 +0900 Subject: [PATCH 12/84] Migrate to py3 style type annotation: sphinx.util.logging --- sphinx/util/logging.py | 116 ++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 76 deletions(-) diff --git a/sphinx/util/logging.py b/sphinx/util/logging.py index afa4ebd23..d6667dacd 100644 --- a/sphinx/util/logging.py +++ b/sphinx/util/logging.py @@ -12,8 +12,10 @@ import logging import logging.handlers from collections import defaultdict from contextlib import contextmanager +from typing import Any, Dict, Generator, IO, List, Tuple, Type, Union from docutils import nodes +from docutils.nodes import Node from docutils.utils import get_source_line from sphinx.errors import SphinxWarning @@ -21,8 +23,7 @@ from sphinx.util.console import colorize if False: # For type annotation - from typing import Any, Dict, Generator, IO, List, Tuple, Type, Union # NOQA - from sphinx.application import Sphinx # NOQA + from sphinx.application import Sphinx NAMESPACE = 'sphinx' @@ -54,8 +55,7 @@ COLOR_MAP = defaultdict(lambda: 'blue', }) -def getLogger(name): - # type: (str) -> SphinxLoggerAdapter +def getLogger(name: str) -> "SphinxLoggerAdapter": """Get logger wrapped by :class:`sphinx.util.logging.SphinxLoggerAdapter`. Sphinx logger always uses ``sphinx.*`` namespace to be independent from @@ -77,8 +77,7 @@ def getLogger(name): return SphinxLoggerAdapter(logger, {}) -def convert_serializable(records): - # type: (List[logging.LogRecord]) -> None +def convert_serializable(records: List[logging.LogRecord]) -> None: """Convert LogRecord serializable.""" for r in records: # extract arguments to a message and clear them @@ -95,8 +94,7 @@ class SphinxLogRecord(logging.LogRecord): prefix = '' location = None # type: Any - def getMessage(self): - # type: () -> str + def getMessage(self) -> str: message = super().getMessage() location = getattr(self, 'location', None) if location: @@ -120,20 +118,17 @@ class SphinxWarningLogRecord(SphinxLogRecord): class SphinxLoggerAdapter(logging.LoggerAdapter): """LoggerAdapter allowing ``type`` and ``subtype`` keywords.""" - def log(self, level, msg, *args, **kwargs): - # type: (Union[int, str], str, Any, Any) -> None + def log(self, level: Union[int, str], msg: str, *args, **kwargs) -> None: if isinstance(level, int): super().log(level, msg, *args, **kwargs) else: levelno = LEVEL_NAMES[level] super().log(levelno, msg, *args, **kwargs) - def verbose(self, msg, *args, **kwargs): - # type: (str, Any, Any) -> None + def verbose(self, msg: str, *args, **kwargs) -> None: self.log(VERBOSE, msg, *args, **kwargs) - def process(self, msg, kwargs): # type: ignore - # type: (str, Dict) -> Tuple[str, Dict] + def process(self, msg: str, kwargs: Dict) -> Tuple[str, Dict]: # type: ignore extra = kwargs.setdefault('extra', {}) if 'type' in kwargs: extra['type'] = kwargs.pop('type') @@ -148,8 +143,7 @@ class SphinxLoggerAdapter(logging.LoggerAdapter): return msg, kwargs - def handle(self, record): - # type: (logging.LogRecord) -> None + def handle(self, record: logging.LogRecord) -> None: self.logger.handle(record) @@ -161,8 +155,7 @@ class WarningStreamHandler(logging.StreamHandler): class NewLineStreamHandler(logging.StreamHandler): """StreamHandler which switches line terminator by record.nonl flag.""" - def emit(self, record): - # type: (logging.LogRecord) -> None + def emit(self, record: logging.LogRecord) -> None: try: self.acquire() if getattr(record, 'nonl', False): @@ -177,16 +170,13 @@ class NewLineStreamHandler(logging.StreamHandler): class MemoryHandler(logging.handlers.BufferingHandler): """Handler buffering all logs.""" - def __init__(self): - # type: () -> None + def __init__(self) -> None: super().__init__(-1) - def shouldFlush(self, record): - # type: (logging.LogRecord) -> bool + def shouldFlush(self, record: logging.LogRecord) -> bool: return False # never flush - def flushTo(self, logger): - # type: (logging.Logger) -> None + def flushTo(self, logger: logging.Logger) -> None: self.acquire() try: for record in self.buffer: @@ -195,15 +185,13 @@ class MemoryHandler(logging.handlers.BufferingHandler): finally: self.release() - def clear(self): - # type: () -> List[logging.LogRecord] + def clear(self) -> List[logging.LogRecord]: buffer, self.buffer = self.buffer, [] return buffer @contextmanager -def pending_warnings(): - # type: () -> Generator +def pending_warnings() -> Generator[logging.Handler, None, None]: """Contextmanager to pend logging warnings temporary. Similar to :func:`pending_logging`. @@ -231,8 +219,7 @@ def pending_warnings(): @contextmanager -def pending_logging(): - # type: () -> Generator +def pending_logging() -> Generator[MemoryHandler, None, None]: """Contextmanager to pend logging all logs temporary. For example:: @@ -264,8 +251,7 @@ def pending_logging(): @contextmanager -def skip_warningiserror(skip=True): - # type: (bool) -> Generator +def skip_warningiserror(skip: bool = True) -> Generator[None, None, None]: """contextmanager to skip WarningIsErrorFilter for a while.""" logger = logging.getLogger(NAMESPACE) @@ -285,8 +271,7 @@ def skip_warningiserror(skip=True): @contextmanager -def prefixed_warnings(prefix): - # type: (str) -> Generator +def prefixed_warnings(prefix: str) -> Generator[None, None, None]: """Prepend prefix to all records for a while. For example:: @@ -332,13 +317,11 @@ def prefixed_warnings(prefix): class LogCollector: - def __init__(self): - # type: () -> None + def __init__(self) -> None: self.logs = [] # type: List[logging.LogRecord] @contextmanager - def collect(self): - # type: () -> Generator + def collect(self) -> Generator[None, None, None]: with pending_logging() as memhandler: yield @@ -348,16 +331,14 @@ class LogCollector: class InfoFilter(logging.Filter): """Filter error and warning messages.""" - def filter(self, record): - # type: (logging.LogRecord) -> bool + def filter(self, record: logging.LogRecord) -> bool: if record.levelno < logging.WARNING: return True else: return False -def is_suppressed_warning(type, subtype, suppress_warnings): - # type: (str, str, List[str]) -> bool +def is_suppressed_warning(type: str, subtype: str, suppress_warnings: List[str]) -> bool: """Check the warning is suppressed or not.""" if type is None: return False @@ -379,13 +360,11 @@ def is_suppressed_warning(type, subtype, suppress_warnings): class WarningSuppressor(logging.Filter): """Filter logs by `suppress_warnings`.""" - def __init__(self, app): - # type: (Sphinx) -> None + def __init__(self, app: "Sphinx") -> None: self.app = app super().__init__() - def filter(self, record): - # type: (logging.LogRecord) -> bool + def filter(self, record: logging.LogRecord) -> bool: type = getattr(record, 'type', None) subtype = getattr(record, 'subtype', None) @@ -405,13 +384,11 @@ class WarningSuppressor(logging.Filter): class WarningIsErrorFilter(logging.Filter): """Raise exception if warning emitted.""" - def __init__(self, app): - # type: (Sphinx) -> None + def __init__(self, app: "Sphinx") -> None: self.app = app super().__init__() - def filter(self, record): - # type: (logging.LogRecord) -> bool + def filter(self, record: logging.LogRecord) -> bool: if getattr(record, 'skip_warningsiserror', False): # disabled by DisableWarningIsErrorFilter return True @@ -433,8 +410,7 @@ class WarningIsErrorFilter(logging.Filter): class DisableWarningIsErrorFilter(logging.Filter): """Disable WarningIsErrorFilter if this filter installed.""" - def filter(self, record): - # type: (logging.LogRecord) -> bool + def filter(self, record: logging.LogRecord) -> bool: record.skip_warningsiserror = True # type: ignore return True @@ -442,13 +418,11 @@ class DisableWarningIsErrorFilter(logging.Filter): class MessagePrefixFilter(logging.Filter): """Prepend prefix to all records.""" - def __init__(self, prefix): - # type: (str) -> None + def __init__(self, prefix: str) -> None: self.prefix = prefix super().__init__() - def filter(self, record): - # type: (logging.LogRecord) -> bool + def filter(self, record: logging.LogRecord) -> bool: if self.prefix: record.msg = self.prefix + ' ' + record.msg return True @@ -462,13 +436,11 @@ class SphinxLogRecordTranslator(logging.Filter): """ LogRecordClass = None # type: Type[logging.LogRecord] - def __init__(self, app): - # type: (Sphinx) -> None + def __init__(self, app: "Sphinx") -> None: self.app = app super().__init__() - def filter(self, record): # type: ignore - # type: (SphinxWarningLogRecord) -> bool + def filter(self, record: SphinxWarningLogRecord) -> bool: # type: ignore if isinstance(record, logging.LogRecord): # force subclassing to handle location record.__class__ = self.LogRecordClass # type: ignore @@ -500,8 +472,7 @@ class WarningLogRecordTranslator(SphinxLogRecordTranslator): LogRecordClass = SphinxWarningLogRecord -def get_node_location(node): - # type: (nodes.Node) -> str +def get_node_location(node: Node) -> str: (source, line) = get_source_line(node) if source and line: return "%s:%s" % (source, line) @@ -514,8 +485,7 @@ def get_node_location(node): class ColorizeFormatter(logging.Formatter): - def format(self, record): - # type: (logging.LogRecord) -> str + def format(self, record: logging.LogRecord) -> str: message = super().format(record) color = getattr(record, 'color', None) if color is None: @@ -529,13 +499,11 @@ class ColorizeFormatter(logging.Formatter): class SafeEncodingWriter: """Stream writer which ignores UnicodeEncodeError silently""" - def __init__(self, stream): - # type: (IO) -> None + def __init__(self, stream: IO) -> None: self.stream = stream self.encoding = getattr(stream, 'encoding', 'ascii') or 'ascii' - def write(self, data): - # type: (str) -> None + def write(self, data: str) -> None: try: self.stream.write(data) except UnicodeEncodeError: @@ -543,25 +511,21 @@ class SafeEncodingWriter: # non-encodable characters, then decode them. self.stream.write(data.encode(self.encoding, 'replace').decode(self.encoding)) - def flush(self): - # type: () -> None + def flush(self) -> None: if hasattr(self.stream, 'flush'): self.stream.flush() class LastMessagesWriter: """Stream writer which memories last 10 messages to save trackback""" - def __init__(self, app, stream): - # type: (Sphinx, IO) -> None + def __init__(self, app: "Sphinx", stream: IO) -> None: self.app = app - def write(self, data): - # type: (str) -> None + def write(self, data: str) -> None: self.app.messagelog.append(data) -def setup(app, status, warning): - # type: (Sphinx, IO, IO) -> None +def setup(app: "Sphinx", status: IO, warning: IO) -> None: """Setup root logger for Sphinx""" logger = logging.getLogger(NAMESPACE) logger.setLevel(logging.DEBUG) From 24f8a3caf03ee2c11a1746413d68a572cb59f23e Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 2 Jun 2019 17:00:03 +0900 Subject: [PATCH 13/84] Migrate to py3 style type annotation: sphinx.util.console --- sphinx/util/console.py | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/sphinx/util/console.py b/sphinx/util/console.py index c207d32ac..d73d0563e 100644 --- a/sphinx/util/console.py +++ b/sphinx/util/console.py @@ -11,6 +11,7 @@ import os import re import sys +from typing import Dict try: # check if colorama is installed to support color on Windows @@ -18,23 +19,17 @@ try: except ImportError: colorama = None -if False: - # For type annotation - from typing import Dict # NOQA - _ansi_re = re.compile('\x1b\\[(\\d\\d;){0,2}\\d\\dm') codes = {} # type: Dict[str, str] -def terminal_safe(s): - # type: (str) -> str +def terminal_safe(s: str) -> str: """safely encode a string for printing to the terminal.""" return s.encode('ascii', 'backslashreplace').decode('ascii') -def get_terminal_width(): - # type: () -> int +def get_terminal_width() -> int: """Borrowed from the py lib.""" try: import termios @@ -53,8 +48,7 @@ def get_terminal_width(): _tw = get_terminal_width() -def term_width_line(text): - # type: (str) -> str +def term_width_line(text: str) -> str: if not codes: # if no coloring, don't output fancy backspaces return text + '\n' @@ -63,8 +57,7 @@ def term_width_line(text): return text.ljust(_tw + len(text) - len(_ansi_re.sub('', text))) + '\r' -def color_terminal(): - # type: () -> bool +def color_terminal() -> bool: if sys.platform == 'win32' and colorama is not None: colorama.init() return True @@ -80,21 +73,18 @@ def color_terminal(): return False -def nocolor(): - # type: () -> None +def nocolor() -> None: if sys.platform == 'win32' and colorama is not None: colorama.deinit() codes.clear() -def coloron(): - # type: () -> None +def coloron() -> None: codes.update(_orig_codes) -def colorize(name, text, input_mode=False): - # type: (str, str, bool) -> str - def escseq(name): +def colorize(name: str, text: str, input_mode: bool = False) -> str: + def escseq(name: str) -> str: # Wrap escape sequence with ``\1`` and ``\2`` to let readline know # it is non-printable characters # ref: https://tiswww.case.edu/php/chet/readline/readline.html @@ -109,15 +99,12 @@ def colorize(name, text, input_mode=False): return escseq(name) + text + escseq('reset') -def strip_colors(s): - # type: (str) -> str +def strip_colors(s: str) -> str: return re.compile('\x1b.*?m').sub('', s) -def create_color_func(name): - # type: (str) -> None - def inner(text): - # type: (str) -> str +def create_color_func(name: str) -> None: + def inner(text: str) -> str: return colorize(name, text) globals()[name] = inner From f3e45e485e9caefb9099e0598549b73231401264 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 2 Jun 2019 17:01:28 +0900 Subject: [PATCH 14/84] Migrate to py3 style type annotation: sphinx.util.compat --- sphinx/util/compat.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/sphinx/util/compat.py b/sphinx/util/compat.py index 492c313d0..75ef90350 100644 --- a/sphinx/util/compat.py +++ b/sphinx/util/compat.py @@ -10,23 +10,22 @@ import sys import warnings +from typing import Any, Dict from docutils.utils import get_source_line from sphinx import addnodes +from sphinx.config import Config from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning from sphinx.transforms import SphinxTransform from sphinx.util import import_object if False: # For type annotation - from typing import Any, Dict # NOQA - from sphinx.application import Sphinx # NOQA - from sphinx.config import Config # NOQA + from sphinx.application import Sphinx -def deprecate_source_parsers(app, config): - # type: (Sphinx, Config) -> None +def deprecate_source_parsers(app: "Sphinx", config: Config) -> None: if config.source_parsers: warnings.warn('The config variable "source_parsers" is deprecated. ' 'Please update your extension for the parser and remove the setting.', @@ -37,8 +36,7 @@ def deprecate_source_parsers(app, config): app.add_source_parser(suffix, parser) -def register_application_for_autosummary(app): - # type: (Sphinx) -> None +def register_application_for_autosummary(app: "Sphinx") -> None: """Register application object to autosummary module. Since Sphinx-1.7, documenters and attrgetters are registered into @@ -55,8 +53,7 @@ class IndexEntriesMigrator(SphinxTransform): """Migrating indexentries from old style (4columns) to new style (5columns).""" default_priority = 700 - def apply(self, **kwargs): - # type: (Any) -> None + def apply(self, **kwargs) -> None: for node in self.document.traverse(addnodes.index): for i, entries in enumerate(node['entries']): if len(entries) == 4: @@ -66,8 +63,7 @@ class IndexEntriesMigrator(SphinxTransform): node['entries'][i] = entries + (None,) -def setup(app): - # type: (Sphinx) -> Dict[str, Any] +def setup(app: "Sphinx") -> Dict[str, Any]: app.add_transform(IndexEntriesMigrator) app.connect('config-inited', deprecate_source_parsers) app.connect('builder-inited', register_application_for_autosummary) From 39c7ee955b71745d2503f61b5ba488345e1b38a4 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 2 Jun 2019 17:46:34 +0900 Subject: [PATCH 15/84] Migrate to py3 style type annotation: sphinx.util.tags --- sphinx/util/tags.py | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/sphinx/util/tags.py b/sphinx/util/tags.py index 193e7ef1e..7e7ba4661 100644 --- a/sphinx/util/tags.py +++ b/sphinx/util/tags.py @@ -6,26 +6,23 @@ :license: BSD, see LICENSE for details. """ -# (ab)use the Jinja parser for parsing our boolean expressions +from typing import Iterator, List + from jinja2 import nodes from jinja2.environment import Environment +from jinja2.nodes import Node from jinja2.parser import Parser env = Environment() -if False: - # For type annotation - from typing import Iterator, List # NOQA - class BooleanParser(Parser): """ Only allow condition exprs and/or/not operations. """ - def parse_compare(self): - # type: () -> nodes.Node - node = None # type: nodes.Node + def parse_compare(self) -> Node: + node = None # type: Node token = self.stream.current if token.type == 'name': if token.value in ('true', 'false', 'True', 'False'): @@ -46,38 +43,31 @@ class BooleanParser(Parser): class Tags: - def __init__(self, tags=None): - # type: (List[str]) -> None + def __init__(self, tags: List[str] = None) -> None: self.tags = dict.fromkeys(tags or [], True) - def has(self, tag): - # type: (str) -> bool + def has(self, tag: str) -> bool: return tag in self.tags __contains__ = has - def __iter__(self): - # type: () -> Iterator[str] + def __iter__(self) -> Iterator[str]: return iter(self.tags) - def add(self, tag): - # type: (str) -> None + def add(self, tag: str) -> None: self.tags[tag] = True - def remove(self, tag): - # type: (str) -> None + def remove(self, tag: str) -> None: self.tags.pop(tag, None) - def eval_condition(self, condition): - # type: (str) -> bool + def eval_condition(self, condition: str) -> bool: # exceptions are handled by the caller parser = BooleanParser(env, condition, state='variable') expr = parser.parse_expression() if not parser.stream.eos: raise ValueError('chunk after expression') - def eval_node(node): - # type: (nodes.Node) -> bool + def eval_node(node: Node) -> bool: if isinstance(node, nodes.CondExpr): if eval_node(node.test): # type: ignore return eval_node(node.expr1) # type: ignore From dd4741c352ab45e28c9b0c4fbd2d366d224be503 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 2 Jun 2019 17:47:15 +0900 Subject: [PATCH 16/84] Migrate to py3 style type annotation: sphinx.util.texescape --- sphinx/util/texescape.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/sphinx/util/texescape.py b/sphinx/util/texescape.py index cc8c0fe1e..408ec1253 100644 --- a/sphinx/util/texescape.py +++ b/sphinx/util/texescape.py @@ -9,10 +9,7 @@ """ import re - -if False: - # For type annotation - from typing import Dict # NOQA +from typing import Dict tex_replacements = [ # map TeX special chars @@ -78,20 +75,17 @@ tex_replace_map = {} tex_hl_escape_map_new = {} -def escape(s): - # type: (str) -> str +def escape(s: str) -> str: """Escape text for LaTeX output.""" return s.translate(tex_escape_map) -def escape_abbr(text): - # type: (str) -> str +def escape_abbr(text: str) -> str: """Adjust spacing after abbreviations. Works with @ letter or other.""" return re.sub(r'\.(?=\s|$)', r'.\@{}', text) -def init(): - # type: () -> None +def init() -> None: for a, b in tex_replacements: tex_escape_map[ord(a)] = b tex_replace_map[ord(a)] = '_' From 086eac39148af091905d0f81bb484c72055c3c0e Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 2 Jun 2019 17:48:07 +0900 Subject: [PATCH 17/84] Migrate to py3 style type annotation: sphinx.util.docutils --- sphinx/util/docutils.py | 152 +++++++++++++++------------------------- 1 file changed, 58 insertions(+), 94 deletions(-) diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index eebfec464..42ff5c4b8 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -16,33 +16,33 @@ from contextlib import contextmanager from copy import copy from distutils.version import LooseVersion from os import path -from typing import IO, cast +from types import ModuleType +from typing import Any, Callable, Dict, Generator, IO, List, Set, Tuple, Type +from typing import cast import docutils from docutils import nodes from docutils.io import FileOutput +from docutils.nodes import Element, Node, system_message from docutils.parsers.rst import Directive, directives, roles, convert_directive_function -from docutils.statemachine import StateMachine +from docutils.parsers.rst.states import Inliner +from docutils.statemachine import StateMachine, State, StringList from docutils.utils import Reporter, unescape from sphinx.deprecation import RemovedInSphinx30Warning from sphinx.errors import ExtensionError, SphinxError from sphinx.locale import __ from sphinx.util import logging +from sphinx.util.typing import RoleFunction logger = logging.getLogger(__name__) report_re = re.compile('^(.+?:(?:\\d+)?): \\((DEBUG|INFO|WARNING|ERROR|SEVERE)/(\\d+)?\\) ') if False: # For type annotation - from types import ModuleType # NOQA - from typing import Any, Callable, Dict, Generator, List, Set, Tuple, Type # NOQA - from docutils.parsers.rst.states import Inliner # NOQA - from docutils.statemachine import State, StringList # NOQA - from sphinx.builders import Builder # NOQA - from sphinx.config import Config # NOQA - from sphinx.environment import BuildEnvironment # NOQA - from sphinx.util.typing import RoleFunction # NOQA + from sphinx.builders import Builder + from sphinx.config import Config + from sphinx.environment import BuildEnvironment __version_info__ = tuple(LooseVersion(docutils.__version__).version) @@ -50,8 +50,7 @@ additional_nodes = set() # type: Set[Type[nodes.Element]] @contextmanager -def docutils_namespace(): - # type: () -> Generator[None, None, None] +def docutils_namespace() -> Generator[None, None, None]: """Create namespace for reST parsers.""" try: _directives = copy(directives._directives) # type: ignore @@ -67,14 +66,12 @@ def docutils_namespace(): additional_nodes.discard(node) -def is_directive_registered(name): - # type: (str) -> bool +def is_directive_registered(name: str) -> bool: """Check the *name* directive is already registered.""" return name in directives._directives # type: ignore -def register_directive(name, directive): - # type: (str, Type[Directive]) -> None +def register_directive(name: str, directive: Type[Directive]) -> None: """Register a directive to docutils. This modifies global state of docutils. So it is better to use this @@ -83,14 +80,12 @@ def register_directive(name, directive): directives.register_directive(name, directive) -def is_role_registered(name): - # type: (str) -> bool +def is_role_registered(name: str) -> bool: """Check the *name* role is already registered.""" return name in roles._roles # type: ignore -def register_role(name, role): - # type: (str, RoleFunction) -> None +def register_role(name: str, role: RoleFunction) -> None: """Register a role to docutils. This modifies global state of docutils. So it is better to use this @@ -99,20 +94,17 @@ def register_role(name, role): roles.register_local_role(name, role) -def unregister_role(name): - # type: (str) -> None +def unregister_role(name: str) -> None: """Unregister a role from docutils.""" roles._roles.pop(name, None) # type: ignore -def is_node_registered(node): - # type: (Type[nodes.Element]) -> bool +def is_node_registered(node: Type[Element]) -> bool: """Check the *node* is already registered.""" return hasattr(nodes.GenericNodeVisitor, 'visit_' + node.__name__) -def register_node(node): - # type: (Type[nodes.Element]) -> None +def register_node(node: Type[Element]) -> None: """Register a node to docutils. This modifies global state of some visitors. So it is better to use this @@ -123,8 +115,7 @@ def register_node(node): additional_nodes.add(node) -def unregister_node(node): - # type: (Type[nodes.Element]) -> None +def unregister_node(node: Type[Element]) -> None: """Unregister a node from docutils. This is inverse of ``nodes._add_nodes_class_names()``. @@ -137,8 +128,7 @@ def unregister_node(node): @contextmanager -def patched_get_language(): - # type: () -> Generator[None, None, None] +def patched_get_language() -> Generator[None, None, None]: """Patch docutils.languages.get_language() temporarily. This ignores the second argument ``reporter`` to suppress warnings. @@ -146,8 +136,7 @@ def patched_get_language(): """ from docutils.languages import get_language - def patched_get_language(language_code, reporter=None): - # type: (str, Reporter) -> Any + def patched_get_language(language_code: str, reporter: Reporter = None) -> Any: return get_language(language_code) try: @@ -159,8 +148,7 @@ def patched_get_language(): @contextmanager -def using_user_docutils_conf(confdir): - # type: (str) -> Generator[None, None, None] +def using_user_docutils_conf(confdir: str) -> Generator[None, None, None]: """Let docutils know the location of ``docutils.conf`` for Sphinx.""" try: docutilsconfig = os.environ.get('DOCUTILSCONFIG', None) @@ -176,8 +164,7 @@ def using_user_docutils_conf(confdir): @contextmanager -def patch_docutils(confdir=None): - # type: (str) -> Generator[None, None, None] +def patch_docutils(confdir: str = None) -> Generator[None, None, None]: """Patch to docutils temporarily.""" with patched_get_language(), using_user_docutils_conf(confdir): yield @@ -191,35 +178,30 @@ class sphinx_domains: """Monkey-patch directive and role dispatch, so that domain-specific markup takes precedence. """ - def __init__(self, env): - # type: (BuildEnvironment) -> None + def __init__(self, env: "BuildEnvironment") -> None: self.env = env self.directive_func = None # type: Callable self.roles_func = None # type: Callable - def __enter__(self): - # type: () -> None + def __enter__(self) -> None: self.enable() - def __exit__(self, type, value, traceback): - # type: (str, str, str) -> None + def __exit__(self, exc_type: Type[Exception], exc_value: Exception, traceback: Any) -> bool: # NOQA self.disable() + return True - def enable(self): - # type: () -> None + def enable(self) -> None: self.directive_func = directives.directive self.role_func = roles.role directives.directive = self.lookup_directive # type: ignore roles.role = self.lookup_role # type: ignore - def disable(self): - # type: () -> None + def disable(self) -> None: directives.directive = self.directive_func roles.role = self.role_func - def lookup_domain_element(self, type, name): - # type: (str, str) -> Any + def lookup_domain_element(self, type: str, name: str) -> Any: """Lookup a markup element (directive or role), given its name which can be a full name (with domain). """ @@ -247,15 +229,13 @@ class sphinx_domains: raise ElementLookupError - def lookup_directive(self, name, lang_module, document): - # type: (str, ModuleType, nodes.document) -> Tuple[Type[Directive], List[nodes.system_message]] # NOQA + def lookup_directive(self, name: str, lang_module: ModuleType, document: nodes.document) -> Tuple[Type[Directive], List[system_message]]: # NOQA try: return self.lookup_domain_element('directive', name) except ElementLookupError: return self.directive_func(name, lang_module, document) - def lookup_role(self, name, lang_module, lineno, reporter): - # type: (str, ModuleType, int, Reporter) -> Tuple[RoleFunction, List[nodes.system_message]] # NOQA + def lookup_role(self, name: str, lang_module: ModuleType, lineno: int, reporter: Reporter) -> Tuple[RoleFunction, List[system_message]]: # NOQA try: return self.lookup_domain_element('role', name) except ElementLookupError: @@ -263,8 +243,7 @@ class sphinx_domains: class WarningStream: - def write(self, text): - # type: (str) -> None + def write(self, text: str) -> None: matched = report_re.search(text) if not matched: logger.warning(text.rstrip("\r\n")) @@ -276,16 +255,14 @@ class WarningStream: class LoggingReporter(Reporter): @classmethod - def from_reporter(cls, reporter): - # type: (Reporter) -> LoggingReporter + def from_reporter(cls, reporter: Reporter) -> "LoggingReporter": """Create an instance of LoggingReporter from other reporter object.""" return cls(reporter.source, reporter.report_level, reporter.halt_level, reporter.debug_flag, reporter.error_handler) - def __init__(self, source, report_level=Reporter.WARNING_LEVEL, - halt_level=Reporter.SEVERE_LEVEL, debug=False, - error_handler='backslashreplace'): - # type: (str, int, int, bool, str) -> None + def __init__(self, source: str, report_level: int = Reporter.WARNING_LEVEL, + halt_level: int = Reporter.SEVERE_LEVEL, debug: bool = False, + error_handler: str = 'backslashreplace') -> None: stream = cast(IO, WarningStream()) super().__init__(source, report_level, halt_level, stream, debug, error_handler=error_handler) @@ -294,18 +271,15 @@ class LoggingReporter(Reporter): class NullReporter(Reporter): """A dummy reporter; write nothing.""" - def __init__(self): - # type: () -> None + def __init__(self) -> None: super().__init__('', 999, 4) -def is_html5_writer_available(): - # type: () -> bool +def is_html5_writer_available() -> bool: 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: Any, has_content: bool = None, argument_spec: Tuple[int, int, bool] = None, **option_spec) -> Any: # NOQA warnings.warn('function based directive support is now deprecated. ' 'Use class based directive instead.', RemovedInSphinx30Warning) @@ -323,8 +297,7 @@ def directive_helper(obj, has_content=None, argument_spec=None, **option_spec): @contextmanager -def switch_source_input(state, content): - # type: (State, StringList) -> Generator[None, None, None] +def switch_source_input(state: State, content: StringList) -> Generator[None, None, None]: """Switch current source input of state temporarily.""" try: # remember the original ``get_source_and_line()`` method @@ -344,13 +317,11 @@ def switch_source_input(state, content): class SphinxFileOutput(FileOutput): """Better FileOutput class for Sphinx.""" - def __init__(self, **kwargs): - # type: (Any) -> None + def __init__(self, **kwargs) -> None: self.overwrite_if_changed = kwargs.pop('overwrite_if_changed', False) super().__init__(**kwargs) - def write(self, data): - # type: (str) -> str + def write(self, data: str) -> str: if (self.destination_path and self.autoclose and 'b' not in self.mode and self.overwrite_if_changed and os.path.exists(self.destination_path)): with open(self.destination_path, encoding=self.encoding) as f: @@ -371,19 +342,16 @@ class SphinxDirective(Directive): """ @property - def env(self): - # type: () -> BuildEnvironment + def env(self) -> "BuildEnvironment": """Reference to the :class:`.BuildEnvironment` object.""" return self.state.document.settings.env @property - def config(self): - # type: () -> Config + def config(self) -> "Config": """Reference to the :class:`.Config` object.""" return self.env.config - def set_source_info(self, node): - # type: (nodes.Node) -> None + def set_source_info(self, node: Node) -> None: """Set source and line number to the node.""" node.source, node.line = self.state_machine.get_source_and_line(self.lineno) @@ -406,8 +374,9 @@ class SphinxRole: content = None #: A list of strings, the directive content for customization #: (from the "role" directive). - def __call__(self, name, rawtext, text, lineno, inliner, options={}, content=[]): - # type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA + def __call__(self, name: str, rawtext: str, text: str, lineno: int, + inliner: Inliner, options: Dict = {}, content: List[str] = [] + ) -> Tuple[List[Node], List[system_message]]: self.rawtext = rawtext self.text = unescape(text) self.lineno = lineno @@ -427,24 +396,20 @@ class SphinxRole: return self.run() - def run(self): - # type: () -> Tuple[List[nodes.Node], List[nodes.system_message]] + def run(self) -> Tuple[List[Node], List[system_message]]: raise NotImplementedError @property - def env(self): - # type: () -> BuildEnvironment + def env(self) -> "BuildEnvironment": """Reference to the :class:`.BuildEnvironment` object.""" return self.inliner.document.settings.env @property - def config(self): - # type: () -> Config + def config(self) -> "Config": """Reference to the :class:`.Config` object.""" return self.env.config - def set_source_info(self, node, lineno=None): - # type: (nodes.Node, int) -> None + def set_source_info(self, node: Node, lineno: int = None) -> None: if lineno is None: lineno = self.lineno @@ -466,8 +431,9 @@ class ReferenceRole(SphinxRole): # \x00 means the "<" was backslash-escaped explicit_title_re = re.compile(r'^(.+?)\s*(?$', re.DOTALL) - def __call__(self, name, rawtext, text, lineno, inliner, options={}, content=[]): - # type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA + def __call__(self, name: str, rawtext: str, text: str, lineno: int, + inliner: Inliner, options: Dict = {}, content: List[str] = [] + ) -> Tuple[List[Node], List[system_message]]: matched = self.explicit_title_re.match(text) if matched: self.has_explicit_title = True @@ -490,8 +456,7 @@ class SphinxTranslator(nodes.NodeVisitor): This class is strongly coupled with Sphinx. """ - def __init__(self, document, builder): - # type: (nodes.document, Builder) -> None + def __init__(self, document: nodes.document, builder: "Builder") -> None: super().__init__(document) self.builder = builder self.config = builder.config @@ -503,8 +468,7 @@ class SphinxTranslator(nodes.NodeVisitor): __document_cache__ = None # type: nodes.document -def new_document(source_path, settings=None): - # type: (str, Any) -> nodes.document +def new_document(source_path: str, settings: Any = None) -> nodes.document: """Return a new empty document object. This is an alternative of docutils'. This is a simple wrapper for ``docutils.utils.new_document()``. It From 2900179436580a53fcf0a9b2b15e31ef8e259979 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 2 Jun 2019 20:29:48 +0900 Subject: [PATCH 18/84] Migrate to py3 style type annotation: sphinx.util.docfields --- sphinx/util/docfields.py | 99 +++++++++++++--------------------------- 1 file changed, 32 insertions(+), 67 deletions(-) diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py index 9b19d229d..f3729c0c9 100644 --- a/sphinx/util/docfields.py +++ b/sphinx/util/docfields.py @@ -10,23 +10,23 @@ """ import warnings -from typing import List, Tuple, cast +from typing import Any, Dict, List, Tuple, Type, Union +from typing import cast from docutils import nodes +from docutils.nodes import Node from sphinx import addnodes from sphinx.deprecation import RemovedInSphinx40Warning +from sphinx.util.typing import TextlikeNode if False: # For type annotation - from typing import Any, Dict, Type, Union # NOQA - from sphinx.directive import ObjectDescription # NOQA - from sphinx.environment import BuildEnvironment # NOQA - from sphinx.util.typing import TextlikeNode # NOQA + from sphinx.environment import BuildEnvironment + from sphinx.directive import ObjectDescription -def _is_single_paragraph(node): - # type: (nodes.field_body) -> bool +def _is_single_paragraph(node: nodes.field_body) -> bool: """True if the node only contains one paragraph (and system messages).""" if len(node) == 0: return False @@ -55,9 +55,8 @@ class Field: is_grouped = False is_typed = False - def __init__(self, name, names=(), label=None, has_arg=True, rolename=None, - bodyrolename=None): - # type: (str, Tuple[str, ...], str, bool, str, str) -> None + def __init__(self, name: str, names: Tuple[str, ...] = (), label: str = None, + has_arg: bool = True, rolename: str = None, bodyrolename: str = None) -> None: self.name = name self.names = names self.label = label @@ -65,15 +64,9 @@ class Field: self.rolename = rolename self.bodyrolename = bodyrolename - def make_xref(self, - rolename, # type: str - domain, # type: str - target, # type: str - innernode=addnodes.literal_emphasis, # type: Type[TextlikeNode] - contnode=None, # type: nodes.Node - env=None, # type: BuildEnvironment - ): - # type: (...) -> nodes.Node + def make_xref(self, rolename: str, domain: str, target: str, + innernode: Type[TextlikeNode] = addnodes.literal_emphasis, + contnode: Node = None, env: "BuildEnvironment" = None) -> Node: if not rolename: return contnode or innernode(target, target) refnode = addnodes.pending_xref('', refdomain=domain, refexplicit=False, @@ -83,28 +76,16 @@ class Field: env.get_domain(domain).process_field_xref(refnode) return refnode - def make_xrefs(self, - rolename, # type: str - domain, # type: str - target, # type: str - innernode=addnodes.literal_emphasis, # type: Type[TextlikeNode] - contnode=None, # type: nodes.Node - env=None, # type: BuildEnvironment - ): - # type: (...) -> List[nodes.Node] + def make_xrefs(self, rolename: str, domain: str, target: str, + innernode: Type[TextlikeNode] = addnodes.literal_emphasis, + contnode: Node = None, env: "BuildEnvironment" = None) -> List[Node]: return [self.make_xref(rolename, domain, target, innernode, contnode, env)] - def make_entry(self, fieldarg, content): - # type: (str, List[nodes.Node]) -> Tuple[str, List[nodes.Node]] + def make_entry(self, fieldarg: str, content: List[Node]) -> Tuple[str, List[Node]]: return (fieldarg, content) - def make_field(self, - types, # type: Dict[str, List[nodes.Node]] - domain, # type: str - item, # type: Tuple - env=None, # type: BuildEnvironment - ): - # type: (...) -> nodes.field + def make_field(self, types: Dict[str, List[Node]], domain: str, + item: Tuple, env: "BuildEnvironment" = None) -> nodes.field: fieldarg, content = item fieldname = nodes.field_name('', self.label) if fieldarg: @@ -138,19 +119,13 @@ class GroupedField(Field): is_grouped = True list_type = nodes.bullet_list - def __init__(self, name, names=(), label=None, rolename=None, - can_collapse=False): - # type: (str, Tuple[str, ...], str, str, bool) -> None + def __init__(self, name: str, names: Tuple[str, ...] = (), label: str = None, + rolename: str = None, can_collapse: bool = False) -> None: super().__init__(name, names, label, True, rolename) self.can_collapse = can_collapse - def make_field(self, - types, # type: Dict[str, List[nodes.Node]] - domain, # type: str - items, # type: Tuple - env=None, # type: BuildEnvironment - ): - # type: (...) -> nodes.field + def make_field(self, types: Dict[str, List[Node]], domain: str, + items: Tuple, env: "BuildEnvironment" = None) -> nodes.field: fieldname = nodes.field_name('', self.label) listnode = self.list_type() for fieldarg, content in items: @@ -191,22 +166,16 @@ class TypedField(GroupedField): """ is_typed = True - def __init__(self, name, names=(), typenames=(), label=None, - rolename=None, typerolename=None, can_collapse=False): - # type: (str, Tuple[str, ...], Tuple[str, ...], str, str, str, bool) -> None + def __init__(self, name: str, names: Tuple[str, ...] = (), typenames: Tuple[str, ...] = (), + label: str = None, rolename: str = None, typerolename: str = None, + can_collapse: bool = False) -> None: super().__init__(name, names, label, rolename, can_collapse) self.typenames = typenames self.typerolename = typerolename - def make_field(self, - types, # type: Dict[str, List[nodes.Node]] - domain, # type: str - items, # type: Tuple - env=None, # type: BuildEnvironment - ): - # type: (...) -> nodes.field - def handle_item(fieldarg, content): - # type: (str, str) -> nodes.paragraph + def make_field(self, types: Dict[str, List[Node]], domain: str, + items: Tuple, env: "BuildEnvironment" = None) -> nodes.field: + def handle_item(fieldarg: str, content: str) -> nodes.paragraph: par = nodes.paragraph() par.extend(self.make_xrefs(self.rolename, domain, fieldarg, addnodes.literal_strong, env=env)) @@ -246,13 +215,11 @@ class DocFieldTransformer: """ typemap = None # type: Dict[str, Tuple[Field, bool]] - def __init__(self, directive): - # type: (ObjectDescription) -> None + def __init__(self, directive: "ObjectDescription") -> None: self.directive = directive self.typemap = directive.get_field_type_map() - def preprocess_fieldtypes(self, types): - # type: (List[Field]) -> Dict[str, Tuple[Field, bool]] + def preprocess_fieldtypes(self, types: List[Field]) -> Dict[str, Tuple[Field, bool]]: warnings.warn('DocFieldTransformer.preprocess_fieldtypes() is deprecated.', RemovedInSphinx40Warning) typemap = {} @@ -265,16 +232,14 @@ class DocFieldTransformer: typemap[name] = typed_field, True return typemap - def transform_all(self, node): - # type: (addnodes.desc_content) -> None + def transform_all(self, node: addnodes.desc_content) -> None: """Transform all field list children of a node.""" # don't traverse, only handle field lists that are immediate children for child in node: if isinstance(child, nodes.field_list): self.transform(child) - def transform(self, node): - # type: (nodes.field_list) -> None + def transform(self, node: nodes.field_list) -> None: """Transform a single field list *node*.""" typemap = self.typemap From b5276b3965388e7b7264a42d1a442b8c734b1877 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 3 Jun 2019 22:06:11 +0900 Subject: [PATCH 19/84] Migrate to py3 style type annotation: sphinx.util.matching --- sphinx/util/matching.py | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/sphinx/util/matching.py b/sphinx/util/matching.py index b1725cb46..97eeff40f 100644 --- a/sphinx/util/matching.py +++ b/sphinx/util/matching.py @@ -9,16 +9,12 @@ """ import re +from typing import Callable, Dict, List, Match, Pattern from sphinx.util.osutil import canon_path -if False: - # For type annotation - from typing import Callable, Dict, List, Match, Pattern # NOQA - -def _translate_pattern(pat): - # type: (str) -> str +def _translate_pattern(pat: str) -> str: """Translate a shell-style glob pattern to a regular expression. Adapted from the fnmatch module, but enhanced so that single stars don't @@ -64,8 +60,7 @@ def _translate_pattern(pat): return res + '$' -def compile_matchers(patterns): - # type: (List[str]) -> List[Callable[[str], Match[str]]] +def compile_matchers(patterns: List[str]) -> List[Callable[[str], Match[str]]]: return [re.compile(_translate_pattern(pat)).match for pat in patterns] @@ -76,17 +71,14 @@ class Matcher: For example, "**/index.rst" matches with "index.rst" """ - def __init__(self, patterns): - # type: (List[str]) -> None + def __init__(self, patterns: List[str]) -> None: expanded = [pat[3:] for pat in patterns if pat.startswith('**/')] self.patterns = compile_matchers(patterns + expanded) - def __call__(self, string): - # type: (str) -> bool + def __call__(self, string: str) -> bool: return self.match(string) - def match(self, string): - # type: (str) -> bool + def match(self, string: str) -> bool: string = canon_path(string) return any(pat(string) for pat in self.patterns) @@ -97,16 +89,14 @@ DOTFILES = Matcher(['**/.*']) _pat_cache = {} # type: Dict[str, Pattern] -def patmatch(name, pat): - # type: (str, str) -> Match[str] +def patmatch(name: str, pat: str) -> Match[str]: """Return if name matches pat. Adapted from fnmatch module.""" if pat not in _pat_cache: _pat_cache[pat] = re.compile(_translate_pattern(pat)) return _pat_cache[pat].match(name) -def patfilter(names, pat): - # type: (List[str], str) -> List[str] +def patfilter(names: List[str], pat: str) -> List[str]: """Return the subset of the list NAMES that match PAT. Adapted from fnmatch module. From 5115fb0172c3ae42f326ed7c50e2aa10917dfb94 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 3 Jun 2019 22:06:44 +0900 Subject: [PATCH 20/84] Migrate to py3 style type annotation: sphinx.util.png --- sphinx/util/png.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sphinx/util/png.py b/sphinx/util/png.py index 2fb97a901..911547db6 100644 --- a/sphinx/util/png.py +++ b/sphinx/util/png.py @@ -20,8 +20,7 @@ DEPTH_CHUNK_START = b'tEXtDepth\x00' IEND_CHUNK = b'\x00\x00\x00\x00IEND\xAE\x42\x60\x82' -def read_png_depth(filename): - # type: (str) -> int +def read_png_depth(filename: str) -> int: """Read the special tEXt chunk indicating the depth from a PNG file.""" with open(filename, 'rb') as f: f.seek(- (LEN_IEND + LEN_DEPTH), 2) @@ -33,8 +32,7 @@ def read_png_depth(filename): return struct.unpack('!i', depthchunk[14:18])[0] -def write_png_depth(filename, depth): - # type: (str, int) -> None +def write_png_depth(filename: str, depth: int) -> None: """Write the special tEXt chunk indicating the depth to a PNG file. The chunk is placed immediately before the special IEND chunk. From 9ba216223a3b7d6d0888e4588709d3923557f096 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 3 Jun 2019 22:07:07 +0900 Subject: [PATCH 21/84] Migrate to py3 style type annotation: sphinx.util.pycompat --- sphinx/util/pycompat.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index dca2849c2..06d3bcc2c 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -13,6 +13,7 @@ import io import sys import textwrap import warnings +from typing import Any, Callable from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias from sphinx.locale import __ @@ -20,10 +21,6 @@ from sphinx.util import logging from sphinx.util.console import terminal_safe from sphinx.util.typing import NoneType -if False: - # For type annotation - from typing import Any, Callable # NOQA - logger = logging.getLogger(__name__) @@ -33,8 +30,7 @@ logger = logging.getLogger(__name__) # convert_with_2to3(): # support for running 2to3 over config files -def convert_with_2to3(filepath): - # type: (str) -> str +def convert_with_2to3(filepath: str) -> str: from lib2to3.refactor import RefactoringTool, get_fixers_from_package from lib2to3.pgen2.parse import ParseError fixers = get_fixers_from_package('lib2to3.fixes') @@ -62,8 +58,7 @@ class UnicodeMixin: return self.__unicode__() -def execfile_(filepath, _globals, open=open): - # type: (str, Any, Callable) -> None +def execfile_(filepath: str, _globals: Any, open: Callable = open) -> None: from sphinx.util.osutil import fs_encoding with open(filepath, 'rb') as f: source = f.read() From e731d746fd6e38d9be9ab84d01acaf2890a6d07a Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 2 Jun 2019 17:14:11 +0900 Subject: [PATCH 22/84] Fix #5502: linkcheck: Consider HTTP 503 response as not an error --- CHANGES | 1 + sphinx/builders/linkcheck.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index d96a87788..7998d18a0 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,7 @@ Bugs fixed * py domain: duplicated warning does not point the location of source code * #1125: html theme: scrollbar is hard to see on classic theme and macOS +* #5502: linkcheck: Consider HTTP 503 response as not an error Testing -------- diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index 164f1e6b7..8b7c7ba9a 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -159,6 +159,9 @@ class CheckExternalLinksBuilder(Builder): if err.response.status_code == 401: # We'll take "Unauthorized" as working. return 'working', ' - unauthorized', 0 + elif err.response.status_code == 503: + # We'll take "Service Unavailable" as ignored. + return 'ignored', str(err), 0 else: return 'broken', str(err), 0 except Exception as err: From 42e9fd896ee36e928888dffcc34d725c7dcceb47 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 3 Jun 2019 22:34:33 +0900 Subject: [PATCH 23/84] test: Move class inheritance samples into target.inheritance module --- .../roots/test-ext-autodoc/target/__init__.py | 21 +--------- .../test-ext-autodoc/target/inheritance.py | 19 +++++++++ tests/test_autodoc.py | 41 +++++++++---------- 3 files changed, 40 insertions(+), 41 deletions(-) create mode 100644 tests/roots/test-ext-autodoc/target/inheritance.py diff --git a/tests/roots/test-ext-autodoc/target/__init__.py b/tests/roots/test-ext-autodoc/target/__init__.py index 771d45c4c..f6970a36c 100644 --- a/tests/roots/test-ext-autodoc/target/__init__.py +++ b/tests/roots/test-ext-autodoc/target/__init__.py @@ -34,26 +34,7 @@ def _funky_classmethod(name, b, c, d, docstring=None): return classmethod(function) -class Base(object): - def inheritedmeth(self): - """Inherited function.""" - - @classmethod - def inheritedclassmeth(cls): - """Inherited class method.""" - - @staticmethod - def inheritedstaticmeth(cls): - """Inherited static method.""" - - -class Derived(Base): - def inheritedmeth(self): - # no docstring here - pass - - -class Class(Base): +class Class(object): """Class to document.""" def meth(self): diff --git a/tests/roots/test-ext-autodoc/target/inheritance.py b/tests/roots/test-ext-autodoc/target/inheritance.py new file mode 100644 index 000000000..3a5fc0711 --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/inheritance.py @@ -0,0 +1,19 @@ +class Base(object): + def inheritedmeth(self): + """Inherited function.""" + + @classmethod + def inheritedclassmeth(cls): + """Inherited class method.""" + + @staticmethod + def inheritedstaticmeth(cls): + """Inherited static method.""" + + +class Derived(Base): + def inheritedmeth(self): + # no docstring here + pass + + diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 877c8a204..31c523865 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -454,7 +454,7 @@ def test_get_doc(): assert getdocl('method', J.foo) == ['Method docstring'] assert getdocl('function', J().foo) == ['Method docstring'] - from target import Base, Derived + from target.inheritance import Base, Derived # NOTE: inspect.getdoc seems not to work with locally defined classes directive.env.config.autodoc_inherit_docstrings = False @@ -498,6 +498,7 @@ def test_new_documenter(app): @pytest.mark.usefixtures('setup_test') def test_attrgetter_using(): from target import Class + from target.inheritance import Derived def assert_getter_works(objtype, name, obj, attrs=[], **kw): getattr_spy = [] @@ -527,7 +528,7 @@ def test_attrgetter_using(): assert_getter_works('class', 'target.Class', Class, ['meth']) options.inherited_members = True - assert_getter_works('class', 'target.Class', Class, ['meth', 'inheritedmeth']) + assert_getter_works('class', 'target.inheritance.Derived', Derived, ['inheritedmeth']) @pytest.mark.sphinx('html', testroot='ext-autodoc') @@ -630,14 +631,14 @@ def test_autodoc_attributes(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_members(app): # default (no-members) - actual = do_autodoc(app, 'class', 'target.Base') + actual = do_autodoc(app, 'class', 'target.inheritance.Base') assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Base', ] # default ALL-members options = {"members": None} - actual = do_autodoc(app, 'class', 'target.Base', options) + actual = do_autodoc(app, 'class', 'target.inheritance.Base', options) assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Base', ' .. py:method:: Base.inheritedclassmeth()', @@ -647,7 +648,7 @@ def test_autodoc_members(app): # default specific-members options = {"members": "inheritedmeth,inheritedstaticmeth"} - actual = do_autodoc(app, 'class', 'target.Base', options) + actual = do_autodoc(app, 'class', 'target.inheritance.Base', options) assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Base', ' .. py:method:: Base.inheritedmeth()', @@ -659,7 +660,7 @@ def test_autodoc_members(app): def test_autodoc_exclude_members(app): options = {"members": None, "exclude-members": "inheritedmeth,inheritedstaticmeth"} - actual = do_autodoc(app, 'class', 'target.Base', options) + actual = do_autodoc(app, 'class', 'target.inheritance.Base', options) assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Base', ' .. py:method:: Base.inheritedclassmeth()' @@ -668,7 +669,7 @@ def test_autodoc_exclude_members(app): # members vs exclude-members options = {"members": "inheritedmeth", "exclude-members": "inheritedmeth"} - actual = do_autodoc(app, 'class', 'target.Base', options) + actual = do_autodoc(app, 'class', 'target.inheritance.Base', options) assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Base', ] @@ -702,15 +703,11 @@ def test_autodoc_undoc_members(app): def test_autodoc_inherited_members(app): options = {"members": None, "inherited-members": None} - actual = do_autodoc(app, 'class', 'target.Class', options) + actual = do_autodoc(app, 'class', 'target.inheritance.Derived', options) assert list(filter(lambda l: 'method::' in l, actual)) == [ - ' .. py:method:: Class.excludemeth()', - ' .. py:method:: Class.inheritedclassmeth()', - ' .. py:method:: Class.inheritedmeth()', - ' .. py:method:: Class.inheritedstaticmeth(cls)', - ' .. py:method:: Class.meth()', - ' .. py:method:: Class.moore(a, e, f) -> happiness', - ' .. py:method:: Class.skipmeth()' + ' .. py:method:: Derived.inheritedclassmeth()', + ' .. py:method:: Derived.inheritedmeth()', + ' .. py:method:: Derived.inheritedstaticmeth(cls)', ] @@ -755,10 +752,12 @@ def test_autodoc_special_members(app): actual = do_autodoc(app, 'class', 'target.Class', options) assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Class(arg)', + ' .. py:attribute:: Class.__dict__', ' .. py:method:: Class.__init__(arg)', ' .. py:attribute:: Class.__module__', ' .. py:method:: Class.__special1__()', ' .. py:method:: Class.__special2__()', + ' .. py:attribute:: Class.__weakref__', ' .. py:attribute:: Class.attr', ' .. py:attribute:: Class.docattr', ' .. py:method:: Class.excludemeth()', @@ -812,12 +811,12 @@ def test_autodoc_noindex(app): # TODO: :noindex: should be propagated to children of target item. - actual = do_autodoc(app, 'class', 'target.Base', options) + actual = do_autodoc(app, 'class', 'target.inheritance.Base', options) assert list(actual) == [ '', '.. py:class:: Base', ' :noindex:', - ' :module: target', + ' :module: target.inheritance', '' ] @@ -885,11 +884,11 @@ def test_autodoc_inner_class(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_classmethod(app): - actual = do_autodoc(app, 'method', 'target.Base.inheritedclassmeth') + actual = do_autodoc(app, 'method', 'target.inheritance.Base.inheritedclassmeth') assert list(actual) == [ '', '.. py:method:: Base.inheritedclassmeth()', - ' :module: target', + ' :module: target.inheritance', ' :classmethod:', '', ' Inherited class method.', @@ -899,11 +898,11 @@ def test_autodoc_classmethod(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_staticmethod(app): - actual = do_autodoc(app, 'method', 'target.Base.inheritedstaticmeth') + actual = do_autodoc(app, 'method', 'target.inheritance.Base.inheritedstaticmeth') assert list(actual) == [ '', '.. py:method:: Base.inheritedstaticmeth(cls)', - ' :module: target', + ' :module: target.inheritance', ' :staticmethod:', '', ' Inherited static method.', From f4114bb7941df27700db293b0625be7940cdacbb Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 3 Jun 2019 22:34:34 +0900 Subject: [PATCH 24/84] test: Update testcase for autodoc_inherit_docstrings --- tests/test_autodoc.py | 9 --------- tests/test_ext_autodoc_configs.py | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 31c523865..3b4d60093 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -454,15 +454,6 @@ def test_get_doc(): assert getdocl('method', J.foo) == ['Method docstring'] assert getdocl('function', J().foo) == ['Method docstring'] - from target.inheritance import Base, Derived - - # NOTE: inspect.getdoc seems not to work with locally defined classes - directive.env.config.autodoc_inherit_docstrings = False - assert getdocl('method', Base.inheritedmeth) == ['Inherited function.'] - assert getdocl('method', Derived.inheritedmeth) == [] - directive.env.config.autodoc_inherit_docstrings = True - assert getdocl('method', Derived.inheritedmeth) == ['Inherited function.'] - @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_new_documenter(app): diff --git a/tests/test_ext_autodoc_configs.py b/tests/test_ext_autodoc_configs.py index cc8f9dbc1..7135db626 100644 --- a/tests/test_ext_autodoc_configs.py +++ b/tests/test_ext_autodoc_configs.py @@ -18,6 +18,30 @@ from test_autodoc import do_autodoc IS_PYPY = platform.python_implementation() == 'PyPy' +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_inherit_docstrings(app): + assert app.config.autodoc_inherit_docstrings is True # default + actual = do_autodoc(app, 'method', 'target.inheritance.Derived.inheritedmeth') + assert list(actual) == [ + '', + '.. py:method:: Derived.inheritedmeth()', + ' :module: target.inheritance', + '', + ' Inherited function.', + ' ' + ] + + # disable autodoc_inherit_docstrings + app.config.autodoc_inherit_docstrings = False + actual = do_autodoc(app, 'method', 'target.inheritance.Derived.inheritedmeth') + assert list(actual) == [ + '', + '.. py:method:: Derived.inheritedmeth()', + ' :module: target.inheritance', + '' + ] + + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_docstring_signature(app): options = {"members": None} From f7851b62e0cb3c8284aa134af663469c0afaf480 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 3 Jun 2019 22:34:36 +0900 Subject: [PATCH 25/84] test: Update testcase for autoclass_content --- .../target/autoclass_content.py | 35 ++++ tests/test_autodoc.py | 88 ---------- tests/test_ext_autodoc_configs.py | 150 ++++++++++++++++++ 3 files changed, 185 insertions(+), 88 deletions(-) create mode 100644 tests/roots/test-ext-autodoc/target/autoclass_content.py diff --git a/tests/roots/test-ext-autodoc/target/autoclass_content.py b/tests/roots/test-ext-autodoc/target/autoclass_content.py new file mode 100644 index 000000000..8924ab09b --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/autoclass_content.py @@ -0,0 +1,35 @@ +class A: + """A class having no __init__, no __new__""" + + +class B: + """A class having __init__(no docstring), no __new__""" + def __init__(self): + pass + + +class C: + """A class having __init__, no __new__""" + def __init__(self): + """__init__ docstring""" + + +class D: + """A class having no __init__, __new__(no docstring)""" + def __new__(cls): + pass + + +class E: + """A class having no __init__, __new__""" + def __new__(cls): + """__new__ docstring""" + + +class F: + """A class having both __init__ and __new__""" + def __init__(self): + """__init__ docstring""" + + def __new__(cls): + """__new__ docstring""" diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 3b4d60093..07ffb2903 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -325,34 +325,6 @@ def test_get_doc(): """Döcstring""" assert getdocl('function', f) == ['Döcstring'] - # class docstring: depends on config value which one is taken - class C: - """Class docstring""" - def __init__(self): - """Init docstring""" - - def __new__(cls): - """New docstring""" - directive.env.config.autoclass_content = 'class' - assert getdocl('class', C) == ['Class docstring'] - directive.env.config.autoclass_content = 'init' - assert getdocl('class', C) == ['Init docstring'] - directive.env.config.autoclass_content = 'both' - assert getdocl('class', C) == ['Class docstring', '', 'Init docstring'] - - class D: - """Class docstring""" - def __init__(self): - """Init docstring - - Other - lines - """ - - # Indentation is normalized for 'both' - assert getdocl('class', D) == ['Class docstring', '', 'Init docstring', - '', 'Other', ' lines'] - # __init__ have signature at first line of docstring class E: """Class docstring""" @@ -386,66 +358,6 @@ def test_get_doc(): directive.env.config.autoclass_content = 'both' assert getdocl('class', E) == ['Class docstring', '', 'Init docstring'] - # class does not have __init__ method - class F: - """Class docstring""" - - # docstring in the __init__ method of base class will be discard - for f in (False, True): - directive.env.config.autodoc_docstring_signature = f - directive.env.config.autoclass_content = 'class' - assert getdocl('class', F) == ['Class docstring'] - directive.env.config.autoclass_content = 'init' - assert getdocl('class', F) == ['Class docstring'] - directive.env.config.autoclass_content = 'both' - assert getdocl('class', F) == ['Class docstring'] - - # class has __init__ method with no docstring - class G: - """Class docstring""" - def __init__(self): - pass - - # docstring in the __init__ method of base class will not be used - for f in (False, True): - directive.env.config.autodoc_docstring_signature = f - directive.env.config.autoclass_content = 'class' - assert getdocl('class', G) == ['Class docstring'] - directive.env.config.autoclass_content = 'init' - assert getdocl('class', G) == ['Class docstring'] - directive.env.config.autoclass_content = 'both' - assert getdocl('class', G) == ['Class docstring'] - - # class has __new__ method with docstring - # class docstring: depends on config value which one is taken - class H: - """Class docstring""" - def __init__(self): - pass - - def __new__(cls): - """New docstring""" - directive.env.config.autoclass_content = 'class' - assert getdocl('class', H) == ['Class docstring'] - directive.env.config.autoclass_content = 'init' - assert getdocl('class', H) == ['New docstring'] - directive.env.config.autoclass_content = 'both' - assert getdocl('class', H) == ['Class docstring', '', 'New docstring'] - - # class has __init__ method without docstring and - # __new__ method with docstring - # class docstring: depends on config value which one is taken - class I: # NOQA - """Class docstring""" - def __new__(cls): - """New docstring""" - directive.env.config.autoclass_content = 'class' - assert getdocl('class', I) == ['Class docstring'] - directive.env.config.autoclass_content = 'init' - assert getdocl('class', I) == ['New docstring'] - directive.env.config.autoclass_content = 'both' - assert getdocl('class', I) == ['Class docstring', '', 'New docstring'] - # verify that method docstrings get extracted in both normal case # and in case of bound method posing as a function class J: # NOQA diff --git a/tests/test_ext_autodoc_configs.py b/tests/test_ext_autodoc_configs.py index 7135db626..a53af11fc 100644 --- a/tests/test_ext_autodoc_configs.py +++ b/tests/test_ext_autodoc_configs.py @@ -18,6 +18,156 @@ from test_autodoc import do_autodoc IS_PYPY = platform.python_implementation() == 'PyPy' +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autoclass_content_class(app): + app.config.autoclass_content = 'class' + options = {"members": None} + actual = do_autodoc(app, 'module', 'target.autoclass_content', options) + assert list(actual) == [ + '', + '.. py:module:: target.autoclass_content', + '', + '', + '.. py:class:: A', + ' :module: target.autoclass_content', + '', + ' A class having no __init__, no __new__', + ' ', + '', + '.. py:class:: B()', + ' :module: target.autoclass_content', + '', + ' A class having __init__(no docstring), no __new__', + ' ', + '', + '.. py:class:: C()', + ' :module: target.autoclass_content', + '', + ' A class having __init__, no __new__', + ' ', + '', + '.. py:class:: D', + ' :module: target.autoclass_content', + '', + ' A class having no __init__, __new__(no docstring)', + ' ', + '', + '.. py:class:: E', + ' :module: target.autoclass_content', + '', + ' A class having no __init__, __new__', + ' ', + '', + '.. py:class:: F()', + ' :module: target.autoclass_content', + '', + ' A class having both __init__ and __new__', + ' ' + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autoclass_content_init(app): + app.config.autoclass_content = 'init' + options = {"members": None} + actual = do_autodoc(app, 'module', 'target.autoclass_content', options) + assert list(actual) == [ + '', + '.. py:module:: target.autoclass_content', + '', + '', + '.. py:class:: A', + ' :module: target.autoclass_content', + '', + ' A class having no __init__, no __new__', + ' ', + '', + '.. py:class:: B()', + ' :module: target.autoclass_content', + '', + ' A class having __init__(no docstring), no __new__', + ' ', + '', + '.. py:class:: C()', + ' :module: target.autoclass_content', + '', + ' __init__ docstring', + ' ', + '', + '.. py:class:: D', + ' :module: target.autoclass_content', + '', + ' A class having no __init__, __new__(no docstring)', + ' ', + '', + '.. py:class:: E', + ' :module: target.autoclass_content', + '', + ' __new__ docstring', + ' ', + '', + '.. py:class:: F()', + ' :module: target.autoclass_content', + '', + ' __init__ docstring', + ' ' + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autoclass_content_both(app): + app.config.autoclass_content = 'both' + options = {"members": None} + actual = do_autodoc(app, 'module', 'target.autoclass_content', options) + assert list(actual) == [ + '', + '.. py:module:: target.autoclass_content', + '', + '', + '.. py:class:: A', + ' :module: target.autoclass_content', + '', + ' A class having no __init__, no __new__', + ' ', + '', + '.. py:class:: B()', + ' :module: target.autoclass_content', + '', + ' A class having __init__(no docstring), no __new__', + ' ', + '', + '.. py:class:: C()', + ' :module: target.autoclass_content', + '', + ' A class having __init__, no __new__', + ' ', + ' __init__ docstring', + ' ', + '', + '.. py:class:: D', + ' :module: target.autoclass_content', + '', + ' A class having no __init__, __new__(no docstring)', + ' ', + '', + '.. py:class:: E', + ' :module: target.autoclass_content', + '', + ' A class having no __init__, __new__', + ' ', + ' __new__ docstring', + ' ', + '', + '.. py:class:: F()', + ' :module: target.autoclass_content', + '', + ' A class having both __init__ and __new__', + ' ', + ' __init__ docstring', + ' ' + ] + + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_inherit_docstrings(app): assert app.config.autodoc_inherit_docstrings is True # default From 81e2e1d97149e8a9993ae09a10a0f3caae88e7c4 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 3 Jun 2019 22:52:25 +0900 Subject: [PATCH 26/84] test: Update testcase for autoclass_content and autodoc_docstring_signature --- .../target/docstring_signature.py | 19 ++++ tests/test_autodoc.py | 33 ------- tests/test_ext_autodoc_configs.py | 91 +++++++++++++++++++ 3 files changed, 110 insertions(+), 33 deletions(-) create mode 100644 tests/roots/test-ext-autodoc/target/docstring_signature.py diff --git a/tests/roots/test-ext-autodoc/target/docstring_signature.py b/tests/roots/test-ext-autodoc/target/docstring_signature.py new file mode 100644 index 000000000..2e5499770 --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/docstring_signature.py @@ -0,0 +1,19 @@ +class A: + """A(foo, bar)""" + + +class B: + """B(foo, bar)""" + def __init__(self): + """B(foo, bar, baz)""" + + +class C: + """C(foo, bar)""" + def __new__(cls): + """C(foo, bar, baz)""" + + +class D: + def __init__(self): + """D(foo, bar, baz)""" diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 07ffb2903..98f8f1a99 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -325,39 +325,6 @@ def test_get_doc(): """Döcstring""" assert getdocl('function', f) == ['Döcstring'] - # __init__ have signature at first line of docstring - class E: - """Class docstring""" - def __init__(self, *args, **kw): - """ - __init__(a1, a2, kw1=True, kw2=False) - - Init docstring - """ - - # signature line in the docstring will be kept when - # autodoc_docstring_signature == False - directive.env.config.autodoc_docstring_signature = False - directive.env.config.autoclass_content = 'class' - assert getdocl('class', E) == ['Class docstring'] - directive.env.config.autoclass_content = 'init' - assert getdocl('class', E) == ['__init__(a1, a2, kw1=True, kw2=False)', - '', 'Init docstring'] - directive.env.config.autoclass_content = 'both' - assert getdocl('class', E) == ['Class docstring', '', - '__init__(a1, a2, kw1=True, kw2=False)', - '', 'Init docstring'] - - # signature line in the docstring will be removed when - # autodoc_docstring_signature == True - directive.env.config.autodoc_docstring_signature = True # default - directive.env.config.autoclass_content = 'class' - assert getdocl('class', E) == ['Class docstring'] - directive.env.config.autoclass_content = 'init' - assert getdocl('class', E) == ['Init docstring'] - directive.env.config.autoclass_content = 'both' - assert getdocl('class', E) == ['Class docstring', '', 'Init docstring'] - # verify that method docstrings get extracted in both normal case # and in case of bound method posing as a function class J: # NOQA diff --git a/tests/test_ext_autodoc_configs.py b/tests/test_ext_autodoc_configs.py index a53af11fc..bbf3debaa 100644 --- a/tests/test_ext_autodoc_configs.py +++ b/tests/test_ext_autodoc_configs.py @@ -281,6 +281,97 @@ def test_autodoc_docstring_signature(app): ] +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autoclass_content_and_docstring_signature_class(app): + app.config.autoclass_content = 'class' + options = {"members": None, + "undoc-members": None} + actual = do_autodoc(app, 'module', 'target.docstring_signature', options) + assert list(actual) == [ + '', + '.. py:module:: target.docstring_signature', + '', + '', + '.. py:class:: A(foo, bar)', + ' :module: target.docstring_signature', + '', + '', + '.. py:class:: B(foo, bar)', + ' :module: target.docstring_signature', + '', + '', + '.. py:class:: C(foo, bar)', + ' :module: target.docstring_signature', + '', + '', + '.. py:class:: D()', + ' :module: target.docstring_signature', + '' + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autoclass_content_and_docstring_signature_init(app): + app.config.autoclass_content = 'init' + options = {"members": None, + "undoc-members": None} + actual = do_autodoc(app, 'module', 'target.docstring_signature', options) + assert list(actual) == [ + '', + '.. py:module:: target.docstring_signature', + '', + '', + '.. py:class:: A(foo, bar)', + ' :module: target.docstring_signature', + '', + '', + '.. py:class:: B(foo, bar, baz)', + ' :module: target.docstring_signature', + '', + '', + '.. py:class:: C(foo, bar, baz)', + ' :module: target.docstring_signature', + '', + '', + '.. py:class:: D(foo, bar, baz)', + ' :module: target.docstring_signature', + '' + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autoclass_content_and_docstring_signature_both(app): + app.config.autoclass_content = 'both' + options = {"members": None, + "undoc-members": None} + actual = do_autodoc(app, 'module', 'target.docstring_signature', options) + assert list(actual) == [ + '', + '.. py:module:: target.docstring_signature', + '', + '', + '.. py:class:: A(foo, bar)', + ' :module: target.docstring_signature', + '', + '', + '.. py:class:: B(foo, bar)', + ' :module: target.docstring_signature', + '', + ' B(foo, bar, baz)', + ' ', + '', + '.. py:class:: C(foo, bar)', + ' :module: target.docstring_signature', + '', + ' C(foo, bar, baz)', + ' ', + '', + '.. py:class:: D(foo, bar, baz)', + ' :module: target.docstring_signature', + '', + ] + + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_mocked_module_imports(app, warning): # no autodoc_mock_imports From 17e26bf29fa6d8f2f40b9da47b1131c30680cd80 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 3 Jun 2019 22:23:55 +0900 Subject: [PATCH 27/84] Migrate to py3 style type annotation: sphinx.util.requests --- sphinx/util/requests.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/sphinx/util/requests.py b/sphinx/util/requests.py index dee52e281..a279b4eb4 100644 --- a/sphinx/util/requests.py +++ b/sphinx/util/requests.py @@ -10,11 +10,14 @@ import warnings from contextlib import contextmanager +from typing import Generator, Union from urllib.parse import urlsplit import pkg_resources import requests +from sphinx.config import Config + try: from requests.packages.urllib3.exceptions import SSLError except ImportError: @@ -54,17 +57,12 @@ else: pkg_resources.VersionConflict): pass # ignored -if False: - # For type annotation - from typing import Any, Generator, Union # NOQA - from sphinx.config import Config # NOQA useragent_header = [('User-Agent', 'Mozilla/5.0 (X11; Linux x86_64; rv:25.0) Gecko/20100101 Firefox/25.0')] -def is_ssl_error(exc): - # type: (Exception) -> bool +def is_ssl_error(exc: Exception) -> bool: """Check an exception is SSLError.""" if isinstance(exc, SSLError): return True @@ -77,8 +75,7 @@ def is_ssl_error(exc): @contextmanager -def ignore_insecure_warning(**kwargs): - # type: (Any) -> Generator +def ignore_insecure_warning(**kwargs) -> Generator[None, None, None]: with warnings.catch_warnings(): if not kwargs.get('verify') and InsecureRequestWarning: # ignore InsecureRequestWarning if verify=False @@ -86,8 +83,7 @@ def ignore_insecure_warning(**kwargs): yield -def _get_tls_cacert(url, config): - # type: (str, Config) -> Union[str, bool] +def _get_tls_cacert(url: str, config: Config) -> Union[str, bool]: """Get additional CA cert for a specific URL. This also returns ``False`` if verification is disabled. @@ -109,8 +105,7 @@ def _get_tls_cacert(url, config): return certs.get(hostname, True) -def get(url, **kwargs): - # type: (str, Any) -> requests.Response +def get(url: str, **kwargs) -> requests.Response: """Sends a GET request like requests.get(). This sets up User-Agent header and TLS verification automatically.""" @@ -123,8 +118,7 @@ def get(url, **kwargs): return requests.get(url, **kwargs) -def head(url, **kwargs): - # type: (str, Any) -> requests.Response +def head(url: str, **kwargs) -> requests.Response: """Sends a HEAD request like requests.head(). This sets up User-Agent header and TLS verification automatically.""" From 6fc8873087ce38f1a4217ca8d5dc2beaf0c32cda Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Mon, 3 Jun 2019 16:25:20 +0300 Subject: [PATCH 28/84] Make generated download links reproducible Without this change the hexdigest generated in DownloadFiles.add_file() depends on source_dir, while just the path fragment relative to it is sufficient. --- sphinx/environment/collectors/asset.py | 2 +- tests/test_build_html.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/environment/collectors/asset.py b/sphinx/environment/collectors/asset.py index 572dea7a4..fed8280c1 100644 --- a/sphinx/environment/collectors/asset.py +++ b/sphinx/environment/collectors/asset.py @@ -137,7 +137,7 @@ class DownloadFileCollector(EnvironmentCollector): logger.warning(__('download file not readable: %s') % filename, location=node, type='download', subtype='not_readable') continue - node['filename'] = app.env.dlfiles.add_file(app.env.docname, filename) + node['filename'] = app.env.dlfiles.add_file(app.env.docname, rel_filename) def setup(app): diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 2df1f8412..8b2d44b5c 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -451,7 +451,7 @@ def test_html_download(app): @pytest.mark.sphinx('html', testroot='roles-download') def test_html_download_role(app, status, warning): app.build() - digest = md5((app.srcdir / 'dummy.dat').encode()).hexdigest() + digest = md5(b'dummy.dat').hexdigest() assert (app.outdir / '_downloads' / digest / 'dummy.dat').exists() content = (app.outdir / 'index.html').text() From 9df6e4014583840900a755149287292daf9399c5 Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Mon, 3 Jun 2019 16:28:41 +0300 Subject: [PATCH 29/84] Test that downloads with the same filename are still handled correctly --- tests/roots/test-roles-download/another/dummy.dat | 1 + tests/roots/test-roles-download/index.rst | 1 + tests/test_build_html.py | 7 +++++++ 3 files changed, 9 insertions(+) create mode 100644 tests/roots/test-roles-download/another/dummy.dat diff --git a/tests/roots/test-roles-download/another/dummy.dat b/tests/roots/test-roles-download/another/dummy.dat new file mode 100644 index 000000000..f6d9fed11 --- /dev/null +++ b/tests/roots/test-roles-download/another/dummy.dat @@ -0,0 +1 @@ +this one will have some content diff --git a/tests/roots/test-roles-download/index.rst b/tests/roots/test-roles-download/index.rst index 41cda621a..cdb075ef3 100644 --- a/tests/roots/test-roles-download/index.rst +++ b/tests/roots/test-roles-download/index.rst @@ -2,5 +2,6 @@ test-roles-download =================== * :download:`dummy.dat` +* :download:`another/dummy.dat` * :download:`not_found.dat` * :download:`Sphinx logo ` diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 8b2d44b5c..677ca9de0 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -453,6 +453,8 @@ def test_html_download_role(app, status, warning): app.build() digest = md5(b'dummy.dat').hexdigest() assert (app.outdir / '_downloads' / digest / 'dummy.dat').exists() + digest_another = md5(b'another/dummy.dat').hexdigest() + assert (app.outdir / '_downloads' / digest_another / 'dummy.dat').exists() content = (app.outdir / 'index.html').text() assert (('
  • ' 'dummy.dat

  • ' % digest) in content) + assert (('
  • ' + '' + 'another/dummy.dat

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

  • ' in content) assert ('
  • Date: Tue, 4 Jun 2019 13:57:24 +0200 Subject: [PATCH 30/84] LaTeX: prevent pagebrak separating note from section title Fixes: #6442 --- CHANGES | 3 +++ sphinx/texinputs/sphinx.sty | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index b82a191ea..aac8a8b35 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,9 @@ Features added Bugs fixed ---------- +* #6442: LaTeX: admonitions of :rst:dir:`note` type can get separated from + immediately preceding section title by pagebreak + Testing -------- diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 6ace6a9ce..184b0d820 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -6,7 +6,7 @@ % \NeedsTeXFormat{LaTeX2e}[1995/12/01] -\ProvidesPackage{sphinx}[2019/01/12 v1.8.4 LaTeX package (Sphinx markup)] +\ProvidesPackage{sphinx}[2019/06/04 v2.1.1 LaTeX package (Sphinx markup)] % provides \ltx@ifundefined % (many packages load ltxcmds: graphicx does for pdftex and lualatex but @@ -1444,7 +1444,7 @@ % Some are quite plain % the spx@notice@bordercolor etc are set in the sphinxadmonition environment \newenvironment{sphinxlightbox}{% - \par\allowbreak + \par \noindent{\color{spx@notice@bordercolor}% \rule{\linewidth}{\spx@notice@border}}\par\nobreak {\parskip\z@skip\noindent}% From a3c9edfbe6d50b486ea28876e47b7d6e27379916 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 4 Jun 2019 01:00:15 +0900 Subject: [PATCH 31/84] Migrate to py3 style type annotation: sphinx.util.osutil --- sphinx/util/osutil.py | 75 +++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 50 deletions(-) diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py index 2b3fe51b0..3751fcc08 100644 --- a/sphinx/util/osutil.py +++ b/sphinx/util/osutil.py @@ -19,14 +19,11 @@ import time import warnings from io import StringIO from os import path +from typing import Any, Generator, Iterator, List, Tuple, Type from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning from sphinx.testing.path import path as Path -if False: - # For type annotation - from typing import Any, Iterator, List, Tuple # NOQA - # Errnos that we need. EEXIST = getattr(errno, 'EEXIST', 0) # RemovedInSphinx40Warning ENOENT = getattr(errno, 'ENOENT', 0) # RemovedInSphinx40Warning @@ -41,19 +38,16 @@ EINVAL = getattr(errno, 'EINVAL', 0) # RemovedInSphinx40Warning SEP = "/" -def os_path(canonicalpath): - # type: (str) -> str +def os_path(canonicalpath: str) -> str: return canonicalpath.replace(SEP, path.sep) -def canon_path(nativepath): - # type: (str) -> str +def canon_path(nativepath: str) -> str: """Return path in OS-independent form""" return nativepath.replace(path.sep, SEP) -def relative_uri(base, to): - # type: (str, str) -> str +def relative_uri(base: str, to: str) -> str: """Return a relative URL from ``base`` to ``to``.""" if to.startswith(SEP): return to @@ -76,22 +70,19 @@ def relative_uri(base, to): return ('..' + SEP) * (len(b2) - 1) + SEP.join(t2) -def ensuredir(path): - # type: (str) -> None +def ensuredir(path: str) -> None: """Ensure that a path exists.""" os.makedirs(path, exist_ok=True) -def walk(top, topdown=True, followlinks=False): - # type: (str, bool, bool) -> Iterator[Tuple[str, List[str], List[str]]] +def walk(top: str, topdown: bool = True, followlinks: bool = False) -> Iterator[Tuple[str, List[str], List[str]]]: # NOQA warnings.warn('sphinx.util.osutil.walk() is deprecated for removal. ' 'Please use os.walk() instead.', RemovedInSphinx40Warning) return os.walk(top, topdown=topdown, followlinks=followlinks) -def mtimes_of_files(dirnames, suffix): - # type: (List[str], str) -> Iterator[float] +def mtimes_of_files(dirnames: List[str], suffix: str) -> Iterator[float]: for dirname in dirnames: for root, dirs, files in os.walk(dirname): for sfile in files: @@ -102,8 +93,7 @@ def mtimes_of_files(dirnames, suffix): pass -def movefile(source, dest): - # type: (str, str) -> None +def movefile(source: str, dest: str) -> None: """Move a file, removing the destination if it exists.""" if os.path.exists(dest): try: @@ -113,16 +103,14 @@ def movefile(source, dest): os.rename(source, dest) -def copytimes(source, dest): - # type: (str, str) -> None +def copytimes(source: str, dest: str) -> None: """Copy a file's modification times.""" st = os.stat(source) if hasattr(os, 'utime'): os.utime(dest, (st.st_atime, st.st_mtime)) -def copyfile(source, dest): - # type: (str, str) -> None +def copyfile(source: str, dest: str) -> None: """Copy a file and its modification times, if possible. Note: ``copyfile`` skips copying if the file has not been changed""" @@ -139,18 +127,15 @@ no_fn_re = re.compile(r'[^a-zA-Z0-9_-]') project_suffix_re = re.compile(' Documentation$') -def make_filename(string): - # type: (str) -> str +def make_filename(string: str) -> str: return no_fn_re.sub('', string) or 'sphinx' -def make_filename_from_project(project): - # type: (str) -> str +def make_filename_from_project(project: str) -> str: return make_filename(project_suffix_re.sub('', project)).lower() -def ustrftime(format, *args): - # type: (str, Any) -> str +def ustrftime(format: str, *args) -> str: """[DEPRECATED] strftime for unicode strings.""" warnings.warn('sphinx.util.osutil.ustrtime is deprecated for removal', RemovedInSphinx30Warning, stacklevel=2) @@ -171,8 +156,7 @@ def ustrftime(format, *args): return r.encode().decode('unicode-escape') -def relpath(path, start=os.curdir): - # type: (str, str) -> str +def relpath(path: str, start: str = os.curdir) -> str: """Return a relative filepath to *path* either from the current directory or from an optional *start* directory. @@ -189,8 +173,7 @@ safe_relpath = relpath # for compatibility fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() -def abspath(pathdir): - # type: (str) -> str +def abspath(pathdir: str) -> str: if isinstance(pathdir, Path): return pathdir.abspath() else: @@ -205,8 +188,7 @@ def abspath(pathdir): return pathdir -def getcwd(): - # type: () -> str +def getcwd() -> str: warnings.warn('sphinx.util.osutil.getcwd() is deprecated. ' 'Please use os.getcwd() instead.', RemovedInSphinx40Warning) @@ -214,8 +196,7 @@ def getcwd(): @contextlib.contextmanager -def cd(target_dir): - # type: (str) -> Iterator[None] +def cd(target_dir: str) -> Generator[None, None, None]: cwd = os.getcwd() try: os.chdir(target_dir) @@ -236,19 +217,16 @@ class FileAvoidWrite: Objects can be used as context managers. """ - def __init__(self, path): - # type: (str) -> None + def __init__(self, path: str) -> None: self._path = path self._io = None # type: StringIO - def write(self, data): - # type: (str) -> None + def write(self, data: str) -> None: if not self._io: self._io = StringIO() self._io.write(data) - def close(self): - # type: () -> None + def close(self) -> None: """Stop accepting writes and write file, if needed.""" if not self._io: raise Exception('FileAvoidWrite does not support empty files.') @@ -267,16 +245,14 @@ class FileAvoidWrite: with open(self._path, 'w') as f: f.write(buf) - def __enter__(self): - # type: () -> FileAvoidWrite + def __enter__(self) -> "FileAvoidWrite": return self - def __exit__(self, type, value, traceback): - # type: (str, str, str) -> None + def __exit__(self, exc_type: Type[Exception], exc_value: Exception, traceback: Any) -> bool: # NOQA self.close() + return True - def __getattr__(self, name): - # type: (str) -> Any + def __getattr__(self, name: str) -> Any: # Proxy to _io instance. if not self._io: raise Exception('Must write to FileAvoidWrite before other ' @@ -285,8 +261,7 @@ class FileAvoidWrite: return getattr(self._io, name) -def rmtree(path): - # type: (str) -> None +def rmtree(path: str) -> None: if os.path.isdir(path): shutil.rmtree(path) else: From 073b92d45a59eb5065d304fbaf0fc2d2ff88c5e8 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 4 Jun 2019 01:00:45 +0900 Subject: [PATCH 32/84] Migrate to py3 style type annotation: sphinx.util.rst --- sphinx/util/rst.py | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/sphinx/util/rst.py b/sphinx/util/rst.py index c897b075a..0824e413f 100644 --- a/sphinx/util/rst.py +++ b/sphinx/util/rst.py @@ -11,23 +11,20 @@ import re from collections import defaultdict from contextlib import contextmanager +from typing import Dict, Generator from unicodedata import east_asian_width from docutils.parsers.rst import roles from docutils.parsers.rst.languages import en as english +from docutils.statemachine import StringList from docutils.utils import Reporter +from jinja2 import Environment from jinja2 import environmentfilter from sphinx.locale import __ from sphinx.util import docutils from sphinx.util import logging -if False: - # For type annotation - from typing import Callable, Dict, Generator # NOQA - from docutils.statemachine import StringList # NOQA - from jinja2 import Environment # NOQA - logger = logging.getLogger(__name__) docinfo_re = re.compile(':\\w+:.*?') @@ -40,18 +37,15 @@ WIDECHARS = defaultdict(lambda: "WF") # type: Dict[str, str] WIDECHARS["ja"] = "WFA" # In Japanese, Ambiguous characters also have double width -def escape(text): - # type: (str) -> str +def escape(text: str) -> str: text = symbols_re.sub(r'\\\1', text) text = re.sub(r'^\.', r'\.', text) # escape a dot at top return text -def textwidth(text, widechars='WF'): - # type: (str, str) -> int +def textwidth(text: str, widechars: str = 'WF') -> int: """Get width of text.""" - def charwidth(char, widechars): - # type: (str, str) -> int + def charwidth(char: str, widechars: str) -> int: if east_asian_width(char) in widechars: return 2 else: @@ -61,8 +55,7 @@ def textwidth(text, widechars='WF'): @environmentfilter -def heading(env, text, level=1): - # type: (Environment, str, int) -> str +def heading(env: Environment, text: str, level: int = 1) -> str: """Create a heading for *level*.""" assert level <= 3 width = textwidth(text, WIDECHARS[env.language]) # type: ignore @@ -71,8 +64,7 @@ def heading(env, text, level=1): @contextmanager -def default_role(docname, name): - # type: (str, str) -> Generator +def default_role(docname: str, name: str) -> Generator[None, None, None]: if name: dummy_reporter = Reporter('', 4, 4) role_fn, _ = roles.role(name, english, 0, dummy_reporter) @@ -86,8 +78,7 @@ def default_role(docname, name): docutils.unregister_role('') -def prepend_prolog(content, prolog): - # type: (StringList, str) -> None +def prepend_prolog(content: StringList, prolog: str) -> None: """Prepend a string to content body as prolog.""" if prolog: pos = 0 @@ -109,8 +100,7 @@ def prepend_prolog(content, prolog): content.insert(pos + lineno + 1, '', '', 0) -def append_epilog(content, epilog): - # type: (StringList, str) -> None +def append_epilog(content: StringList, epilog: str) -> None: """Append a string to content body as epilog.""" if epilog: content.append('', '', 0) From 1e70267cc8a3a31dbadea4e72971bec4c60cb255 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 4 Jun 2019 01:02:02 +0900 Subject: [PATCH 33/84] Migrate to py3 style type annotation: sphinx.util.template --- sphinx/util/template.py | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/sphinx/util/template.py b/sphinx/util/template.py index b521c5c79..636767d41 100644 --- a/sphinx/util/template.py +++ b/sphinx/util/template.py @@ -9,7 +9,9 @@ """ import os +from typing import Dict +from jinja2.loaders import BaseLoader from jinja2.sandbox import SandboxedEnvironment from sphinx import package_dir @@ -17,58 +19,45 @@ from sphinx.jinja2glue import SphinxFileSystemLoader from sphinx.locale import get_translator from sphinx.util import rst, texescape -if False: - # For type annotation - from typing import Dict # NOQA - from jinja2.loaders import BaseLoader # NOQA - class BaseRenderer: - def __init__(self, loader=None): - # type: (BaseLoader) -> None + def __init__(self, loader: BaseLoader = None) -> None: self.env = SandboxedEnvironment(loader=loader, extensions=['jinja2.ext.i18n']) self.env.filters['repr'] = repr self.env.install_gettext_translations(get_translator()) # type: ignore - def render(self, template_name, context): - # type: (str, Dict) -> str + def render(self, template_name: str, context: Dict) -> str: return self.env.get_template(template_name).render(context) - def render_string(self, source, context): - # type: (str, Dict) -> str + def render_string(self, source: str, context: Dict) -> str: return self.env.from_string(source).render(context) class FileRenderer(BaseRenderer): - def __init__(self, search_path): - # type: (str) -> None + def __init__(self, search_path: str) -> None: loader = SphinxFileSystemLoader(search_path) super().__init__(loader) @classmethod - def render_from_file(cls, filename, context): - # type: (str, Dict) -> str + def render_from_file(cls, filename: str, context: Dict) -> str: dirname = os.path.dirname(filename) basename = os.path.basename(filename) return cls(dirname).render(basename, context) class SphinxRenderer(FileRenderer): - def __init__(self, template_path=None): - # type: (str) -> None + def __init__(self, template_path: str = None) -> None: if template_path is None: template_path = os.path.join(package_dir, 'templates') super().__init__(template_path) @classmethod - def render_from_file(cls, filename, context): - # type: (str, Dict) -> str + def render_from_file(cls, filename: str, context: Dict) -> str: return FileRenderer.render_from_file(filename, context) class LaTeXRenderer(SphinxRenderer): - def __init__(self, template_path=None): - # type: (str) -> None + def __init__(self, template_path: str = None) -> None: if template_path is None: template_path = os.path.join(package_dir, 'templates', 'latex') super().__init__(template_path) @@ -87,8 +76,7 @@ class LaTeXRenderer(SphinxRenderer): class ReSTRenderer(SphinxRenderer): - def __init__(self, template_path=None, language=None): - # type: (str, str) -> None + def __init__(self, template_path: str = None, language: str = None) -> None: super().__init__(template_path) # add language to environment From 8aa04eb85537e6607718fad35b70758747258248 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 4 Jun 2019 01:02:23 +0900 Subject: [PATCH 34/84] Migrate to py3 style type annotation: sphinx.util.nodes --- sphinx/util/nodes.py | 98 +++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 60 deletions(-) diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 58c6a6698..e0aaaa14e 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -10,9 +10,14 @@ import re import warnings -from typing import Any, cast +from typing import Any, Callable, Iterable, List, Set, Tuple, Type +from typing import cast from docutils import nodes +from docutils.nodes import Element, Node +from docutils.parsers.rst import Directive +from docutils.parsers.rst.states import Inliner +from docutils.statemachine import StringList from sphinx import addnodes from sphinx.deprecation import RemovedInSphinx40Warning @@ -21,11 +26,8 @@ from sphinx.util import logging if False: # For type annotation - from typing import Callable, Iterable, List, Optional, Set, Tuple, Type # NOQA - from docutils.parsers.rst.states import Inliner # NOQA - from docutils.statemachine import StringList # NOQA - from sphinx.builders import Builder # NOQA - from sphinx.utils.tags import Tags # NOQA + from sphinx.builders import Builder + from sphinx.utils.tags import Tags logger = logging.getLogger(__name__) @@ -57,13 +59,11 @@ class NodeMatcher: # => [, , ...] """ - def __init__(self, *classes, **attrs): - # type: (Type[nodes.Node], Any) -> None + def __init__(self, *classes: Type[Node], **attrs) -> None: self.classes = classes self.attrs = attrs - def match(self, node): - # type: (nodes.Node) -> bool + def match(self, node: Node) -> bool: try: if self.classes and not isinstance(node, self.classes): return False @@ -85,13 +85,11 @@ class NodeMatcher: # for non-Element nodes return False - def __call__(self, node): - # type: (nodes.Node) -> bool + def __call__(self, node: Node) -> bool: return self.match(node) -def get_full_module_name(node): - # type: (nodes.Node) -> str +def get_full_module_name(node: Node) -> str: """ return full module dotted path like: 'docutils.nodes.paragraph' @@ -101,8 +99,7 @@ def get_full_module_name(node): return '{}.{}'.format(node.__module__, node.__class__.__name__) -def repr_domxml(node, length=80): - # type: (nodes.Node, Optional[int]) -> str +def repr_domxml(node: Node, length: int = 80) -> str: """ return DOM XML representation of the specified node like: 'New in version...' @@ -122,8 +119,7 @@ def repr_domxml(node, length=80): return text -def apply_source_workaround(node): - # type: (nodes.Element) -> None +def apply_source_workaround(node: Element) -> None: # workaround: nodes.term have wrong rawsource if classifier is specified. # The behavior of docutils-0.11, 0.12 is: # * when ``term text : classifier1 : classifier2`` is specified, @@ -186,8 +182,7 @@ IGNORED_NODES = ( ) -def is_pending_meta(node): - # type: (nodes.Node) -> bool +def is_pending_meta(node: Node) -> bool: if (isinstance(node, nodes.pending) and isinstance(node.details.get('nodes', [None])[0], addnodes.meta)): return True @@ -195,8 +190,7 @@ def is_pending_meta(node): return False -def is_translatable(node): - # type: (nodes.Node) -> bool +def is_translatable(node: Node) -> bool: if isinstance(node, addnodes.translatable): return True @@ -251,8 +245,7 @@ META_TYPE_NODES = ( ) -def extract_messages(doctree): - # type: (nodes.Element) -> Iterable[Tuple[nodes.Element, str]] +def extract_messages(doctree: Element) -> Iterable[Tuple[Element, str]]: """Extract translatable messages from a document tree.""" for node in doctree.traverse(is_translatable): # type: nodes.Element if isinstance(node, addnodes.translatable): @@ -279,39 +272,34 @@ def extract_messages(doctree): yield node, msg -def find_source_node(node): - # type: (nodes.Element) -> str +def find_source_node(node: Element) -> str: warnings.warn('find_source_node() is deprecated.', RemovedInSphinx40Warning) return get_node_source(node) -def get_node_source(node): - # type: (nodes.Element) -> str +def get_node_source(node: Element) -> str: for pnode in traverse_parent(node): if pnode.source: return pnode.source return None -def get_node_line(node): - # type: (nodes.Element) -> int +def get_node_line(node: Element) -> int: for pnode in traverse_parent(node): if pnode.line: return pnode.line return None -def traverse_parent(node, cls=None): - # type: (nodes.Element, Any) -> Iterable[nodes.Element] +def traverse_parent(node: Element, cls: Any = None) -> Iterable[Element]: while node: if cls is None or isinstance(node, cls): yield node node = node.parent -def get_prev_node(node): - # type: (nodes.Node) -> nodes.Node +def get_prev_node(node: Node) -> Node: pos = node.parent.index(node) if pos > 0: return node.parent[pos - 1] @@ -319,8 +307,7 @@ def get_prev_node(node): return None -def traverse_translatable_index(doctree): - # type: (nodes.Element) -> Iterable[Tuple[nodes.Element, List[str]]] +def traverse_translatable_index(doctree: Element) -> Iterable[Tuple[Element, List[str]]]: """Traverse translatable index node from a document tree.""" for node in doctree.traverse(NodeMatcher(addnodes.index, inline=False)): # type: addnodes.index # NOQA if 'raw_entries' in node: @@ -330,8 +317,7 @@ def traverse_translatable_index(doctree): yield node, entries -def nested_parse_with_titles(state, content, node): - # type: (Any, StringList, nodes.Node) -> str +def nested_parse_with_titles(state: Any, content: StringList, node: Node) -> str: """Version of state.nested_parse() that allows titles and does not require titles to have the same decoration as the calling document. @@ -350,8 +336,7 @@ def nested_parse_with_titles(state, content, node): state.memo.section_level = surrounding_section_level -def clean_astext(node): - # type: (nodes.Element) -> str +def clean_astext(node: Element) -> str: """Like node.astext(), but ignore images.""" node = node.deepcopy() for img in node.traverse(nodes.image): @@ -361,8 +346,7 @@ def clean_astext(node): return node.astext() -def split_explicit_title(text): - # type: (str) -> Tuple[bool, str, str] +def split_explicit_title(text: str) -> Tuple[bool, str, str]: """Split role content into title and target, if given.""" match = explicit_title_re.match(text) if match: @@ -375,8 +359,7 @@ indextypes = [ ] -def process_index_entry(entry, targetid): - # type: (str, str) -> List[Tuple[str, str, str, str, str]] +def process_index_entry(entry: str, targetid: str) -> List[Tuple[str, str, str, str, str]]: from sphinx.domains.python import pairindextypes indexentries = [] # type: List[Tuple[str, str, str, str, str]] @@ -414,8 +397,9 @@ def process_index_entry(entry, targetid): return indexentries -def inline_all_toctrees(builder, docnameset, docname, tree, colorfunc, traversed): - # type: (Builder, Set[str], str, nodes.document, Callable, List[str]) -> nodes.document +def inline_all_toctrees(builder: "Builder", docnameset: Set[str], docname: str, + tree: nodes.document, colorfunc: Callable, traversed: List[str] + ) -> nodes.document: """Inline all toctrees in the *tree*. Record all docnames in *docnameset*, and output docnames with *colorfunc*. @@ -447,8 +431,8 @@ def inline_all_toctrees(builder, docnameset, docname, tree, colorfunc, traversed return tree -def make_refnode(builder, fromdocname, todocname, targetid, child, title=None): - # type: (Builder, str, str, str, nodes.Node, str) -> nodes.reference +def make_refnode(builder: "Builder", fromdocname: str, todocname: str, targetid: str, + child: Node, title: str = None) -> nodes.reference: """Shortcut to create a reference node.""" node = nodes.reference('', '', internal=True) if fromdocname == todocname and targetid: @@ -465,19 +449,16 @@ def make_refnode(builder, fromdocname, todocname, targetid, child, title=None): return node -def set_source_info(directive, node): - # type: (Any, nodes.Node) -> None +def set_source_info(directive: Directive, node: Node) -> None: node.source, node.line = \ directive.state_machine.get_source_and_line(directive.lineno) -def set_role_source_info(inliner, lineno, node): - # type: (Inliner, int, nodes.Node) -> None +def set_role_source_info(inliner: Inliner, lineno: int, node: Node) -> None: node.source, node.line = inliner.reporter.get_source_and_line(lineno) # type: ignore -def copy_source_info(src, dst): - # type: (nodes.Element, nodes.Element) -> None +def copy_source_info(src: Element, dst: Element) -> None: dst.source = get_node_source(src) dst.line = get_node_line(src) @@ -493,8 +474,7 @@ NON_SMARTQUOTABLE_PARENT_NODES = ( ) -def is_smartquotable(node): - # type: (nodes.Node) -> bool +def is_smartquotable(node: Node) -> bool: """Check the node is smart-quotable or not.""" if isinstance(node.parent, NON_SMARTQUOTABLE_PARENT_NODES): return False @@ -506,8 +486,7 @@ def is_smartquotable(node): return True -def process_only_nodes(document, tags): - # type: (nodes.Node, Tags) -> None +def process_only_nodes(document: Node, tags: "Tags") -> None: """Filter ``only`` nodes which does not match *tags*.""" for node in document.traverse(addnodes.only): try: @@ -530,8 +509,7 @@ def process_only_nodes(document, tags): # monkey-patch Element.copy to copy the rawsource and line # for docutils-0.14 or older versions. -def _new_copy(self): - # type: (nodes.Element) -> nodes.Element +def _new_copy(self: Element) -> Element: newnode = self.__class__(self.rawsource, **self.attributes) if isinstance(self, nodes.Element): newnode.source = self.source From 086e46bdf212063e5cde6964ab02a24d6f6fe5fc Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 5 Jun 2019 01:30:23 +0900 Subject: [PATCH 35/84] Migrate to py3 style type annotation: sphinx.util.math --- sphinx/util/math.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/sphinx/util/math.py b/sphinx/util/math.py index d296a3fe0..2af4e4db6 100644 --- a/sphinx/util/math.py +++ b/sphinx/util/math.py @@ -8,15 +8,12 @@ :license: BSD, see LICENSE for details. """ +from docutils import nodes -if False: - # For type annotation - from docutils import nodes # NOQA - from sphinx.builders.html import HTMLTranslator # NOQA +from sphinx.builders.html import HTMLTranslator -def get_node_equation_number(writer, node): - # type: (HTMLTranslator, nodes.math_block) -> str +def get_node_equation_number(writer: HTMLTranslator, node: nodes.math_block) -> str: if writer.builder.config.math_numfig and writer.builder.config.numfig: figtype = 'displaymath' if writer.builder.name == 'singlehtml': @@ -31,10 +28,8 @@ def get_node_equation_number(writer, node): return node['number'] -def wrap_displaymath(text, label, numbering): - # type: (str, str, bool) -> str - def is_equation(part): - # type: (str) -> str +def wrap_displaymath(text: str, label: str, numbering: bool) -> str: + def is_equation(part: str) -> str: return part.strip() if label is None: From 09d8526ee772f10cd04eb579a8b40e540a99fdc5 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 5 Jun 2019 01:31:10 +0900 Subject: [PATCH 36/84] Migrate to py3 style type annotation: sphinx.util.parallel --- sphinx/util/parallel.py | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/sphinx/util/parallel.py b/sphinx/util/parallel.py index 013dc3071..2d519a8d3 100644 --- a/sphinx/util/parallel.py +++ b/sphinx/util/parallel.py @@ -12,6 +12,7 @@ import os import time import traceback from math import sqrt +from typing import Any, Callable, Dict, List, Sequence try: import multiprocessing @@ -21,10 +22,6 @@ except ImportError: from sphinx.errors import SphinxParallelError from sphinx.util import logging -if False: - # For type annotation - from typing import Any, Callable, Dict, List, Sequence # NOQA - logger = logging.getLogger(__name__) @@ -35,12 +32,10 @@ parallel_available = multiprocessing and (os.name == 'posix') class SerialTasks: """Has the same interface as ParallelTasks, but executes tasks directly.""" - def __init__(self, nproc=1): - # type: (int) -> None + def __init__(self, nproc: int = 1) -> None: pass - def add_task(self, task_func, arg=None, result_func=None): - # type: (Callable, Any, Callable) -> None + def add_task(self, task_func: Callable, arg: Any = None, result_func: Callable = None) -> None: # NOQA if arg is not None: res = task_func(arg) else: @@ -48,16 +43,14 @@ class SerialTasks: if result_func: result_func(res) - def join(self): - # type: () -> None + def join(self) -> None: pass class ParallelTasks: """Executes *nproc* tasks in parallel after forking.""" - def __init__(self, nproc): - # type: (int) -> None + def __init__(self, nproc: int) -> None: self.nproc = nproc # (optional) function performed by each task on the result of main task self._result_funcs = {} # type: Dict[int, Callable] @@ -74,8 +67,7 @@ class ParallelTasks: # task number of each subprocess self._taskid = 0 - def _process(self, pipe, func, arg): - # type: (Any, Callable, Any) -> None + def _process(self, pipe: Any, func: Callable, arg: Any) -> None: try: collector = logging.LogCollector() with collector.collect(): @@ -91,8 +83,7 @@ class ParallelTasks: logging.convert_serializable(collector.logs) pipe.send((failed, collector.logs, ret)) - def add_task(self, task_func, arg=None, result_func=None): - # type: (Callable, Any, Callable) -> None + def add_task(self, task_func: Callable, arg: Any = None, result_func: Callable = None) -> None: # NOQA tid = self._taskid self._taskid += 1 self._result_funcs[tid] = result_func or (lambda arg, result: None) @@ -104,13 +95,11 @@ class ParallelTasks: self._precvsWaiting[tid] = precv self._join_one() - def join(self): - # type: () -> None + def join(self) -> None: while self._pworking: self._join_one() - def _join_one(self): - # type: () -> None + def _join_one(self) -> None: for tid, pipe in self._precvs.items(): if pipe.poll(): exc, logs, result = pipe.recv() @@ -132,8 +121,7 @@ class ParallelTasks: self._pworking += 1 -def make_chunks(arguments, nproc, maxbatch=10): - # type: (Sequence[str], int, int) -> List[Any] +def make_chunks(arguments: Sequence[str], nproc: int, maxbatch: int = 10) -> List[Any]: # determine how many documents to read in one go nargs = len(arguments) chunksize = nargs // nproc From ffe5e129c3bcb4a0ecd80e5863c92d94910ecffd Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 5 Jun 2019 01:50:22 +0900 Subject: [PATCH 38/84] Migrate to py3 style type annotation: sphinx.util.stemmer --- sphinx/util/stemmer/__init__.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/sphinx/util/stemmer/__init__.py b/sphinx/util/stemmer/__init__.py index 047aac708..bda5d2bc2 100644 --- a/sphinx/util/stemmer/__init__.py +++ b/sphinx/util/stemmer/__init__.py @@ -18,18 +18,15 @@ except ImportError: class BaseStemmer: - def stem(self, word): - # type: (str) -> str + def stem(self, word: str) -> str: raise NotImplementedError() class PyStemmer(BaseStemmer): - def __init__(self): - # type: () -> None + def __init__(self) -> None: self.stemmer = _PyStemmer('porter') - def stem(self, word): - # type: (str) -> str + def stem(self, word: str) -> str: return self.stemmer.stemWord(word) @@ -37,13 +34,11 @@ class StandardStemmer(PorterStemmer, BaseStemmer): # type: ignore """All those porter stemmer implementations look hideous; make at least the stem method nicer. """ - def stem(self, word): # type: ignore - # type: (str) -> str + def stem(self, word: str) -> str: # type: ignore return super().stem(word, 0, len(word) - 1) -def get_stemmer(): - # type: () -> BaseStemmer +def get_stemmer() -> BaseStemmer: if PYSTEMMER: return PyStemmer() else: From d141fbf054cf3885538820363460e9e729e3cef0 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 5 Jun 2019 01:49:51 +0900 Subject: [PATCH 39/84] Migrate to py3 style type annotation: sphinx.util.stemmer.porter --- sphinx/util/stemmer/porter.py | 48 ++++++++++++----------------------- 1 file changed, 16 insertions(+), 32 deletions(-) diff --git a/sphinx/util/stemmer/porter.py b/sphinx/util/stemmer/porter.py index 51c132c2c..1f979624a 100644 --- a/sphinx/util/stemmer/porter.py +++ b/sphinx/util/stemmer/porter.py @@ -30,8 +30,7 @@ class PorterStemmer: - def __init__(self): - # type: () -> None + def __init__(self) -> None: """The main part of the stemming algorithm starts here. b is a buffer holding a word to be stemmed. The letters are in b[k0], b[k0+1] ... ending at b[k]. In fact k0 = 0 in this demo program. k is @@ -47,8 +46,7 @@ class PorterStemmer: self.k0 = 0 self.j = 0 # j is a general offset into the string - def cons(self, i): - # type: (int) -> int + def cons(self, i: int) -> int: """cons(i) is TRUE <=> b[i] is a consonant.""" if self.b[i] == 'a' or self.b[i] == 'e' or self.b[i] == 'i' \ or self.b[i] == 'o' or self.b[i] == 'u': @@ -60,8 +58,7 @@ class PorterStemmer: return (not self.cons(i - 1)) return 1 - def m(self): - # type: () -> int + def m(self) -> int: """m() measures the number of consonant sequences between k0 and j. if c is a consonant sequence and v a vowel sequence, and <..> indicates arbitrary presence, @@ -98,16 +95,14 @@ class PorterStemmer: i = i + 1 i = i + 1 - def vowelinstem(self): - # type: () -> int + def vowelinstem(self) -> int: """vowelinstem() is TRUE <=> k0,...j contains a vowel""" for i in range(self.k0, self.j + 1): if not self.cons(i): return 1 return 0 - def doublec(self, j): - # type: (int) -> int + def doublec(self, j: int) -> int: """doublec(j) is TRUE <=> j,(j-1) contain a double consonant.""" if j < (self.k0 + 1): return 0 @@ -115,8 +110,7 @@ class PorterStemmer: return 0 return self.cons(j) - def cvc(self, i): - # type: (int) -> int + def cvc(self, i: int) -> int: """cvc(i) is TRUE <=> i-2,i-1,i has the form consonant - vowel - consonant and also if the second c is not w,x or y. this is used when trying to @@ -133,8 +127,7 @@ class PorterStemmer: return 0 return 1 - def ends(self, s): - # type: (str) -> int + def ends(self, s: str) -> int: """ends(s) is TRUE <=> k0,...k ends with the string s.""" length = len(s) if s[length - 1] != self.b[self.k]: # tiny speed-up @@ -146,22 +139,19 @@ class PorterStemmer: self.j = self.k - length return 1 - def setto(self, s): - # type: (str) -> None + def setto(self, s: str) -> None: """setto(s) sets (j+1),...k to the characters in the string s, readjusting k.""" length = len(s) self.b = self.b[:self.j + 1] + s + self.b[self.j + length + 1:] self.k = self.j + length - def r(self, s): - # type: (str) -> None + def r(self, s: str) -> None: """r(s) is used further down.""" if self.m() > 0: self.setto(s) - def step1ab(self): - # type: () -> None + def step1ab(self) -> None: """step1ab() gets rid of plurals and -ed or -ing. e.g. caresses -> caress @@ -208,15 +198,13 @@ class PorterStemmer: elif (self.m() == 1 and self.cvc(self.k)): self.setto("e") - def step1c(self): - # type: () -> None + def step1c(self) -> None: """step1c() turns terminal y to i when there is another vowel in the stem.""" if (self.ends("y") and self.vowelinstem()): self.b = self.b[:self.k] + 'i' + self.b[self.k + 1:] - def step2(self): - # type: () -> None + def step2(self) -> None: """step2() maps double suffices to single ones. so -ization ( = -ize plus -ation) maps to -ize etc. note that the string before the suffix must give m() > 0. @@ -275,8 +263,7 @@ class PorterStemmer: self.r("log") # To match the published algorithm, delete this phrase - def step3(self): - # type: () -> None + def step3(self) -> None: """step3() dels with -ic-, -full, -ness etc. similar strategy to step2.""" if self.b[self.k] == 'e': @@ -298,8 +285,7 @@ class PorterStemmer: if self.ends("ness"): self.r("") - def step4(self): - # type: () -> None + def step4(self) -> None: """step4() takes off -ant, -ence etc., in context vcvc.""" if self.b[self.k - 1] == 'a': if self.ends("al"): @@ -382,8 +368,7 @@ class PorterStemmer: if self.m() > 1: self.k = self.j - def step5(self): - # type: () -> None + def step5(self) -> None: """step5() removes a final -e if m() > 1, and changes -ll to -l if m() > 1. """ @@ -395,8 +380,7 @@ class PorterStemmer: if self.b[self.k] == 'l' and self.doublec(self.k) and self.m() > 1: self.k = self.k - 1 - def stem(self, p, i, j): - # type: (str, int, int) -> str + def stem(self, p: str, i: int, j: int) -> str: """In stem(p,i,j), p is a char pointer, and the string to be stemmed is from p[i] to p[j] inclusive. Typically i is zero and j is the offset to the last character of a string, (p[j+1] == '\0'). The From 60b1cec446ef184adc91bdaaca93980c49312ba7 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 5 Jun 2019 01:51:07 +0900 Subject: [PATCH 40/84] Migrate to py3 style type annotation: sphinx.util.smartypants --- sphinx/util/smartypants.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/sphinx/util/smartypants.py b/sphinx/util/smartypants.py index 7450e07b8..47f8b59b2 100644 --- a/sphinx/util/smartypants.py +++ b/sphinx/util/smartypants.py @@ -26,14 +26,12 @@ """ import re +from typing import Generator, Iterable, Tuple from docutils.utils import smartquotes from sphinx.util.docutils import __version_info__ as docutils_version -if False: # For type annotation - from typing import Generator, Iterable, Tuple # NOQA - langquotes = {'af': '“”‘’', 'af-x-altquot': '„”‚’', @@ -125,8 +123,7 @@ langquotes = {'af': '“”‘’', } -def educateQuotes(text, language='en'): - # type: (str, str) -> str +def educateQuotes(text: str, language: str = 'en') -> str: """ Parameter: - text string (unicode or bytes). - language (`BCP 47` language tag.) @@ -240,8 +237,10 @@ def educateQuotes(text, language='en'): return text -def educate_tokens(text_tokens, attr=smartquotes.default_smartypants_attr, language='en'): - # type: (Iterable[Tuple[str, str]], str, str) -> Generator[str, None, None] +def educate_tokens(text_tokens: Iterable[Tuple[str, str]], + attr: str = smartquotes.default_smartypants_attr, + language: str = 'en' + ) -> Generator[str, None, None]: """Return iterator that "educates" the items of `text_tokens`. This is modified to intercept the ``attr='2'`` as it was used by the From 9c5b26756a79ae9bbbe6bf385019687494a85649 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 5 Jun 2019 01:52:15 +0900 Subject: [PATCH 41/84] Migrate to py3 style type annotation: sphinx.util --- sphinx/util/__init__.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 66c53b37e..1fb7bcde6 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -337,15 +337,13 @@ _coding_re = re.compile(r'coding[:=]\s*([-\w.]+)') def detect_encoding(readline: Callable[[], bytes]) -> str: """Like tokenize.detect_encoding() from Py3k, but a bit simplified.""" - def read_or_stop(): - # type: () -> bytes + def read_or_stop() -> bytes: try: return readline() except StopIteration: return None - def get_normal_name(orig_enc): - # type: (str) -> str + def get_normal_name(orig_enc: str) -> str: """Imitates get_normal_name in tokenizer.c.""" # Only care about the first 12 characters. enc = orig_enc[:12].lower().replace('_', '-') @@ -356,8 +354,7 @@ def detect_encoding(readline: Callable[[], bytes]) -> str: return 'iso-8859-1' return orig_enc - def find_cookie(line): - # type: (bytes) -> str + def find_cookie(line: bytes) -> str: try: line_string = line.decode('ascii') except UnicodeDecodeError: From e3321bb3a95000e8cdeadf9e5ebb6749d5a55eed Mon Sep 17 00:00:00 2001 From: Audrey Dutcher Date: Tue, 4 Jun 2019 19:44:28 -0700 Subject: [PATCH 42/84] Don't crash when autodocumenting classes with __slots__ = None --- 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 37bdae8a9..0c6f47039 100644 --- a/sphinx/ext/autodoc/importer.py +++ b/sphinx/ext/autodoc/importer.py @@ -128,7 +128,7 @@ def get_object_members(subject, objpath, attrgetter, analyzer=None): members[name] = Attribute(name, True, value) # members in __slots__ - if isclass(subject) and hasattr(subject, '__slots__'): + if isclass(subject) and getattr(subject, '__slots__', None) is not None: from sphinx.ext.autodoc import SLOTSATTR for name in subject.__slots__: From 2f93300920354e8e5ce544e25f61dfd5da62d225 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 5 Jun 2019 22:28:39 +0900 Subject: [PATCH 43/84] Fix #6448: autodoc: crashed when autodocumenting classes with __slots__ = None --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index b82a191ea..b5bd56135 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,8 @@ Features added Bugs fixed ---------- +* #6448: autodoc: crashed when autodocumenting classes with ``__slots__ = None`` + Testing -------- From 07a45f84016921c29ce60052673f67224ad8b4d1 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 5 Jun 2019 22:39:06 +0900 Subject: [PATCH 44/84] Update CHANGES for PR #6439 --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index 7998d18a0..a66ac17d2 100644 --- a/CHANGES +++ b/CHANGES @@ -19,6 +19,7 @@ Bugs fixed * py domain: duplicated warning does not point the location of source code * #1125: html theme: scrollbar is hard to see on classic theme and macOS * #5502: linkcheck: Consider HTTP 503 response as not an error +* #6439: Make generated download links reproducible Testing -------- From bc5e18af92d17ddf34defa3403756dddaeb4b930 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 7 Jun 2019 02:23:39 +0900 Subject: [PATCH 45/84] Fix #6452: autosummary: crashed when generating document of properties --- CHANGES | 1 + sphinx/ext/autosummary/generate.py | 2 +- tests/test_ext_autosummary.py | 18 +++++++++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index b5bd56135..f8e8f46ee 100644 --- a/CHANGES +++ b/CHANGES @@ -17,6 +17,7 @@ Bugs fixed ---------- * #6448: autodoc: crashed when autodocumenting classes with ``__slots__ = None`` +* #6452: autosummary: crashed when generating document of properties Testing -------- diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index ba1ec219a..1bfbb0da4 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -221,7 +221,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', get_members(obj, {'attribute', 'property'}) parts = name.split('.') - if doc.objtype in ('method', 'attribute'): + if doc.objtype in ('method', 'attribute', 'property'): mod_name = '.'.join(parts[:-2]) cls_name = parts[-2] obj_name = '.'.join(parts[-2:]) diff --git a/tests/test_ext_autosummary.py b/tests/test_ext_autosummary.py index 04af9ed85..ae97d3b57 100644 --- a/tests/test_ext_autosummary.py +++ b/tests/test_ext_autosummary.py @@ -10,7 +10,7 @@ import sys from io import StringIO -from unittest.mock import Mock +from unittest.mock import Mock, patch import pytest from docutils import nodes @@ -19,6 +19,7 @@ from sphinx import addnodes from sphinx.ext.autosummary import ( autosummary_table, autosummary_toc, mangle_signature, import_by_name, extract_summary ) +from sphinx.ext.autosummary.generate import generate_autosummary_docs from sphinx.testing.util import assert_node, etree_parse from sphinx.util.docutils import new_document @@ -286,3 +287,18 @@ def test_autosummary_imported_members(app, status, warning): ' \n' in module) finally: sys.modules.pop('autosummary_dummy_package', None) + + +@pytest.mark.sphinx(testroot='ext-autodoc') +def test_generate_autosummary_docs_property(app): + with patch('sphinx.ext.autosummary.generate.find_autosummary_in_files') as mock: + mock.return_value = [('target.methods.Base.prop', 'prop', None)] + generate_autosummary_docs([], output_dir=app.srcdir, builder=app.builder, app=app) + + content = (app.srcdir / 'target.methods.Base.prop.rst').text() + assert content == ("target.methods.Base.prop\n" + "========================\n" + "\n" + ".. currentmodule:: target.methods\n" + "\n" + ".. autoproperty:: Base.prop") From 65122ad1cb9702b9dac220513b12c3cb222f8d94 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 4 Jun 2019 16:19:43 +0100 Subject: [PATCH 46/84] apidoc: Insert newlines between submodules Fix a regression introduced in 2.1.0 where submodules didn't have a newline between them resulting in the following warning: Explicit markup ends without a blank line; unexpected unindent. Signed-off-by: Stephen Finucane Closes: #6440 --- sphinx/templates/apidoc/package.rst | 4 ++-- tests/test_ext_apidoc.py | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/sphinx/templates/apidoc/package.rst b/sphinx/templates/apidoc/package.rst index 0026af34c..ed9f669ea 100644 --- a/sphinx/templates/apidoc/package.rst +++ b/sphinx/templates/apidoc/package.rst @@ -40,8 +40,8 @@ Submodules {{- [submodule, "module"] | join(" ") | e | heading(2) }} {% endif %} {{ automodule(submodule, automodule_options) }} -{%- endfor %} -{% endif %} +{% endfor %} +{%- endif %} {% endif %} {%- if not modulefirst and not is_namespace %} diff --git a/tests/test_ext_apidoc.py b/tests/test_ext_apidoc.py index c6cf43c7e..34c6ec824 100644 --- a/tests/test_ext_apidoc.py +++ b/tests/test_ext_apidoc.py @@ -467,7 +467,8 @@ def test_package_file(tempdir): outdir = path(tempdir) (outdir / 'testpkg').makedirs() (outdir / 'testpkg' / '__init__.py').write_text('') - (outdir / 'testpkg' / 'example.py').write_text('') + (outdir / 'testpkg' / 'hello.py').write_text('') + (outdir / 'testpkg' / 'world.py').write_text('') (outdir / 'testpkg' / 'subpkg').makedirs() (outdir / 'testpkg' / 'subpkg' / '__init__.py').write_text('') apidoc_main(['-o', tempdir, tempdir / 'testpkg']) @@ -488,10 +489,18 @@ def test_package_file(tempdir): "Submodules\n" "----------\n" "\n" - "testpkg.example module\n" - "----------------------\n" + "testpkg.hello module\n" + "--------------------\n" "\n" - ".. automodule:: testpkg.example\n" + ".. automodule:: testpkg.hello\n" + " :members:\n" + " :undoc-members:\n" + " :show-inheritance:\n" + "\n" + "testpkg.world module\n" + "--------------------\n" + "\n" + ".. automodule:: testpkg.world\n" " :members:\n" " :undoc-members:\n" " :show-inheritance:\n" From 4945628594d6c1e793b812db8733165e4cd77614 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 8 Jun 2019 13:51:14 +0900 Subject: [PATCH 47/84] Fix #6455: napoleon: docstrings for properties are not processed --- CHANGES | 1 + sphinx/ext/napoleon/docstring.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 7043d835d..6341ecaad 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,7 @@ Bugs fixed immediately preceding section title by pagebreak * #6448: autodoc: crashed when autodocumenting classes with ``__slots__ = None`` * #6452: autosummary: crashed when generating document of properties +* #6455: napoleon: docstrings for properties are not processed Testing -------- diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index 0fea99fb8..5ba65d351 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -555,7 +555,7 @@ class GoogleDocstring: # type: () -> None self._parsed_lines = self._consume_empty() - if self._name and (self._what == 'attribute' or self._what == 'data'): + if self._name and self._what in ('attribute', 'data', 'property'): # Implicit stop using StopIteration no longer allowed in # Python 3.7; see PEP 479 res = [] # type: List[str] From 0aed08b5be0244aef3d8bab7e50376fee1450480 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 8 Jun 2019 16:09:45 +0900 Subject: [PATCH 48/84] Fix #6451: autodoc: generates docs for "optional import"ed modules as variables --- CHANGES | 1 + sphinx/pycode/parser.py | 11 +++++++++++ tests/test_pycode_parser.py | 15 +++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/CHANGES b/CHANGES index 7043d835d..5edac25b3 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,7 @@ Bugs fixed immediately preceding section title by pagebreak * #6448: autodoc: crashed when autodocumenting classes with ``__slots__ = None`` * #6452: autosummary: crashed when generating document of properties +* #6451: autodoc: generates docs for "optional import"ed modules as variables Testing -------- diff --git a/sphinx/pycode/parser.py b/sphinx/pycode/parser.py index f9489e91a..1746537bb 100644 --- a/sphinx/pycode/parser.py +++ b/sphinx/pycode/parser.py @@ -357,6 +357,17 @@ class VariableCommentPicker(ast.NodeVisitor): except TypeError: pass # this assignment is not new definition! + def visit_Try(self, node): + # type: (ast.Try) -> None + """Handles Try node and processes body and else-clause. + + .. note:: pycode parser ignores objects definition in except-clause. + """ + for subnode in node.body: + self.visit(subnode) + for subnode in node.orelse: + self.visit(subnode) + def visit_ClassDef(self, node): # type: (ast.ClassDef) -> None """Handles ClassDef node and set context.""" diff --git a/tests/test_pycode_parser.py b/tests/test_pycode_parser.py index ba9778b80..8f8c6cbab 100644 --- a/tests/test_pycode_parser.py +++ b/tests/test_pycode_parser.py @@ -149,6 +149,21 @@ def test_complex_assignment_py3(): assert parser.definitions == {} +def test_assignment_in_try_clause(): + source = ('try:\n' + ' a = None #: comment\n' + 'except:\n' + ' b = None #: ignored\n' + 'else:\n' + ' c = None #: comment\n') + parser = Parser(source) + parser.parse() + assert parser.comments == {('', 'a'): 'comment', + ('', 'c'): 'comment'} + assert parser.deforders == {'a': 0, + 'c': 1} + + def test_obj_assignment(): source = ('obj = SomeObject() #: some object\n' 'obj.attr = 1 #: attr1\n' From 3f5ce569b63163ca4b69aba72ac5cdd445a67dd0 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 8 Jun 2019 19:38:51 +0900 Subject: [PATCH 49/84] Migrate to py3 style type annotation: sphinx.directives.code --- sphinx/directives/code.py | 68 ++++++++++++++------------------------- 1 file changed, 25 insertions(+), 43 deletions(-) diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py index ff21bcdab..aa5192f78 100644 --- a/sphinx/directives/code.py +++ b/sphinx/directives/code.py @@ -9,12 +9,15 @@ import sys import warnings from difflib import unified_diff +from typing import Any, Dict, List, Tuple from docutils import nodes +from docutils.nodes import Element, Node from docutils.parsers.rst import directives from docutils.statemachine import StringList from sphinx import addnodes +from sphinx.config import Config from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.locale import __ from sphinx.util import logging @@ -23,9 +26,7 @@ from sphinx.util.docutils import SphinxDirective if False: # For type annotation - from typing import Any, Dict, List, Tuple # NOQA - from sphinx.application import Sphinx # NOQA - from sphinx.config import Config # NOQA + from sphinx.application import Sphinx logger = logging.getLogger(__name__) @@ -45,8 +46,7 @@ class Highlight(SphinxDirective): 'linenothreshold': directives.positive_int, } - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: language = self.arguments[0].strip() linenothreshold = self.options.get('linenothreshold', sys.maxsize) force = 'force' in self.options @@ -60,16 +60,14 @@ class Highlight(SphinxDirective): class HighlightLang(Highlight): """highlightlang directive (deprecated)""" - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: warnings.warn('highlightlang directive is deprecated. ' 'Please use highlight directive instead.', RemovedInSphinx40Warning, stacklevel=2) return super().run() -def dedent_lines(lines, dedent, location=None): - # type: (List[str], int, Tuple[str, int]) -> List[str] +def dedent_lines(lines: List[str], dedent: int, location: Tuple[str, int] = None) -> List[str]: if not dedent: return lines @@ -86,8 +84,7 @@ def dedent_lines(lines, dedent, location=None): return new_lines -def container_wrapper(directive, literal_node, caption): - # type: (SphinxDirective, nodes.Node, str) -> nodes.container +def container_wrapper(directive: SphinxDirective, literal_node: Node, caption: str) -> nodes.container: # NOQA container_node = nodes.container('', literal_block=True, classes=['literal-block-wrapper']) parsed = nodes.Element() @@ -129,8 +126,7 @@ class CodeBlock(SphinxDirective): 'name': directives.unchanged, } - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: document = self.state.document code = '\n'.join(self.content) location = self.state_machine.get_source_and_line(self.lineno) @@ -157,7 +153,7 @@ class CodeBlock(SphinxDirective): lines = dedent_lines(lines, self.options['dedent'], location=location) code = '\n'.join(lines) - literal = nodes.literal_block(code, code) # type: nodes.Element + literal = nodes.literal_block(code, code) # type: Element if 'linenos' in self.options or 'lineno-start' in self.options: literal['linenos'] = True literal['classes'] += self.options.get('class', []) @@ -209,8 +205,7 @@ class LiteralIncludeReader: ('diff', 'end-at'), ] - def __init__(self, filename, options, config): - # type: (str, Dict, Config) -> None + def __init__(self, filename: str, options: Dict, config: Config) -> None: self.filename = filename self.options = options self.encoding = options.get('encoding', config.source_encoding) @@ -218,15 +213,13 @@ class LiteralIncludeReader: self.parse_options() - def parse_options(self): - # type: () -> None + def parse_options(self) -> None: for option1, option2 in self.INVALID_OPTIONS_PAIR: if option1 in self.options and option2 in self.options: raise ValueError(__('Cannot use both "%s" and "%s" options') % (option1, option2)) - def read_file(self, filename, location=None): - # type: (str, Tuple[str, int]) -> List[str] + def read_file(self, filename: str, location: Tuple[str, int] = None) -> List[str]: try: with open(filename, encoding=self.encoding, errors='strict') as f: text = f.read() @@ -241,8 +234,7 @@ class LiteralIncludeReader: 'be wrong, try giving an :encoding: option') % (self.encoding, filename)) - def read(self, location=None): - # type: (Tuple[str, int]) -> Tuple[str, int] + def read(self, location: Tuple[str, int] = None) -> Tuple[str, int]: if 'diff' in self.options: lines = self.show_diff() else: @@ -259,16 +251,14 @@ class LiteralIncludeReader: return ''.join(lines), len(lines) - def show_diff(self, location=None): - # type: (Tuple[str, int]) -> List[str] + def show_diff(self, location: Tuple[str, int] = None) -> List[str]: new_lines = self.read_file(self.filename) old_filename = self.options.get('diff') old_lines = self.read_file(old_filename) diff = unified_diff(old_lines, new_lines, old_filename, self.filename) return list(diff) - def pyobject_filter(self, lines, location=None): - # type: (List[str], Tuple[str, int]) -> List[str] + def pyobject_filter(self, lines: List[str], location: Tuple[str, int] = None) -> List[str]: pyobject = self.options.get('pyobject') if pyobject: from sphinx.pycode import ModuleAnalyzer @@ -286,8 +276,7 @@ class LiteralIncludeReader: return lines - def lines_filter(self, lines, location=None): - # type: (List[str], Tuple[str, int]) -> List[str] + def lines_filter(self, lines: List[str], location: Tuple[str, int] = None) -> List[str]: linespec = self.options.get('lines') if linespec: linelist = parselinenos(linespec, len(lines)) @@ -311,8 +300,7 @@ class LiteralIncludeReader: return lines - def start_filter(self, lines, location=None): - # type: (List[str], Tuple[str, int]) -> List[str] + def start_filter(self, lines: List[str], location: Tuple[str, int] = None) -> List[str]: if 'start-at' in self.options: start = self.options.get('start-at') inclusive = False @@ -343,8 +331,7 @@ class LiteralIncludeReader: return lines - def end_filter(self, lines, location=None): - # type: (List[str], Tuple[str, int]) -> List[str] + def end_filter(self, lines: List[str], location: Tuple[str, int] = None) -> List[str]: if 'end-at' in self.options: end = self.options.get('end-at') inclusive = True @@ -371,24 +358,21 @@ class LiteralIncludeReader: return lines - def prepend_filter(self, lines, location=None): - # type: (List[str], Tuple[str, int]) -> List[str] + def prepend_filter(self, lines: List[str], location: Tuple[str, int] = None) -> List[str]: prepend = self.options.get('prepend') if prepend: lines.insert(0, prepend + '\n') return lines - def append_filter(self, lines, location=None): - # type: (List[str], Tuple[str, int]) -> List[str] + def append_filter(self, lines: List[str], location: Tuple[str, int] = None) -> List[str]: append = self.options.get('append') if append: lines.append(append + '\n') return lines - def dedent_filter(self, lines, location=None): - # type: (List[str], Tuple[str, int]) -> List[str] + def dedent_filter(self, lines: List[str], location: Tuple[str, int] = None) -> List[str]: if 'dedent' in self.options: return dedent_lines(lines, self.options.get('dedent'), location=location) else: @@ -430,8 +414,7 @@ class LiteralInclude(SphinxDirective): 'diff': directives.unchanged_required, } - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: document = self.state.document if not document.settings.file_insertion_enabled: return [document.reporter.warning('File insertion disabled', @@ -449,7 +432,7 @@ class LiteralInclude(SphinxDirective): reader = LiteralIncludeReader(filename, self.options, self.config) text, lines = reader.read(location=location) - retnode = nodes.literal_block(text, text, source=filename) # type: nodes.Element + retnode = nodes.literal_block(text, text, source=filename) # type: Element retnode['force'] = 'force' in self.options self.set_source_info(retnode) if self.options.get('diff'): # if diff is set, set udiff @@ -483,8 +466,7 @@ class LiteralInclude(SphinxDirective): return [document.reporter.warning(exc, line=self.lineno)] -def setup(app): - # type: (Sphinx) -> Dict[str, Any] +def setup(app: "Sphinx") -> Dict[str, Any]: directives.register_directive('highlight', Highlight) directives.register_directive('highlightlang', HighlightLang) directives.register_directive('code-block', CodeBlock) From e14c76d94228bf01102417317b1c1613b7cebc86 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 8 Jun 2019 19:42:46 +0900 Subject: [PATCH 50/84] Migrate to py3 style type annotation: sphinx.directives.other --- sphinx/directives/other.py | 46 +++++++++++++++----------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index 5863541bf..1113e8241 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -7,9 +7,11 @@ """ import re +from typing import Any, Dict, List from typing import cast from docutils import nodes +from docutils.nodes import Element, Node from docutils.parsers.rst import directives from docutils.parsers.rst.directives.admonitions import BaseAdmonition from docutils.parsers.rst.directives.misc import Class @@ -25,15 +27,13 @@ from sphinx.util.nodes import explicit_title_re, process_index_entry if False: # For type annotation - from typing import Any, Dict, List # NOQA - from sphinx.application import Sphinx # NOQA + from sphinx.application import Sphinx glob_re = re.compile(r'.*[*?\[].*') -def int_or_nothing(argument): - # type: (str) -> int +def int_or_nothing(argument: str) -> int: if not argument: return 999 return int(argument) @@ -60,8 +60,7 @@ class TocTree(SphinxDirective): 'reversed': directives.flag, } - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: subnode = addnodes.toctree() subnode['parent'] = self.env.docname @@ -160,11 +159,10 @@ class Author(SphinxDirective): final_argument_whitespace = True option_spec = {} # type: Dict - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: if not self.config.show_authors: return [] - para = nodes.paragraph(translatable=False) # type: nodes.Element + para = nodes.paragraph(translatable=False) # type: Element emph = nodes.emphasis() para += emph if self.name == 'sectionauthor': @@ -179,7 +177,7 @@ class Author(SphinxDirective): inodes, messages = self.state.inline_text(self.arguments[0], self.lineno) emph.extend(inodes) - ret = [para] # type: List[nodes.Node] + ret = [para] # type: List[Node] ret += messages return ret @@ -194,8 +192,7 @@ class Index(SphinxDirective): final_argument_whitespace = True option_spec = {} # type: Dict - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: arguments = self.arguments[0].split('\n') targetid = 'index-%s' % self.env.new_serialno('index') targetnode = nodes.target('', '', ids=[targetid]) @@ -226,8 +223,7 @@ class TabularColumns(SphinxDirective): final_argument_whitespace = True option_spec = {} # type: Dict - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: node = addnodes.tabular_col_spec() node['spec'] = self.arguments[0] self.set_source_info(node) @@ -244,15 +240,14 @@ class Centered(SphinxDirective): final_argument_whitespace = True option_spec = {} # type: Dict - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: if not self.arguments: return [] - subnode = addnodes.centered() # type: nodes.Element + subnode = addnodes.centered() # type: Element inodes, messages = self.state.inline_text(self.arguments[0], self.lineno) subnode.extend(inodes) - ret = [subnode] # type: List[nodes.Node] + ret = [subnode] # type: List[Node] ret += messages return ret @@ -267,8 +262,7 @@ class Acks(SphinxDirective): final_argument_whitespace = False option_spec = {} # type: Dict - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: node = addnodes.acks() node.document = self.state.document self.state.nested_parse(self.content, self.content_offset, node) @@ -291,8 +285,7 @@ class HList(SphinxDirective): 'columns': int, } - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: ncolumns = self.options.get('columns', 2) node = nodes.paragraph() node.document = self.state.document @@ -325,8 +318,7 @@ class Only(SphinxDirective): final_argument_whitespace = True option_spec = {} # type: Dict - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: node = addnodes.only() node.document = self.state.document self.set_source_info(node) @@ -380,8 +372,7 @@ class Include(BaseInclude, SphinxDirective): "correctly", i.e. relative to source directory. """ - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: if self.arguments[0].startswith('<') and \ self.arguments[0].endswith('>'): # docutils "standard" includes, do not do path processing @@ -392,8 +383,7 @@ class Include(BaseInclude, SphinxDirective): return super().run() -def setup(app): - # type: (Sphinx) -> Dict[str, Any] +def setup(app: "Sphinx") -> Dict[str, Any]: directives.register_directive('toctree', TocTree) directives.register_directive('sectionauthor', Author) directives.register_directive('moduleauthor', Author) From c4dfc7402ed022ecdb1cd85b5f69ef1e583efd29 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 9 Jun 2019 00:23:18 +0900 Subject: [PATCH 51/84] Fix #6436: napoleon: "Unknown target name" error --- CHANGES | 2 ++ sphinx/ext/napoleon/docstring.py | 3 +++ tests/test_ext_napoleon_docstring.py | 4 ++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 7043d835d..a869afb82 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,8 @@ Bugs fixed immediately preceding section title by pagebreak * #6448: autodoc: crashed when autodocumenting classes with ``__slots__ = None`` * #6452: autosummary: crashed when generating document of properties +* #6436: napoleon: "Unknown target name" error if variable name ends with + underscore Testing -------- diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index 0fea99fb8..f57e2481c 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -328,6 +328,9 @@ class GoogleDocstring: def _escape_args_and_kwargs(self, name): # type: (str) -> str + if name.endswith('_'): + name = name[:-1] + r'\_' + if name[:2] == '**': return r'\*\*' + name[2:] elif name[:1] == '*': diff --git a/tests/test_ext_napoleon_docstring.py b/tests/test_ext_napoleon_docstring.py index a333dc47b..f1714bd62 100644 --- a/tests/test_ext_napoleon_docstring.py +++ b/tests/test_ext_napoleon_docstring.py @@ -1351,8 +1351,8 @@ arg_ : type """ expected = """ -:ivar arg_: some description -:vartype arg_: type +:ivar arg\\_: some description +:vartype arg\\_: type """ config = Config(napoleon_use_ivar=True) From 4db7a815289964dc4caab065e326b52ad2ec2144 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 8 Jun 2019 19:44:09 +0900 Subject: [PATCH 52/84] Migrate to py3 style type annotation: sphinx.directives.patches --- sphinx/directives/patches.py | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/sphinx/directives/patches.py b/sphinx/directives/patches.py index 189e1f7e3..b4b1664ab 100644 --- a/sphinx/directives/patches.py +++ b/sphinx/directives/patches.py @@ -6,10 +6,11 @@ :license: BSD, see LICENSE for details. """ +from typing import Any, Dict, List, Tuple from typing import cast from docutils import nodes -from docutils.nodes import make_id +from docutils.nodes import Node, make_id, system_message from docutils.parsers.rst import directives from docutils.parsers.rst.directives import images, html, tables @@ -20,8 +21,7 @@ from sphinx.util.nodes import set_source_info if False: # For type annotation - from typing import Dict, List, Tuple # NOQA - from sphinx.application import Sphinx # NOQA + from sphinx.application import Sphinx class Figure(images.Figure): @@ -29,8 +29,7 @@ class Figure(images.Figure): instead of the image node. """ - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: name = self.options.pop('name', None) result = super().run() if len(result) == 2 or isinstance(result[0], nodes.system_message): @@ -52,8 +51,7 @@ class Figure(images.Figure): class Meta(html.Meta, SphinxDirective): - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: result = super().run() for node in result: if (isinstance(node, nodes.pending) and @@ -74,8 +72,7 @@ class RSTTable(tables.RSTTable): Only for docutils-0.13 or older version.""" - def make_title(self): - # type: () -> Tuple[nodes.title, List[nodes.system_message]] + def make_title(self) -> Tuple[nodes.title, List[system_message]]: title, message = super().make_title() if title: set_source_info(self, title) @@ -88,8 +85,7 @@ class CSVTable(tables.CSVTable): Only for docutils-0.13 or older version.""" - def make_title(self): - # type: () -> Tuple[nodes.title, List[nodes.system_message]] + def make_title(self) -> Tuple[nodes.title, List[system_message]]: title, message = super().make_title() if title: set_source_info(self, title) @@ -102,8 +98,7 @@ class ListTable(tables.ListTable): Only for docutils-0.13 or older version.""" - def make_title(self): - # type: () -> Tuple[nodes.title, List[nodes.system_message]] + def make_title(self) -> Tuple[nodes.title, List[system_message]]: title, message = super().make_title() if title: set_source_info(self, title) @@ -125,8 +120,7 @@ class Code(SphinxDirective): } has_content = True - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: self.assert_has_content() code = '\n'.join(self.content) @@ -169,8 +163,7 @@ class MathDirective(SphinxDirective): 'nowrap': directives.flag, } - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: latex = '\n'.join(self.content) if self.arguments and self.arguments[0]: latex = self.arguments[0] + '\n\n' + latex @@ -184,12 +177,11 @@ class MathDirective(SphinxDirective): self.add_name(node) self.set_source_info(node) - ret = [node] # type: List[nodes.Node] + ret = [node] # type: List[Node] self.add_target(ret) return ret - def add_target(self, ret): - # type: (List[nodes.Node]) -> None + def add_target(self, ret: List[Node]) -> None: node = cast(nodes.math_block, ret[0]) # assign label automatically if math_number_all enabled @@ -216,8 +208,7 @@ class MathDirective(SphinxDirective): self.state_machine.reporter.warning(exc, line=self.lineno) -def setup(app): - # type: (Sphinx) -> Dict +def setup(app: "Sphinx") -> Dict[str, Any]: directives.register_directive('figure', Figure) directives.register_directive('meta', Meta) directives.register_directive('table', RSTTable) From acab4029afe8b3ecafa4a5c7e5f6b51482349cd8 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 8 Jun 2019 19:44:54 +0900 Subject: [PATCH 53/84] Migrate to py3 style type annotation: sphinx.directives --- sphinx/directives/__init__.py | 43 ++++++++++++++--------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py index 40f838c48..13f48e827 100644 --- a/sphinx/directives/__init__.py +++ b/sphinx/directives/__init__.py @@ -9,23 +9,24 @@ """ import re -from typing import List, cast +from typing import Any, Dict, List, Tuple +from typing import cast from docutils import nodes +from docutils.nodes import Node from docutils.parsers.rst import directives, roles from sphinx import addnodes +from sphinx.addnodes import desc_signature from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias from sphinx.util import docutils -from sphinx.util.docfields import DocFieldTransformer, TypedField +from sphinx.util.docfields import DocFieldTransformer, Field, TypedField from sphinx.util.docutils import SphinxDirective +from sphinx.util.typing import DirectiveOption if False: # For type annotation - from typing import Any, Dict, Tuple # NOQA - from sphinx.application import Sphinx # NOQA - from sphinx.util.docfields import Field # NOQA - from sphinx.util.typing import DirectiveOption # NOQA + from sphinx.application import Sphinx # RE to strip backslash escapes @@ -70,8 +71,7 @@ class ObjectDescription(SphinxDirective): # Warning: this might be removed in future version. Don't touch this from extensions. _doc_field_type_map = {} # type: Dict[str, Tuple[Field, bool]] - def get_field_type_map(self): - # type: () -> Dict[str, Tuple[Field, bool]] + def get_field_type_map(self) -> Dict[str, Tuple[Field, bool]]: if self._doc_field_type_map == {}: for field in self.doc_field_types: for name in field.names: @@ -84,8 +84,7 @@ class ObjectDescription(SphinxDirective): return self._doc_field_type_map - def get_signatures(self): - # type: () -> List[str] + def get_signatures(self) -> List[str]: """ Retrieve the signatures to document from the directive arguments. By default, signatures are given as arguments, one per line. @@ -96,8 +95,7 @@ class ObjectDescription(SphinxDirective): # remove backslashes to support (dummy) escapes; helps Vim highlighting return [strip_backslash_re.sub(r'\1', line.strip()) for line in lines] - def handle_signature(self, sig, signode): - # type: (str, addnodes.desc_signature) -> Any + def handle_signature(self, sig: str, signode: desc_signature) -> Any: """ Parse the signature *sig* into individual nodes and append them to *signode*. If ValueError is raised, parsing is aborted and the whole @@ -109,8 +107,7 @@ class ObjectDescription(SphinxDirective): """ raise ValueError - def add_target_and_index(self, name, sig, signode): - # type: (Any, str, addnodes.desc_signature) -> None + def add_target_and_index(self, name: Any, sig: str, signode: desc_signature) -> None: """ Add cross-reference IDs and entries to self.indexnode, if applicable. @@ -118,24 +115,21 @@ class ObjectDescription(SphinxDirective): """ return # do nothing by default - def before_content(self): - # type: () -> None + def before_content(self) -> None: """ Called before parsing content. Used to set information about the current directive context on the build environment. """ pass - def after_content(self): - # type: () -> None + def after_content(self) -> None: """ Called after parsing content. Used to reset information about the current directive context on the build environment. """ pass - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: """ Main directive entry function, called by docutils upon encountering the directive. @@ -212,8 +206,7 @@ class DefaultRole(SphinxDirective): optional_arguments = 1 final_argument_whitespace = False - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: if not self.arguments: docutils.unregister_role('') return [] @@ -244,8 +237,7 @@ class DefaultDomain(SphinxDirective): final_argument_whitespace = False option_spec = {} # type: Dict - def run(self): - # type: () -> List[nodes.Node] + def run(self) -> List[Node]: domain_name = self.arguments[0].lower() # if domain_name not in env.domains: # # try searching by label @@ -294,8 +286,7 @@ deprecated_alias('sphinx.directives', DescDirective = ObjectDescription -def setup(app): - # type: (Sphinx) -> Dict[str, Any] +def setup(app: "Sphinx") -> Dict[str, Any]: directives.register_directive('default-role', DefaultRole) directives.register_directive('default-domain', DefaultDomain) directives.register_directive('describe', ObjectDescription) From f672e00c089947ff97dcf8d5cd85c86495d231d6 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 9 Jun 2019 13:02:20 +0900 Subject: [PATCH 54/84] refactor: correct interface of directive() and role() to docutils' --- sphinx/util/docutils.py | 18 +++++++++--------- sphinx/util/typing.py | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index 42ff5c4b8..b9795e7e4 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -17,7 +17,7 @@ from copy import copy from distutils.version import LooseVersion from os import path from types import ModuleType -from typing import Any, Callable, Dict, Generator, IO, List, Set, Tuple, Type +from typing import Any, Callable, Dict, Generator, IO, List, Optional, Set, Tuple, Type from typing import cast import docutils @@ -194,8 +194,8 @@ class sphinx_domains: self.directive_func = directives.directive self.role_func = roles.role - directives.directive = self.lookup_directive # type: ignore - roles.role = self.lookup_role # type: ignore + directives.directive = self.lookup_directive + roles.role = self.lookup_role def disable(self) -> None: directives.directive = self.directive_func @@ -229,17 +229,17 @@ class sphinx_domains: raise ElementLookupError - def lookup_directive(self, name: str, lang_module: ModuleType, document: nodes.document) -> Tuple[Type[Directive], List[system_message]]: # NOQA + def lookup_directive(self, directive_name: str, language_module: ModuleType, document: nodes.document) -> Tuple[Optional[Type[Directive]], List[system_message]]: # NOQA try: - return self.lookup_domain_element('directive', name) + return self.lookup_domain_element('directive', directive_name) except ElementLookupError: - return self.directive_func(name, lang_module, document) + return self.directive_func(directive_name, language_module, document) - def lookup_role(self, name: str, lang_module: ModuleType, lineno: int, reporter: Reporter) -> Tuple[RoleFunction, List[system_message]]: # NOQA + def lookup_role(self, role_name: str, language_module: ModuleType, lineno: int, reporter: Reporter) -> Tuple[RoleFunction, List[system_message]]: # NOQA try: - return self.lookup_domain_element('role', name) + return self.lookup_domain_element('role', role_name) except ElementLookupError: - return self.role_func(name, lang_module, lineno, reporter) + return self.role_func(role_name, language_module, lineno, reporter) class WarningStream: diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index 77724d38b..67f396e73 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -27,7 +27,7 @@ NoneType = type(None) PathMatcher = Callable[[str], bool] # common role functions -RoleFunction = Callable[[str, str, str, int, Inliner, Dict, List[str]], +RoleFunction = Callable[[str, str, str, int, Inliner, Dict[str, Any], List[str]], Tuple[List[nodes.Node], List[nodes.system_message]]] # title getter functions for enumerable nodes (see sphinx.domains.std) From ec2ee2f7a0cf64370fce62871cf57f589a3d601a Mon Sep 17 00:00:00 2001 From: Bingyao Liu Date: Sun, 9 Jun 2019 21:45:40 +0800 Subject: [PATCH 55/84] docs: Update invalid file path of configuration.rst in devguide --- CONTRIBUTING.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 1807d3998..8c74b184a 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -250,7 +250,7 @@ the source and the compiled catalogs. When a new locale is submitted, add a new directory with the ISO 639-1 language identifier and put ``sphinx.po`` in there. Don't forget to update the possible -values for :confval:`language` in ``doc/config.rst``. +values for :confval:`language` in ``doc/usage/configuration.rst``. The Sphinx core messages can also be translated on `Transifex `_. There exists a client tool named ``tx`` in the From 34acdc37aaa7a07bbd3792cf8836eca2e42d1cdd Mon Sep 17 00:00:00 2001 From: Bingyao Liu Date: Sun, 9 Jun 2019 21:47:43 +0800 Subject: [PATCH 56/84] Fix #6464: updated invalid links of language codes in quickstart and all locale po files --- sphinx/cmd/quickstart.py | 2 +- sphinx/locale/ar/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/bn/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/ca/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/cak/LC_MESSAGES/sphinx.po | 4 ++-- sphinx/locale/cs/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/cy/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/da/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/de/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/el/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/eo/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/es/LC_MESSAGES/sphinx.po | 4 ++-- sphinx/locale/et/LC_MESSAGES/sphinx.po | 4 ++-- sphinx/locale/eu/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/fa/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/fi/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/fr/LC_MESSAGES/sphinx.po | 4 ++-- sphinx/locale/he/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/hi/LC_MESSAGES/sphinx.po | 4 ++-- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/hr/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/hu/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/id/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/it/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/ja/LC_MESSAGES/sphinx.po | 4 ++-- sphinx/locale/ko/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/lt/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/lv/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/mk/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/ne/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/nl/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/pl/LC_MESSAGES/sphinx.po | 4 ++-- sphinx/locale/pt/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/ro/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/ru/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/si/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/sk/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/sl/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/sphinx.pot | 2 +- sphinx/locale/sr/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/sr@latin/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/sr_RS/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/sv/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/ta/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/tr/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/ur/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/vi/LC_MESSAGES/sphinx.po | 2 +- sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po | 4 ++-- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po | 2 +- 53 files changed, 61 insertions(+), 61 deletions(-) diff --git a/sphinx/cmd/quickstart.py b/sphinx/cmd/quickstart.py index 49abc2fa1..2a509bacb 100644 --- a/sphinx/cmd/quickstart.py +++ b/sphinx/cmd/quickstart.py @@ -310,7 +310,7 @@ you can select a language here by its language code. Sphinx will then translate text that it generates into that language. For a list of supported codes, see -http://sphinx-doc.org/config.html#confval-language.''')) +https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language.''')) d['language'] = do_prompt(__('Project language'), 'en') if d['language'] == 'en': d['language'] = None diff --git a/sphinx/locale/ar/LC_MESSAGES/sphinx.po b/sphinx/locale/ar/LC_MESSAGES/sphinx.po index 2452773d1..48df63fc1 100644 --- a/sphinx/locale/ar/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/ar/LC_MESSAGES/sphinx.po @@ -1448,7 +1448,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/bn/LC_MESSAGES/sphinx.po b/sphinx/locale/bn/LC_MESSAGES/sphinx.po index 897bc0d6b..89c81694d 100644 --- a/sphinx/locale/bn/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/bn/LC_MESSAGES/sphinx.po @@ -1448,7 +1448,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/ca/LC_MESSAGES/sphinx.po b/sphinx/locale/ca/LC_MESSAGES/sphinx.po index 16c5dd220..dcb638253 100644 --- a/sphinx/locale/ca/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/ca/LC_MESSAGES/sphinx.po @@ -1448,7 +1448,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/cak/LC_MESSAGES/sphinx.po b/sphinx/locale/cak/LC_MESSAGES/sphinx.po index 0c652d72f..338d62059 100644 --- a/sphinx/locale/cak/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/cak/LC_MESSAGES/sphinx.po @@ -1448,8 +1448,8 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." -msgstr "\nWe xetz'ib'an ri wuj pa jun ch'abäl man Q'anch' ta,\nyatikïr nacha' jun chïk runuk'unem ch'ab'äl wawe' chi Sphinx tiq'ax\nruch'abäl ri wuj xtik'iyij pa ri ch'ab'äl re.\n\nChi natz'u rucholajem runuk'unem ch'ab'äl k'o chïk pa Sphinx, tab'e pa\nhttp://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." +msgstr "\nWe xetz'ib'an ri wuj pa jun ch'abäl man Q'anch' ta,\nyatikïr nacha' jun chïk runuk'unem ch'ab'äl wawe' chi Sphinx tiq'ax\nruch'abäl ri wuj xtik'iyij pa ri ch'ab'äl re.\n\nChi natz'u rucholajem runuk'unem ch'ab'äl k'o chïk pa Sphinx, tab'e pa\nhttps://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." #: sphinx/cmd/quickstart.py:314 msgid "Project language" diff --git a/sphinx/locale/cs/LC_MESSAGES/sphinx.po b/sphinx/locale/cs/LC_MESSAGES/sphinx.po index ed2dbfe76..825dc2166 100644 --- a/sphinx/locale/cs/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/cs/LC_MESSAGES/sphinx.po @@ -1449,7 +1449,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/cy/LC_MESSAGES/sphinx.po b/sphinx/locale/cy/LC_MESSAGES/sphinx.po index bc00d3b12..67faadf88 100644 --- a/sphinx/locale/cy/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/cy/LC_MESSAGES/sphinx.po @@ -1449,7 +1449,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/da/LC_MESSAGES/sphinx.po b/sphinx/locale/da/LC_MESSAGES/sphinx.po index b7d6f2a4e..721553d0f 100644 --- a/sphinx/locale/da/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/da/LC_MESSAGES/sphinx.po @@ -1450,7 +1450,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/de/LC_MESSAGES/sphinx.po b/sphinx/locale/de/LC_MESSAGES/sphinx.po index 96b24539d..cac01042e 100644 --- a/sphinx/locale/de/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/de/LC_MESSAGES/sphinx.po @@ -1451,7 +1451,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/el/LC_MESSAGES/sphinx.po b/sphinx/locale/el/LC_MESSAGES/sphinx.po index 2586e7e27..54a19f5fd 100644 --- a/sphinx/locale/el/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/el/LC_MESSAGES/sphinx.po @@ -1448,7 +1448,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/eo/LC_MESSAGES/sphinx.po b/sphinx/locale/eo/LC_MESSAGES/sphinx.po index 62b11e910..bd6def59d 100644 --- a/sphinx/locale/eo/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/eo/LC_MESSAGES/sphinx.po @@ -1448,7 +1448,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/es/LC_MESSAGES/sphinx.po b/sphinx/locale/es/LC_MESSAGES/sphinx.po index 1b877fdd1..a54f40ef8 100644 --- a/sphinx/locale/es/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/es/LC_MESSAGES/sphinx.po @@ -1454,8 +1454,8 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." -msgstr "\nSi los documentos están escritos en un idioma distinto al Inglés,\npuedes seleccionar un idioma aqui a través de su código. Sphinx traducirá\nlos textos que genere en dicho idioma.\n\nPara una lista de los códigos soportados visita: \nhttp://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." +msgstr "\nSi los documentos están escritos en un idioma distinto al Inglés,\npuedes seleccionar un idioma aqui a través de su código. Sphinx traducirá\nlos textos que genere en dicho idioma.\n\nPara una lista de los códigos soportados visita: \nhttps://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." #: sphinx/cmd/quickstart.py:314 msgid "Project language" diff --git a/sphinx/locale/et/LC_MESSAGES/sphinx.po b/sphinx/locale/et/LC_MESSAGES/sphinx.po index 2237960ae..abc73f875 100644 --- a/sphinx/locale/et/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/et/LC_MESSAGES/sphinx.po @@ -1451,8 +1451,8 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." -msgstr "\nKui su dokumendid pole kirjutatud inglise keeles, siis võid siin määrata\nkeele vastava keelekoodi abil. Sel juhul tõlgib Sphinx enda genereeritud\nteksti vastavasse keelde.\n\nToetatud keelekoodide kohta vaata\nhttp://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." +msgstr "\nKui su dokumendid pole kirjutatud inglise keeles, siis võid siin määrata\nkeele vastava keelekoodi abil. Sel juhul tõlgib Sphinx enda genereeritud\nteksti vastavasse keelde.\n\nToetatud keelekoodide kohta vaata\nhttps://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." #: sphinx/cmd/quickstart.py:314 msgid "Project language" diff --git a/sphinx/locale/eu/LC_MESSAGES/sphinx.po b/sphinx/locale/eu/LC_MESSAGES/sphinx.po index 6ad672fa5..02ac72526 100644 --- a/sphinx/locale/eu/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/eu/LC_MESSAGES/sphinx.po @@ -1449,7 +1449,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/fa/LC_MESSAGES/sphinx.po b/sphinx/locale/fa/LC_MESSAGES/sphinx.po index c80f85f81..e9d5a9934 100644 --- a/sphinx/locale/fa/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/fa/LC_MESSAGES/sphinx.po @@ -1447,7 +1447,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/fi/LC_MESSAGES/sphinx.po b/sphinx/locale/fi/LC_MESSAGES/sphinx.po index ee28d41a7..d9832746a 100644 --- a/sphinx/locale/fi/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/fi/LC_MESSAGES/sphinx.po @@ -1448,7 +1448,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/fr/LC_MESSAGES/sphinx.po b/sphinx/locale/fr/LC_MESSAGES/sphinx.po index 6762dc21e..0516db6d5 100644 --- a/sphinx/locale/fr/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/fr/LC_MESSAGES/sphinx.po @@ -1465,8 +1465,8 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." -msgstr "\nSi le documents doit être écrit dans une autre langue que l'anglais, vous pouvez choisir une langue ici en saisissant son code. Sphinx traduira le texte qu'il génère dans cette langue. Pour une liste des codes supportés : http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." +msgstr "\nSi le documents doit être écrit dans une autre langue que l'anglais, vous pouvez choisir une langue ici en saisissant son code. Sphinx traduira le texte qu'il génère dans cette langue. Pour une liste des codes supportés : https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." #: sphinx/cmd/quickstart.py:314 msgid "Project language" diff --git a/sphinx/locale/he/LC_MESSAGES/sphinx.po b/sphinx/locale/he/LC_MESSAGES/sphinx.po index 2e222d711..7cf0d6e87 100644 --- a/sphinx/locale/he/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/he/LC_MESSAGES/sphinx.po @@ -1448,7 +1448,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/hi/LC_MESSAGES/sphinx.po b/sphinx/locale/hi/LC_MESSAGES/sphinx.po index 6a95b6be4..0668fb5b9 100644 --- a/sphinx/locale/hi/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/hi/LC_MESSAGES/sphinx.po @@ -1449,8 +1449,8 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." -msgstr "\nयदि प्रलेखों को अंग्रेजी के अलावा अन्य किसी भाषा में लिखा जाना है,\nतो यहाँ पर आप भाषा का कूटशब्द दे सकते हैं. स्फिंक्स तदपुरांत,\nजो वाक्यांश बनाता है उसे उस भाषा में अनुवादित करेगा.\n\nमान्य भाषा कूटशब्द सूची यहाँ पर देखें\nhttp://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." +msgstr "\nयदि प्रलेखों को अंग्रेजी के अलावा अन्य किसी भाषा में लिखा जाना है,\nतो यहाँ पर आप भाषा का कूटशब्द दे सकते हैं. स्फिंक्स तदपुरांत,\nजो वाक्यांश बनाता है उसे उस भाषा में अनुवादित करेगा.\n\nमान्य भाषा कूटशब्द सूची यहाँ पर देखें\nhttps://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." #: sphinx/cmd/quickstart.py:314 msgid "Project language" diff --git a/sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po b/sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po index 3168863d3..81f7b7cfc 100644 --- a/sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po @@ -1447,7 +1447,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/hr/LC_MESSAGES/sphinx.po b/sphinx/locale/hr/LC_MESSAGES/sphinx.po index df4eea180..71fa20ac8 100644 --- a/sphinx/locale/hr/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/hr/LC_MESSAGES/sphinx.po @@ -1448,7 +1448,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/hu/LC_MESSAGES/sphinx.po b/sphinx/locale/hu/LC_MESSAGES/sphinx.po index 8d172470f..c64a9a59c 100644 --- a/sphinx/locale/hu/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/hu/LC_MESSAGES/sphinx.po @@ -1452,7 +1452,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/id/LC_MESSAGES/sphinx.po b/sphinx/locale/id/LC_MESSAGES/sphinx.po index f4ae7cc2c..bd323c2fb 100644 --- a/sphinx/locale/id/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/id/LC_MESSAGES/sphinx.po @@ -1451,7 +1451,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/it/LC_MESSAGES/sphinx.po b/sphinx/locale/it/LC_MESSAGES/sphinx.po index baec45df5..19a951be0 100644 --- a/sphinx/locale/it/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/it/LC_MESSAGES/sphinx.po @@ -1452,7 +1452,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/ja/LC_MESSAGES/sphinx.po b/sphinx/locale/ja/LC_MESSAGES/sphinx.po index 01a10f634..93f03c07f 100644 --- a/sphinx/locale/ja/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/ja/LC_MESSAGES/sphinx.po @@ -1460,8 +1460,8 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." -msgstr "\nドキュメントを英語以外の言語で書く場合は、\n 言語コードで言語を選択できます。Sphinx は生成したテキストをその言語に翻訳します。\n\nサポートされているコードのリストについては、\nhttp://sphinx-doc.org/config.html#confval-language を参照してください。" +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." +msgstr "\nドキュメントを英語以外の言語で書く場合は、\n 言語コードで言語を選択できます。Sphinx は生成したテキストをその言語に翻訳します。\n\nサポートされているコードのリストについては、\nhttps://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language を参照してください。" #: sphinx/cmd/quickstart.py:314 msgid "Project language" diff --git a/sphinx/locale/ko/LC_MESSAGES/sphinx.po b/sphinx/locale/ko/LC_MESSAGES/sphinx.po index b88f11d2a..50a7f7448 100644 --- a/sphinx/locale/ko/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/ko/LC_MESSAGES/sphinx.po @@ -1448,7 +1448,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/lt/LC_MESSAGES/sphinx.po b/sphinx/locale/lt/LC_MESSAGES/sphinx.po index 9d9707c71..908d02a22 100644 --- a/sphinx/locale/lt/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/lt/LC_MESSAGES/sphinx.po @@ -1448,7 +1448,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/lv/LC_MESSAGES/sphinx.po b/sphinx/locale/lv/LC_MESSAGES/sphinx.po index bcfda0332..4b0385fb3 100644 --- a/sphinx/locale/lv/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/lv/LC_MESSAGES/sphinx.po @@ -1447,7 +1447,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/mk/LC_MESSAGES/sphinx.po b/sphinx/locale/mk/LC_MESSAGES/sphinx.po index fc4a16386..4a17380ca 100644 --- a/sphinx/locale/mk/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/mk/LC_MESSAGES/sphinx.po @@ -1448,7 +1448,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po b/sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po index 77db4ca5a..53b038c25 100644 --- a/sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po @@ -1447,7 +1447,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/ne/LC_MESSAGES/sphinx.po b/sphinx/locale/ne/LC_MESSAGES/sphinx.po index e87d8a887..5949d78a9 100644 --- a/sphinx/locale/ne/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/ne/LC_MESSAGES/sphinx.po @@ -1449,7 +1449,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/nl/LC_MESSAGES/sphinx.po b/sphinx/locale/nl/LC_MESSAGES/sphinx.po index a52f5ecaa..02b3d2786 100644 --- a/sphinx/locale/nl/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/nl/LC_MESSAGES/sphinx.po @@ -1453,7 +1453,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/pl/LC_MESSAGES/sphinx.po b/sphinx/locale/pl/LC_MESSAGES/sphinx.po index 6128a6de6..82903c2de 100644 --- a/sphinx/locale/pl/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/pl/LC_MESSAGES/sphinx.po @@ -1451,8 +1451,8 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." -msgstr "\nJeśli dokumenty mają być pisane w języku innym niż angielski,\nmożesz tutaj wybrać język przez jego kod. Sphinx następnie\nprzetłumaczy tekst, który generuje, na ten język.\n\nListę wspieranych kodów znajdziesz na\nhttp://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." +msgstr "\nJeśli dokumenty mają być pisane w języku innym niż angielski,\nmożesz tutaj wybrać język przez jego kod. Sphinx następnie\nprzetłumaczy tekst, który generuje, na ten język.\n\nListę wspieranych kodów znajdziesz na\nhttps://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." #: sphinx/cmd/quickstart.py:314 msgid "Project language" diff --git a/sphinx/locale/pt/LC_MESSAGES/sphinx.po b/sphinx/locale/pt/LC_MESSAGES/sphinx.po index 058d87dde..47ce06ee0 100644 --- a/sphinx/locale/pt/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/pt/LC_MESSAGES/sphinx.po @@ -1447,7 +1447,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po index e54e9288d..4715e32e7 100644 --- a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po @@ -1451,7 +1451,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po b/sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po index b896e2779..75e9f3135 100644 --- a/sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po @@ -1449,7 +1449,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/ro/LC_MESSAGES/sphinx.po b/sphinx/locale/ro/LC_MESSAGES/sphinx.po index a0a850740..1ac7ef043 100644 --- a/sphinx/locale/ro/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/ro/LC_MESSAGES/sphinx.po @@ -1449,7 +1449,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/ru/LC_MESSAGES/sphinx.po b/sphinx/locale/ru/LC_MESSAGES/sphinx.po index 4ccab39b5..f9cdfba3a 100644 --- a/sphinx/locale/ru/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/ru/LC_MESSAGES/sphinx.po @@ -1453,7 +1453,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/si/LC_MESSAGES/sphinx.po b/sphinx/locale/si/LC_MESSAGES/sphinx.po index 5852306a2..59e13eaed 100644 --- a/sphinx/locale/si/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/si/LC_MESSAGES/sphinx.po @@ -1448,7 +1448,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/sk/LC_MESSAGES/sphinx.po b/sphinx/locale/sk/LC_MESSAGES/sphinx.po index 70275b891..90213cf37 100644 --- a/sphinx/locale/sk/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/sk/LC_MESSAGES/sphinx.po @@ -1450,7 +1450,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/sl/LC_MESSAGES/sphinx.po b/sphinx/locale/sl/LC_MESSAGES/sphinx.po index 0353526c0..3d94d0f54 100644 --- a/sphinx/locale/sl/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/sl/LC_MESSAGES/sphinx.po @@ -1447,7 +1447,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/sphinx.pot b/sphinx/locale/sphinx.pot index 07f4216c4..c4f9a54fb 100644 --- a/sphinx/locale/sphinx.pot +++ b/sphinx/locale/sphinx.pot @@ -1461,7 +1461,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/sr/LC_MESSAGES/sphinx.po b/sphinx/locale/sr/LC_MESSAGES/sphinx.po index e13bb82d9..1e99514a3 100644 --- a/sphinx/locale/sr/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/sr/LC_MESSAGES/sphinx.po @@ -1448,7 +1448,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/sr@latin/LC_MESSAGES/sphinx.po b/sphinx/locale/sr@latin/LC_MESSAGES/sphinx.po index 941497ee1..0fe6b5ca7 100644 --- a/sphinx/locale/sr@latin/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/sr@latin/LC_MESSAGES/sphinx.po @@ -1447,7 +1447,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/sr_RS/LC_MESSAGES/sphinx.po b/sphinx/locale/sr_RS/LC_MESSAGES/sphinx.po index 3e78b6f2b..8c95c6cd9 100644 --- a/sphinx/locale/sr_RS/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/sr_RS/LC_MESSAGES/sphinx.po @@ -1447,7 +1447,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/sv/LC_MESSAGES/sphinx.po b/sphinx/locale/sv/LC_MESSAGES/sphinx.po index 8f26be6f1..d6004ba71 100644 --- a/sphinx/locale/sv/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/sv/LC_MESSAGES/sphinx.po @@ -1447,7 +1447,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/ta/LC_MESSAGES/sphinx.po b/sphinx/locale/ta/LC_MESSAGES/sphinx.po index 9d42b0728..ef9ac7d92 100644 --- a/sphinx/locale/ta/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/ta/LC_MESSAGES/sphinx.po @@ -1448,7 +1448,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/tr/LC_MESSAGES/sphinx.po b/sphinx/locale/tr/LC_MESSAGES/sphinx.po index 4c1f310c4..c6dac42b8 100644 --- a/sphinx/locale/tr/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/tr/LC_MESSAGES/sphinx.po @@ -1449,7 +1449,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po b/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po index 9506624c0..fd08c96bf 100644 --- a/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po @@ -1448,7 +1448,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/ur/LC_MESSAGES/sphinx.po b/sphinx/locale/ur/LC_MESSAGES/sphinx.po index 1464969b7..975cd09ca 100644 --- a/sphinx/locale/ur/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/ur/LC_MESSAGES/sphinx.po @@ -1447,7 +1447,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/vi/LC_MESSAGES/sphinx.po b/sphinx/locale/vi/LC_MESSAGES/sphinx.po index 01bae6d8c..f2e3e766b 100644 --- a/sphinx/locale/vi/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/vi/LC_MESSAGES/sphinx.po @@ -1448,7 +1448,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 diff --git a/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po b/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po index a5c28080b..72429ac95 100644 --- a/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po @@ -1456,8 +1456,8 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." -msgstr "\n如果用英语以外的语言编写文档,你可以在此按语言代码选择语种。\nSphinx 会把内置文本翻译成相应语言的版本。\n\n支持的语言代码列表见:\nhttp://sphinx-doc.org/config.html#confval-language。" +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." +msgstr "\n如果用英语以外的语言编写文档,你可以在此按语言代码选择语种。\nSphinx 会把内置文本翻译成相应语言的版本。\n\n支持的语言代码列表见:\nhttps://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language。" #: sphinx/cmd/quickstart.py:314 msgid "Project language" diff --git a/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po b/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po index cafadd80a..ebe07553b 100644 --- a/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po @@ -1453,7 +1453,7 @@ msgid "" "translate text that it generates into that language.\n" "\n" "For a list of supported codes, see\n" -"http://sphinx-doc.org/config.html#confval-language." +"https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language." msgstr "" #: sphinx/cmd/quickstart.py:314 From c3b93c15c27cc4553c26673311243aea24bffb0b Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 10 Jun 2019 01:42:14 +0900 Subject: [PATCH 57/84] Update CHANGES for PR #6445 --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index 12ebc4975..1c5749867 100644 --- a/CHANGES +++ b/CHANGES @@ -23,6 +23,7 @@ Bugs fixed * #6455: napoleon: docstrings for properties are not processed * #6436: napoleon: "Unknown target name" error if variable name ends with underscore +* #6440: apidoc: missing blank lines between modules Testing -------- From 649ebea438761e5d44bb7d52db7c3291b7f250df Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 9 Jun 2019 03:00:03 +0900 Subject: [PATCH 58/84] autosummary: deprecate info and warn arguments for generate_autosummary_docs() --- CHANGES | 5 +++++ doc/extdev/deprecated.rst | 16 ++++++++++++++++ sphinx/ext/autosummary/__init__.py | 1 - sphinx/ext/autosummary/generate.py | 25 ++++++++++++++++++++++--- 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index eb530f81d..ab0979dac 100644 --- a/CHANGES +++ b/CHANGES @@ -10,6 +10,11 @@ Incompatible changes Deprecated ---------- +* The ``info`` and ``warn`` arguments of + ``sphinx.ext.autosummary.generate.generate_autosummary_docs()`` +* ``sphinx.ext.autosummary.generate._simple_info()`` +* ``sphinx.ext.autosummary.generate._simple_warn()`` + Features added -------------- diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst index 38fae288c..c40ffb27f 100644 --- a/doc/extdev/deprecated.rst +++ b/doc/extdev/deprecated.rst @@ -26,6 +26,22 @@ The following is a list of deprecated interfaces. - (will be) Removed - Alternatives + * - The ``info`` and ``warn`` arguments of + ``sphinx.ext.autosummary.generate.generate_autosummary_docs()`` + - 2.2 + - 4.0 + - ``logging.info()`` and ``logging.warning()`` + + * - ``sphinx.ext.autosummary.generate._simple_info()`` + - 2.2 + - 4.0 + - ``logging.info()`` + + * - ``sphinx.ext.autosummary.generate._simple_warn()`` + - 2.2 + - 4.0 + - ``logging.warning()`` + * - ``sphinx.builders.latex.LaTeXBuilder.apply_transforms()`` - 2.1 - 4.0 diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 262b36cea..6eb0fea9b 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -754,7 +754,6 @@ def process_generate_options(app): imported_members = app.config.autosummary_imported_members with mock(app.config.autosummary_mock_imports): generate_autosummary_docs(genfiles, builder=app.builder, - warn=logger.warning, info=logger.info, suffix=suffix, base_path=app.srcdir, app=app, imported_members=imported_members) diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 1bfbb0da4..4a699dd9f 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -23,6 +23,7 @@ import os import pydoc import re import sys +import warnings from jinja2 import BaseLoader, FileSystemLoader, TemplateNotFound from jinja2.sandbox import SandboxedEnvironment @@ -30,10 +31,12 @@ from jinja2.sandbox import SandboxedEnvironment import sphinx.locale from sphinx import __display_version__ from sphinx import package_dir +from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.ext.autosummary import import_by_name, get_documenter from sphinx.jinja2glue import BuiltinTemplateLoader from sphinx.locale import __ from sphinx.registry import SphinxComponentRegistry +from sphinx.util import logging from sphinx.util import rst from sphinx.util.inspect import safe_getattr from sphinx.util.osutil import ensuredir @@ -45,12 +48,17 @@ if False: from sphinx.ext.autodoc import Documenter # NOQA +logger = logging.getLogger(__name__) + + class DummyApplication: """Dummy Application class for sphinx-autogen command.""" def __init__(self): # type: () -> None self.registry = SphinxComponentRegistry() + self.messagelog = [] # type: List[str] + self.verbosity = 0 def setup_documenters(app): @@ -127,10 +135,20 @@ class AutosummaryRenderer: # -- Generating output --------------------------------------------------------- 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): + warn=None, info=None, base_path=None, builder=None, + template_dir=None, imported_members=False, app=None): # type: (List[str], str, str, Callable, Callable, str, Builder, str, bool, Any) -> None + if info: + warnings.warn('info argument for generate_autosummary_docs() is deprecated.', + RemovedInSphinx40Warning) + else: + info = logger.info + + if warn: + warnings.warn('warn argument for generate_autosummary_docs() is deprecated.', + RemovedInSphinx40Warning) + else: + warn = logger.warning showed_sources = list(sorted(sources)) if len(showed_sources) > 20: @@ -420,6 +438,7 @@ def main(argv=sys.argv[1:]): sphinx.locale.init_console(os.path.join(package_dir, 'locale'), 'sphinx') app = DummyApplication() + logging.setup(app, sys.stdout, sys.stderr) # type: ignore setup_documenters(app) args = get_parser().parse_args(argv) generate_autosummary_docs(args.source_file, args.output_dir, From 21be789d4d539378df96523c94934b0e05c363de Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 10 Jun 2019 21:39:07 +0900 Subject: [PATCH 59/84] Revert "Fix #1063: autodoc: automodule directive handles undocumented module level variables" This reverts commit 69d93c967c591a65f10bcb9cb07e03d5cf03cce6 (without CHANGES). --- sphinx/ext/autodoc/__init__.py | 13 ++-------- tests/roots/test-ext-autodoc/target/module.py | 4 ---- tests/test_autodoc.py | 24 ------------------- 3 files changed, 2 insertions(+), 39 deletions(-) delete mode 100644 tests/roots/test-ext-autodoc/target/module.py diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index f66269a6b..7172553ac 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -549,10 +549,8 @@ class Documenter: if self.analyzer: attr_docs = self.analyzer.find_attr_docs() - tagorder = self.analyzer.tagorder else: attr_docs = {} - tagorder = {} # process members and determine which to skip for (membername, member) in members: @@ -582,13 +580,12 @@ class Documenter: membername in self.options.special_members: keep = has_doc or self.options.undoc_members elif (namespace, membername) in attr_docs: - has_doc = bool(attr_docs[namespace, membername]) if want_all and membername.startswith('_'): # ignore members whose name starts with _ by default - keep = has_doc and self.options.private_members + keep = self.options.private_members else: # keep documented attributes - keep = has_doc + keep = True isattr = True elif want_all and membername.startswith('_'): # ignore members whose name starts with _ by default @@ -597,8 +594,6 @@ class Documenter: else: # ignore undocumented members if :undoc-members: is not given keep = has_doc or self.options.undoc_members - # module top level item or not - isattr = membername in tagorder # give the user a chance to decide whether this member # should be skipped @@ -1296,10 +1291,6 @@ class DataDocumenter(ModuleLevelDocumenter): return self.get_attr(self.parent or self.object, '__module__', None) \ or self.modname - def get_doc(self, encoding=None, ignore=1): - # type: (str, int) -> List[List[str]] - return [] - class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: ignore """ diff --git a/tests/roots/test-ext-autodoc/target/module.py b/tests/roots/test-ext-autodoc/target/module.py deleted file mode 100644 index d5b557d94..000000000 --- a/tests/roots/test-ext-autodoc/target/module.py +++ /dev/null @@ -1,4 +0,0 @@ -#: docstring for CONSTANT1 -CONSTANT1 = "" - -CONSTANT2 = "" diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 518d23e8c..f6f5a618c 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -1689,30 +1689,6 @@ def test_partialmethod(app): assert list(actual) == expected -@pytest.mark.usefixtures('setup_test') -def test_module_variables(): - options = {"members": None, - "undoc-members": True} - actual = do_autodoc(app, 'module', 'target.module', options) - assert list(actual) == [ - '', - '.. py:module:: target.module', - '', - '', - '.. py:data:: CONSTANT1', - ' :module: target.module', - " :annotation: = ''", - '', - ' docstring for CONSTANT1', - ' ', - '', - '.. py:data:: CONSTANT2', - ' :module: target.module', - " :annotation: = ''", - '', - ] - - @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_typehints_signature(app): app.config.autodoc_typehints = "signature" From 8828017f6c08a756decffe1cd496b7083dded322 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 10 Jun 2019 21:42:51 +0900 Subject: [PATCH 60/84] Update CHANGES --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index 4df249167..a1a1e7e79 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,8 @@ Dependencies Incompatible changes -------------------- +* #6447: autodoc: Stop to generate document for undocumented module variables + Deprecated ---------- From afa4a772da3cb5218360b9e54490f49acab3bdbe Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 10 Jun 2019 22:34:45 +0900 Subject: [PATCH 61/84] Bump to 2.1.1 final --- CHANGES | 16 ++-------------- sphinx/__init__.py | 4 ++-- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/CHANGES b/CHANGES index a1a1e7e79..6a4492639 100644 --- a/CHANGES +++ b/CHANGES @@ -1,20 +1,11 @@ -Release 2.1.1 (in development) -============================== - -Dependencies ------------- +Release 2.1.1 (released Jun 10, 2019) +===================================== Incompatible changes -------------------- * #6447: autodoc: Stop to generate document for undocumented module variables -Deprecated ----------- - -Features added --------------- - Bugs fixed ---------- @@ -28,9 +19,6 @@ Bugs fixed underscore * #6440: apidoc: missing blank lines between modules -Testing --------- - Release 2.1.0 (released Jun 02, 2019) ===================================== diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 8a6bdcc8b..15a18de83 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -32,7 +32,7 @@ if 'PYTHONWARNINGS' not in os.environ: warnings.filterwarnings('ignore', "'U' mode is deprecated", DeprecationWarning, module='docutils.io') -__version__ = '2.1.1+' +__version__ = '2.1.1' __released__ = '2.1.1' # used when Sphinx builds its own docs #: Version info for better programmatic use. @@ -43,7 +43,7 @@ __released__ = '2.1.1' # used when Sphinx builds its own docs #: #: .. versionadded:: 1.2 #: Before version 1.2, check the string ``sphinx.__version__``. -version_info = (2, 1, 1, 'beta', 0) +version_info = (2, 1, 1, 'final', 0) package_dir = path.abspath(path.dirname(__file__)) From 5b307fcf4f73517e79352b6b6857c35f4f1783ba Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 10 Jun 2019 22:36:32 +0900 Subject: [PATCH 62/84] Bump version --- CHANGES | 21 +++++++++++++++++++++ sphinx/__init__.py | 6 +++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 6a4492639..937e6133f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,24 @@ +Release 2.1.2 (in development) +============================== + +Dependencies +------------ + +Incompatible changes +-------------------- + +Deprecated +---------- + +Features added +-------------- + +Bugs fixed +---------- + +Testing +-------- + Release 2.1.1 (released Jun 10, 2019) ===================================== diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 15a18de83..094884e5b 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -32,8 +32,8 @@ if 'PYTHONWARNINGS' not in os.environ: warnings.filterwarnings('ignore', "'U' mode is deprecated", DeprecationWarning, module='docutils.io') -__version__ = '2.1.1' -__released__ = '2.1.1' # used when Sphinx builds its own docs +__version__ = '2.1.2+' +__released__ = '2.1.2' # used when Sphinx builds its own docs #: Version info for better programmatic use. #: @@ -43,7 +43,7 @@ __released__ = '2.1.1' # used when Sphinx builds its own docs #: #: .. versionadded:: 1.2 #: Before version 1.2, check the string ``sphinx.__version__``. -version_info = (2, 1, 1, 'final', 0) +version_info = (2, 1, 2, 'beta', 0) package_dir = path.abspath(path.dirname(__file__)) From cc3022b7f059333c0892f0c37a28db19a5e26e8d Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 9 Jun 2019 00:56:52 +0900 Subject: [PATCH 63/84] Migrate to py3 style type annotation: sphinx.builders.html --- sphinx/builders/html.py | 218 ++++++++++++++-------------------------- 1 file changed, 77 insertions(+), 141 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 504bbc598..1fd9e6cac 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -15,16 +15,21 @@ import sys import warnings from hashlib import md5 from os import path +from typing import Any, Dict, IO, Iterable, Iterator, List, Set, Type, Tuple from docutils import nodes from docutils.core import publish_parts from docutils.frontend import OptionParser from docutils.io import DocTreeInput, StringOutput +from docutils.nodes import Node from docutils.utils import relative_path from sphinx import package_dir, __display_version__ +from sphinx.application import Sphinx from sphinx.builders import Builder +from sphinx.config import Config from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning +from sphinx.domains import Domain, Index, IndexEntry from sphinx.environment.adapters.asset import ImageAdapter from sphinx.environment.adapters.indexentries import IndexEntries from sphinx.environment.adapters.toctree import TocTree @@ -41,16 +46,9 @@ from sphinx.util.i18n import format_date from sphinx.util.inventory import InventoryFile from sphinx.util.matching import patmatch, Matcher, DOTFILES from sphinx.util.osutil import os_path, relative_uri, ensuredir, movefile, copyfile +from sphinx.util.tags import Tags from sphinx.writers.html import HTMLWriter, HTMLTranslator -if False: - # For type annotation - from typing import Any, Dict, IO, Iterable, Iterator, List, Set, Type, Tuple # NOQA - from sphinx.application import Sphinx # NOQA - from sphinx.config import Config # NOQA - from sphinx.domains import Domain, Index, IndexEntry # NOQA - from sphinx.util.tags import Tags # NOQA - # HTML5 Writer is avialable or not if is_html5_writer_available(): from sphinx.writers.html5 import HTML5Translator @@ -65,8 +63,7 @@ logger = logging.getLogger(__name__) return_codes_re = re.compile('[\r\n]+') -def get_stable_hash(obj): - # type: (Any) -> str +def get_stable_hash(obj: Any) -> str: """ Return a stable hash for a Python data structure. We can't just use the md5 of str(obj) since for example dictionary items are enumerated @@ -89,8 +86,7 @@ class Stylesheet(str): attributes = None # type: Dict[str, str] filename = None # type: str - def __new__(cls, filename, *args, **attributes): - # type: (str, str, str) -> None + def __new__(cls, filename: str, *args: str, **attributes: str) -> None: self = str.__new__(cls, filename) # type: ignore self.filename = filename self.attributes = attributes @@ -105,23 +101,20 @@ class Stylesheet(str): class JSContainer(list): """The container for JavaScript scripts.""" - def insert(self, index, obj): - # type: (int, str) -> None + def insert(self, index: int, obj: str) -> None: warnings.warn('To modify script_files in the theme is deprecated. ' 'Please insert a