From 60f3968ef787257283a6af8023ae9890f3ae8384 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 13 Dec 2017 23:48:07 +0900 Subject: [PATCH 01/37] diet test-root: Move ziptheme to test-theming --- .../roots/{test-root => test-theming}/ziptheme.zip | Bin tests/test_theming.py | 7 ++++--- 2 files changed, 4 insertions(+), 3 deletions(-) rename tests/roots/{test-root => test-theming}/ziptheme.zip (100%) diff --git a/tests/roots/test-root/ziptheme.zip b/tests/roots/test-theming/ziptheme.zip similarity index 100% rename from tests/roots/test-root/ziptheme.zip rename to tests/roots/test-theming/ziptheme.zip diff --git a/tests/test_theming.py b/tests/test_theming.py index ef9656bc5..f8cfd395e 100644 --- a/tests/test_theming.py +++ b/tests/test_theming.py @@ -17,6 +17,7 @@ from sphinx.theming import ThemeError @pytest.mark.sphinx( + testroot='theming', confoverrides={'html_theme': 'ziptheme', 'html_theme_options.testopt': 'foo'}) def test_theme_api(app, status, warning): @@ -25,9 +26,9 @@ def test_theme_api(app, status, warning): # test Theme class API assert set(app.html_themes.keys()) == \ set(['basic', 'default', 'scrolls', 'agogo', 'sphinxdoc', 'haiku', - 'traditional', 'testtheme', 'ziptheme', 'epub', 'nature', - 'pyramid', 'bizstyle', 'classic', 'nonav']) - assert app.html_themes['testtheme'] == app.srcdir / 'testtheme' + 'traditional', 'test-theme', 'ziptheme', 'epub', 'nature', + 'pyramid', 'bizstyle', 'classic', 'nonav', 'parent', 'child']) + assert app.html_themes['test-theme'] == app.srcdir / 'test_theme/test-theme' assert app.html_themes['ziptheme'] == app.srcdir / 'ziptheme.zip' # test Theme instance API From a8ab1f164b069528344a880e75550f57ec05a6b5 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 13 Dec 2017 23:48:07 +0900 Subject: [PATCH 02/37] diet test-root: Move testtheme to test-theming --- tests/roots/test-root/conf.py | 3 --- .../test_theme/staticfiles}/layout.html | 0 .../staticfiles}/static/staticimg.png | Bin .../staticfiles}/static/statictmpl.html_t | 0 .../test_theme/staticfiles}/theme.conf | 0 tests/test_build_html.py | 4 ---- tests/test_build_html5.py | 4 ---- tests/test_theming.py | 22 +++++++++++++++--- 8 files changed, 19 insertions(+), 14 deletions(-) rename tests/roots/{test-root/testtheme => test-theming/test_theme/staticfiles}/layout.html (100%) rename tests/roots/{test-root/testtheme => test-theming/test_theme/staticfiles}/static/staticimg.png (100%) rename tests/roots/{test-root/testtheme => test-theming/test_theme/staticfiles}/static/statictmpl.html_t (100%) rename tests/roots/{test-root/testtheme => test-theming/test_theme/staticfiles}/theme.conf (100%) diff --git a/tests/roots/test-root/conf.py b/tests/roots/test-root/conf.py index a23aec482..c25beb9f1 100644 --- a/tests/roots/test-root/conf.py +++ b/tests/roots/test-root/conf.py @@ -29,9 +29,6 @@ numfig = True rst_epilog = '.. |subst| replace:: global substitution' -html_theme = 'testtheme' -html_theme_path = ['.'] -html_theme_options = {'testopt': 'testoverride'} html_sidebars = {'**': 'customsb.html', 'contents': ['contentssb.html', 'localtoc.html', 'globaltoc.html']} diff --git a/tests/roots/test-root/testtheme/layout.html b/tests/roots/test-theming/test_theme/staticfiles/layout.html similarity index 100% rename from tests/roots/test-root/testtheme/layout.html rename to tests/roots/test-theming/test_theme/staticfiles/layout.html diff --git a/tests/roots/test-root/testtheme/static/staticimg.png b/tests/roots/test-theming/test_theme/staticfiles/static/staticimg.png similarity index 100% rename from tests/roots/test-root/testtheme/static/staticimg.png rename to tests/roots/test-theming/test_theme/staticfiles/static/staticimg.png diff --git a/tests/roots/test-root/testtheme/static/statictmpl.html_t b/tests/roots/test-theming/test_theme/staticfiles/static/statictmpl.html_t similarity index 100% rename from tests/roots/test-root/testtheme/static/statictmpl.html_t rename to tests/roots/test-theming/test_theme/staticfiles/static/statictmpl.html_t diff --git a/tests/roots/test-root/testtheme/theme.conf b/tests/roots/test-theming/test_theme/staticfiles/theme.conf similarity index 100% rename from tests/roots/test-root/testtheme/theme.conf rename to tests/roots/test-theming/test_theme/staticfiles/theme.conf diff --git a/tests/test_build_html.py b/tests/test_build_html.py index ceeb5f01c..6fa769b8b 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -377,7 +377,6 @@ def test_static_output(app): 'contents.html': [ (".//meta[@name='hc'][@content='hcval']", ''), (".//meta[@name='hc_co'][@content='hcval_co']", ''), - (".//meta[@name='testopt'][@content='testoverride']", ''), (".//td[@class='label']", r'\[Ref1\]'), (".//td[@class='label']", ''), (".//li[@class='toctree-l1']/a", 'Testing various markup'), @@ -410,9 +409,6 @@ def test_static_output(app): (".//a[@href='http://bugs.python.org/issue1000']", "issue 1000"), (".//a[@href='http://bugs.python.org/issue1042']", "explicit caption"), ], - '_static/statictmpl.html': [ - (".//project", 'Sphinx '), - ], 'genindex.html': [ # index entries (".//a/strong", "Main"), diff --git a/tests/test_build_html5.py b/tests/test_build_html5.py index 7fc91b9ec..4ad282973 100644 --- a/tests/test_build_html5.py +++ b/tests/test_build_html5.py @@ -252,7 +252,6 @@ def cached_etree_parse(): 'contents.html': [ (".//meta[@name='hc'][@content='hcval']", ''), (".//meta[@name='hc_co'][@content='hcval_co']", ''), - (".//meta[@name='testopt'][@content='testoverride']", ''), (".//dt[@class='label']/span[@class='brackets']", r'Ref1'), (".//dt[@class='label']", ''), (".//li[@class='toctree-l1']/a", 'Testing various markup'), @@ -285,9 +284,6 @@ def cached_etree_parse(): (".//a[@href='http://bugs.python.org/issue1000']", "issue 1000"), (".//a[@href='http://bugs.python.org/issue1042']", "explicit caption"), ], - '_static/statictmpl.html': [ - (".//project", 'Sphinx '), - ], 'genindex.html': [ # index entries (".//a/strong", "Main"), diff --git a/tests/test_theming.py b/tests/test_theming.py index f8cfd395e..176c9eebc 100644 --- a/tests/test_theming.py +++ b/tests/test_theming.py @@ -26,10 +26,11 @@ def test_theme_api(app, status, warning): # test Theme class API assert set(app.html_themes.keys()) == \ set(['basic', 'default', 'scrolls', 'agogo', 'sphinxdoc', 'haiku', - 'traditional', 'test-theme', 'ziptheme', 'epub', 'nature', - 'pyramid', 'bizstyle', 'classic', 'nonav', 'parent', 'child']) - assert app.html_themes['test-theme'] == app.srcdir / 'test_theme/test-theme' + 'traditional', 'epub', 'nature', 'pyramid', 'bizstyle', 'classic', 'nonav', + 'test-theme', 'ziptheme', 'staticfiles', 'parent', 'child']) + assert app.html_themes['test-theme'] == app.srcdir / 'test_theme' / 'test-theme' assert app.html_themes['ziptheme'] == app.srcdir / 'ziptheme.zip' + assert app.html_themes['staticfiles'] == app.srcdir / 'test_theme' / 'staticfiles' # test Theme instance API theme = app.builder.theme @@ -94,3 +95,18 @@ def test_double_inheriting_theme(app, status, warning): def test_nested_zipped_theme(app, status, warning): assert app.builder.theme.name == 'child' app.build() # => not raises TemplateNotFound + + +@pytest.mark.sphinx(testroot='theming', + confoverrides={'html_theme': 'staticfiles'}) +def test_staticfiles(app, status, warning): + app.build() + assert (app.outdir / '_static' / 'staticimg.png').exists() + assert (app.outdir / '_static' / 'statictmpl.html').exists() + assert (app.outdir / '_static' / 'statictmpl.html').text() == ( + '\n' + 'Python' + ) + + result = (app.outdir / 'index.html').text() + assert '' in result From a5b64c98af1f898f95484010e0eb67f3ef4d6c3d Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 13 Dec 2017 23:46:57 +0900 Subject: [PATCH 03/37] diet test-root: Remove html_static_path and html_extra_path (duplicated with test-html_assets) --- tests/roots/test-root/_static/README | 1 - tests/roots/test-root/_static/excluded.css | 1 - tests/roots/test-root/_static/subdir/foo.css | 1 - tests/roots/test-root/conf.py | 2 -- tests/roots/test-root/robots.txt | 2 -- tests/roots/test-root/templated.css_t | 2 -- tests/test_build_html.py | 27 -------------------- 7 files changed, 36 deletions(-) delete mode 100644 tests/roots/test-root/_static/README delete mode 100644 tests/roots/test-root/_static/excluded.css delete mode 100644 tests/roots/test-root/_static/subdir/foo.css delete mode 100644 tests/roots/test-root/robots.txt delete mode 100644 tests/roots/test-root/templated.css_t diff --git a/tests/roots/test-root/_static/README b/tests/roots/test-root/_static/README deleted file mode 100644 index 9e1ec3569..000000000 --- a/tests/roots/test-root/_static/README +++ /dev/null @@ -1 +0,0 @@ -This whole directory is there to test html_static_path. diff --git a/tests/roots/test-root/_static/excluded.css b/tests/roots/test-root/_static/excluded.css deleted file mode 100644 index 03c941a44..000000000 --- a/tests/roots/test-root/_static/excluded.css +++ /dev/null @@ -1 +0,0 @@ -/* This file should be excluded from being copied over */ diff --git a/tests/roots/test-root/_static/subdir/foo.css b/tests/roots/test-root/_static/subdir/foo.css deleted file mode 100644 index 9427981d6..000000000 --- a/tests/roots/test-root/_static/subdir/foo.css +++ /dev/null @@ -1 +0,0 @@ -/* Stub file */ diff --git a/tests/roots/test-root/conf.py b/tests/roots/test-root/conf.py index c25beb9f1..bd3cb9a9b 100644 --- a/tests/roots/test-root/conf.py +++ b/tests/roots/test-root/conf.py @@ -33,8 +33,6 @@ html_sidebars = {'**': 'customsb.html', 'contents': ['contentssb.html', 'localtoc.html', 'globaltoc.html']} html_style = 'default.css' -html_static_path = ['_static', 'templated.css_t'] -html_extra_path = ['robots.txt'] html_last_updated_fmt = '%b %d, %Y' html_context = {'hckey': 'hcval', 'hckey_co': 'wrong_hcval_co'} diff --git a/tests/roots/test-root/robots.txt b/tests/roots/test-root/robots.txt deleted file mode 100644 index 1b425ee0f..000000000 --- a/tests/roots/test-root/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -Disallow: /cgi-bin/ diff --git a/tests/roots/test-root/templated.css_t b/tests/roots/test-root/templated.css_t deleted file mode 100644 index 72ddb807c..000000000 --- a/tests/roots/test-root/templated.css_t +++ /dev/null @@ -1,2 +0,0 @@ -/* Stub file, templated */ -{{ sphinx_version }} diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 6fa769b8b..c27dad42b 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -126,24 +126,6 @@ def check_xpath(etree, fname, path, check, be_found=True): [node.text for node in nodes])) -def check_static_entries(outdir): - staticdir = outdir / '_static' - assert staticdir.isdir() - # a file from a directory entry in html_static_path - assert (staticdir / 'README').isfile() - # a directory from a directory entry in html_static_path - assert (staticdir / 'subdir' / 'foo.css').isfile() - # a file from a file entry in html_static_path - assert (staticdir / 'templated.css').isfile() - assert (staticdir / 'templated.css').text().splitlines()[1] == __display_version__ - # a file from _static, but matches exclude_patterns - assert not (staticdir / 'excluded.css').exists() - - -def check_extra_entries(outdir): - assert (outdir / 'robots.txt').isfile() - - @pytest.mark.sphinx('html', testroot='warnings') def test_html_warnings(app, warning): app.build() @@ -156,15 +138,6 @@ def test_html_warnings(app, warning): '--- Got:\n' + html_warnings -@pytest.mark.sphinx('html', tags=['testtag'], confoverrides={ - 'html_context.hckey_co': 'hcval_co'}) -@pytest.mark.test_params(shared_result='test_build_html_output') -def test_static_output(app): - app.build() - check_static_entries(app.builder.outdir) - check_extra_entries(app.builder.outdir) - - @pytest.mark.parametrize("fname,expect", flat_dict({ 'images.html': [ (".//img[@src='_images/img.png']", ''), From a3f04c3397726ee688a5ebdebbbd23510f973d7f Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 15 Dec 2017 02:30:09 +0900 Subject: [PATCH 04/37] Refactor test_build_html: Use basic project instead roots --- tests/test_build_html.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/test_build_html.py b/tests/test_build_html.py index c27dad42b..083add9a1 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -1114,16 +1114,22 @@ def test_html_assets(app): assert not (app.outdir / 'subdir' / '.htpasswd').exists() -@pytest.mark.sphinx('html', confoverrides={'html_sourcelink_suffix': ''}) +@pytest.mark.sphinx('html', testroot='basic', confoverrides={'html_sourcelink_suffix': '.txt'}) def test_html_sourcelink_suffix(app): app.builder.build_all() - content_otherext = (app.outdir / 'otherext.html').text() - content_images = (app.outdir / 'images.html').text() + assert (app.outdir / '_sources' / 'index.rst.txt').exists() - assert ' Date: Fri, 15 Dec 2017 02:30:25 +0900 Subject: [PATCH 05/37] test: Add test_html_copy_source --- tests/test_build_html.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 083add9a1..0ccd4da01 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -1114,6 +1114,12 @@ def test_html_assets(app): assert not (app.outdir / 'subdir' / '.htpasswd').exists() +@pytest.mark.sphinx('html', testroot='basic', confoverrides={'html_copy_source': False}) +def test_html_copy_source(app): + app.builder.build_all() + assert not (app.outdir / '_sources' / 'index.rst.txt').exists() + + @pytest.mark.sphinx('html', testroot='basic', confoverrides={'html_sourcelink_suffix': '.txt'}) def test_html_sourcelink_suffix(app): app.builder.build_all() From 451ba5e76a2417c70ae4e2213b78c109d17bca05 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 15 Dec 2017 21:11:38 +0900 Subject: [PATCH 06/37] Fix ResourceWarning on pycode --- sphinx/pycode/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py index 66544f073..20de2a656 100644 --- a/sphinx/pycode/__init__.py +++ b/sphinx/pycode/__init__.py @@ -36,11 +36,11 @@ class ModuleAnalyzer(object): if ('file', filename) in cls.cache: return cls.cache['file', filename] try: - fileobj = open(filename, 'rb') + with open(filename, 'rb') as f: + obj = cls(f, modname, filename) + cls.cache['file', filename] = obj except Exception as err: raise PycodeError('error opening %r' % filename, err) - obj = cls(fileobj, modname, filename) - cls.cache['file', filename] = obj return obj @classmethod From 2a2ed8915a2eaea45f2f65ca08dd563708b5c0a4 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 15 Dec 2017 21:14:19 +0900 Subject: [PATCH 07/37] diet test-root: Move .mo files to new testroot (for test_html_rebuild_mo) --- .../bom.po | 0 tests/roots/test-builder-gettext-dont-rebuild-mo/bom.rst | 5 +++++ tests/roots/test-builder-gettext-dont-rebuild-mo/conf.py | 7 +++++++ .../roots/test-builder-gettext-dont-rebuild-mo/index.rst | 6 ++++++ tests/roots/test-root/subdir.po | 9 --------- tests/test_intl.py | 4 ++-- 6 files changed, 20 insertions(+), 11 deletions(-) rename tests/roots/{test-root => test-builder-gettext-dont-rebuild-mo}/bom.po (100%) create mode 100644 tests/roots/test-builder-gettext-dont-rebuild-mo/bom.rst create mode 100644 tests/roots/test-builder-gettext-dont-rebuild-mo/conf.py create mode 100644 tests/roots/test-builder-gettext-dont-rebuild-mo/index.rst delete mode 100644 tests/roots/test-root/subdir.po diff --git a/tests/roots/test-root/bom.po b/tests/roots/test-builder-gettext-dont-rebuild-mo/bom.po similarity index 100% rename from tests/roots/test-root/bom.po rename to tests/roots/test-builder-gettext-dont-rebuild-mo/bom.po diff --git a/tests/roots/test-builder-gettext-dont-rebuild-mo/bom.rst b/tests/roots/test-builder-gettext-dont-rebuild-mo/bom.rst new file mode 100644 index 000000000..3fea824f8 --- /dev/null +++ b/tests/roots/test-builder-gettext-dont-rebuild-mo/bom.rst @@ -0,0 +1,5 @@ +File with UTF-8 BOM +=================== + +This file has a UTF-8 "BOM". + diff --git a/tests/roots/test-builder-gettext-dont-rebuild-mo/conf.py b/tests/roots/test-builder-gettext-dont-rebuild-mo/conf.py new file mode 100644 index 000000000..31e7a6ed4 --- /dev/null +++ b/tests/roots/test-builder-gettext-dont-rebuild-mo/conf.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- + +master_doc = 'index' + +latex_documents = [ + (master_doc, 'test.tex', 'The basic Sphinx documentation for testing', 'Sphinx', 'report') +] diff --git a/tests/roots/test-builder-gettext-dont-rebuild-mo/index.rst b/tests/roots/test-builder-gettext-dont-rebuild-mo/index.rst new file mode 100644 index 000000000..7ff38c5a8 --- /dev/null +++ b/tests/roots/test-builder-gettext-dont-rebuild-mo/index.rst @@ -0,0 +1,6 @@ +The basic Sphinx documentation for testing +========================================== + +.. toctree:: + + bom diff --git a/tests/roots/test-root/subdir.po b/tests/roots/test-root/subdir.po deleted file mode 100644 index f515f2207..000000000 --- a/tests/roots/test-root/subdir.po +++ /dev/null @@ -1,9 +0,0 @@ -#, fuzzy -msgid "" -msgstr "" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -msgid "Including in subdir" -msgstr "translation" diff --git a/tests/test_intl.py b/tests/test_intl.py index 8eff52340..6b72438bd 100644 --- a/tests/test_intl.py +++ b/tests/test_intl.py @@ -520,7 +520,7 @@ def test_gettext_buildr_ignores_only_directive(app): @sphinx_intl # use individual shared_result directory to avoid "incompatible doctree" error -@pytest.mark.test_params(shared_result='test_gettext_dont_rebuild_mo') +@pytest.mark.sphinx(testroot='builder-gettext-dont-rebuild-mo') def test_gettext_dont_rebuild_mo(make_app, app_params, build_mo): # --- don't rebuild by .mo mtime def get_number_of_update_targets(app_): @@ -533,7 +533,7 @@ def test_gettext_dont_rebuild_mo(make_app, app_params, build_mo): app0 = make_app('dummy', *args, **kwargs) build_mo(app0.srcdir) app0.build() - assert (app0.srcdir / 'bom.mo') + assert (app0.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').exists() # Since it is after the build, the number of documents to be updated is 0 assert get_number_of_update_targets(app0) == 0 # When rewriting the timestamp of mo file, the number of documents to be From c250c63daaf67e9ca3f24ed9974d17016326de07 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 15 Dec 2017 21:14:20 +0900 Subject: [PATCH 08/37] test: Skip some builders on test_build_all --- tests/test_build.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_build.py b/tests/test_build.py index 2372b102b..c61fbb5c2 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -59,13 +59,13 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir): return srcdir +# note: this test skips building docs for some builders because they have independent testcase. +# (html, latex, texinfo and manpage) @pytest.mark.parametrize( "buildername", [ - # note: no 'html' - if it's ok with dirhtml it's ok with html - 'dirhtml', 'singlehtml', 'latex', 'texinfo', 'pickle', 'json', 'text', - 'htmlhelp', 'qthelp', 'epub2', 'epub', 'applehelp', 'changes', 'xml', - 'pseudoxml', 'man', 'linkcheck', + 'dirhtml', 'singlehtml', 'pickle', 'json', 'text', 'htmlhelp', 'qthelp', + 'epub2', 'epub', 'applehelp', 'changes', 'xml', 'pseudoxml', 'linkcheck', ], ) @mock.patch('sphinx.builders.linkcheck.requests.head', From facce1fb020a61637a8d110cf556a08912e64035 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 16 Dec 2017 01:27:18 +0900 Subject: [PATCH 09/37] quickstart: Fix make_mode should be chosen by default --- sphinx/cmd/quickstart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/cmd/quickstart.py b/sphinx/cmd/quickstart.py index af07d4978..80e9e3dd4 100644 --- a/sphinx/cmd/quickstart.py +++ b/sphinx/cmd/quickstart.py @@ -586,7 +586,7 @@ Makefile to be used with sphinx-build. dest='batchfile', help='do not create batchfile') group.add_argument('-m', '--use-make-mode', action='store_true', - dest='make_mode', + dest='make_mode', default=True, help='use make-mode for Makefile/make.bat') group.add_argument('-M', '--no-use-make-mode', action='store_false', dest='make_mode', From b131c33af5ea49c6416026050725a0121c8991f3 Mon Sep 17 00:00:00 2001 From: jfbu Date: Fri, 15 Dec 2017 22:54:52 +0100 Subject: [PATCH 10/37] Fix markup in doc/ext/math.rst --- doc/ext/math.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ext/math.rst b/doc/ext/math.rst index 3d25d09ba..ca53f3a8e 100644 --- a/doc/ext/math.rst +++ b/doc/ext/math.rst @@ -78,7 +78,7 @@ or use Python raw strings (``r"raw"``). Normally, equations are not numbered. If you want your equation to get a number, use the ``label`` option. When given, it selects an internal label for the equation, by which it can be cross-referenced, and causes an equation - number to be issued. See :rst:role:`eqref` for an example. The numbering + number to be issued. See :rst:role:`eq` for an example. The numbering style depends on the output format. There is also an option ``nowrap`` that prevents any wrapping of the given From 7cabd6cbb053965e1930b53c27d596fd1a52dd78 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 16 Dec 2017 14:51:55 +0900 Subject: [PATCH 11/37] Fix mypy violations --- sphinx/builders/html.py | 2 +- sphinx/builders/qthelp.py | 2 +- sphinx/config.py | 2 +- sphinx/domains/cpp.py | 2 +- sphinx/environment/collectors/toctree.py | 2 +- sphinx/ext/napoleon/docstring.py | 22 +++++++++++----------- sphinx/pycode/nodes.py | 2 +- sphinx/setup_command.py | 10 +++++----- sphinx/transforms/compact_bullet_list.py | 6 +++++- sphinx/util/__init__.py | 8 +++----- sphinx/util/logging.py | 4 ++-- 11 files changed, 32 insertions(+), 30 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index ead51a983..e50d2abe4 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -274,7 +274,7 @@ class StandaloneHTMLBuilder(Builder): # type: () -> Iterator[unicode] cfgdict = dict((confval.name, confval.value) for confval in self.config.filter('html')) self.config_hash = get_stable_hash(cfgdict) - self.tags_hash = get_stable_hash(sorted(self.tags)) # type: ignore + self.tags_hash = get_stable_hash(sorted(self.tags)) old_config_hash = old_tags_hash = '' try: with open(path.join(self.outdir, '.buildinfo')) as fp: diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py index 14979fe4b..12a50d140 100644 --- a/sphinx/builders/qthelp.py +++ b/sphinx/builders/qthelp.py @@ -264,7 +264,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder): link = node['refuri'] title = htmlescape(node.astext()).replace('"', '"') item = section_template % {'title': title, 'ref': link} - item = u' ' * 4 * indentlevel + item # type: ignore + item = u' ' * 4 * indentlevel + item parts.append(item.encode('ascii', 'xmlcharrefreplace')) elif isinstance(node, nodes.bullet_list): for subnode in node: diff --git a/sphinx/config.py b/sphinx/config.py index 02ee529a3..cc5f57e8e 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -288,7 +288,7 @@ class Config(object): logger.warning("%s", exc) for name in config: if name in self.values: - self.__dict__[name] = config[name] + self.__dict__[name] = config[name] # type: ignore if isinstance(self.source_suffix, string_types): # type: ignore self.source_suffix = [self.source_suffix] # type: ignore diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 23c398e13..aa97481ab 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -528,7 +528,7 @@ class ASTBase(UnicodeMixin): if type(self) is not type(other): return False try: - for key, value in iteritems(self.__dict__): # type: ignore + for key, value in iteritems(self.__dict__): if value != getattr(other, key): return False except AttributeError: diff --git a/sphinx/environment/collectors/toctree.py b/sphinx/environment/collectors/toctree.py index f19fd5d26..91aa21f2e 100644 --- a/sphinx/environment/collectors/toctree.py +++ b/sphinx/environment/collectors/toctree.py @@ -262,7 +262,7 @@ class TocTreeCollector(EnvironmentCollector): continue - figtype = env.get_domain('std').get_figtype(subnode) # type: ignore + figtype = env.get_domain('std').get_figtype(subnode) if figtype and subnode['ids']: register_fignumber(docname, secnum, figtype, subnode) diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index c77598ef1..d3a64049b 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -194,7 +194,7 @@ class GoogleDocstring(UnicodeMixin): line = self._line_iter.peek() while(not self._is_section_break() and (not line or self._is_indented(line, indent))): - lines.append(next(self._line_iter)) # type: ignore + lines.append(next(self._line_iter)) line = self._line_iter.peek() return lines @@ -204,7 +204,7 @@ class GoogleDocstring(UnicodeMixin): while (self._line_iter.has_next() and self._line_iter.peek() and not self._is_section_header()): - lines.append(next(self._line_iter)) # type: ignore + lines.append(next(self._line_iter)) return lines def _consume_empty(self): @@ -212,13 +212,13 @@ class GoogleDocstring(UnicodeMixin): lines = [] line = self._line_iter.peek() while self._line_iter.has_next() and not line: - lines.append(next(self._line_iter)) # type: ignore + lines.append(next(self._line_iter)) line = self._line_iter.peek() return lines def _consume_field(self, parse_type=True, prefer_type=False): # type: (bool, bool) -> Tuple[unicode, unicode, List[unicode]] - line = next(self._line_iter) # type: ignore + line = next(self._line_iter) before, colon, after = self._partition_field_on_colon(line) _name, _type, _desc = before, '', after # type: unicode, unicode, unicode @@ -250,7 +250,7 @@ class GoogleDocstring(UnicodeMixin): def _consume_inline_attribute(self): # type: () -> Tuple[unicode, List[unicode]] - line = next(self._line_iter) # type: ignore + line = next(self._line_iter) _type, colon, _desc = self._partition_field_on_colon(line) if not colon: _type, _desc = _desc, _type @@ -285,7 +285,7 @@ class GoogleDocstring(UnicodeMixin): def _consume_section_header(self): # type: () -> unicode - section = next(self._line_iter) # type: ignore + section = next(self._line_iter) stripped_section = section.strip(':') if stripped_section.lower() in self._sections: section = stripped_section @@ -295,7 +295,7 @@ class GoogleDocstring(UnicodeMixin): # type: () -> List[unicode] lines = [] while self._line_iter.has_next(): - lines.append(next(self._line_iter)) # type: ignore + lines.append(next(self._line_iter)) return lines def _consume_to_next_section(self): @@ -303,7 +303,7 @@ class GoogleDocstring(UnicodeMixin): self._consume_empty() lines = [] while not self._is_section_break(): - lines.append(next(self._line_iter)) # type: ignore + lines.append(next(self._line_iter)) return lines + self._consume_empty() def _dedent(self, lines, full=False): @@ -886,7 +886,7 @@ class NumpyDocstring(GoogleDocstring): def _consume_field(self, parse_type=True, prefer_type=False): # type: (bool, bool) -> Tuple[unicode, unicode, List[unicode]] - line = next(self._line_iter) # type: ignore + line = next(self._line_iter) if parse_type: _name, _, _type = self._partition_field_on_colon(line) else: @@ -907,10 +907,10 @@ class NumpyDocstring(GoogleDocstring): def _consume_section_header(self): # type: () -> unicode - section = next(self._line_iter) # type: ignore + section = next(self._line_iter) if not _directive_regex.match(section): # Consume the header underline - next(self._line_iter) # type: ignore + next(self._line_iter) return section def _is_section_break(self): diff --git a/sphinx/pycode/nodes.py b/sphinx/pycode/nodes.py index cecde9bd0..ea3b3e9ad 100644 --- a/sphinx/pycode/nodes.py +++ b/sphinx/pycode/nodes.py @@ -208,5 +208,5 @@ class NodeVisitor(object): def generic_visit(self, node): """Called if no explicit visitor function exists for a node.""" if isinstance(node, Node): - for child in node: # type: ignore + for child in node: self.visit(child) diff --git a/sphinx/setup_command.py b/sphinx/setup_command.py index d219a14d9..8c00a2ff8 100644 --- a/sphinx/setup_command.py +++ b/sphinx/setup_command.py @@ -136,8 +136,8 @@ class BuildDoc(Command): # type: () -> None if self.source_dir is None: self.source_dir = self._guess_source_dir() - self.announce('Using source directory %s' % self.source_dir) # type: ignore - self.ensure_dirname('source_dir') # type: ignore + self.announce('Using source directory %s' % self.source_dir) + self.ensure_dirname('source_dir') if self.source_dir is None: self.source_dir = os.curdir self.source_dir = abspath(self.source_dir) @@ -145,10 +145,10 @@ class BuildDoc(Command): self.config_dir = self.source_dir self.config_dir = abspath(self.config_dir) - self.ensure_string_list('builder') # type: ignore + self.ensure_string_list('builder') if self.build_dir is None: - build = self.get_finalized_command('build') # type: ignore - self.build_dir = os.path.join(abspath(build.build_base), 'sphinx') + build = self.get_finalized_command('build') + self.build_dir = os.path.join(abspath(build.build_base), 'sphinx') # type: ignore self.mkpath(self.build_dir) # type: ignore self.build_dir = abspath(self.build_dir) self.doctree_dir = os.path.join(self.build_dir, 'doctrees') diff --git a/sphinx/transforms/compact_bullet_list.py b/sphinx/transforms/compact_bullet_list.py index 006ae7161..8c930c8bc 100644 --- a/sphinx/transforms/compact_bullet_list.py +++ b/sphinx/transforms/compact_bullet_list.py @@ -14,6 +14,10 @@ from docutils import nodes from sphinx import addnodes from sphinx.transforms import SphinxTransform +if False: + # For type annotation + from typing import List # NOQA + class RefOnlyListChecker(nodes.GenericNodeVisitor): """Raise `nodes.NodeFound` if non-simple list item is encountered. @@ -32,7 +36,7 @@ class RefOnlyListChecker(nodes.GenericNodeVisitor): def visit_list_item(self, node): # type: (nodes.Node) -> None - children = [] + children = [] # type: List[nodes.Node] for child in node.children: if not isinstance(child, nodes.Invisible): children.append(child) diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 03f8ce6a3..55fb9fcc1 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -398,10 +398,8 @@ def parselinenos(spec, total): elif len(begend) == 1: items.append(int(begend[0]) - 1) elif len(begend) == 2: - start = int(begend[0] or 1) # type: ignore - # left half open (cf. -10) - end = int(begend[1] or max(start, total)) # type: ignore - # right half open (cf. 10-) + start = int(begend[0] or 1) # left half open (cf. -10) + end = int(begend[1] or max(start, total)) # right half open (cf. 10-) if start > end: # invalid range (cf. 10-1) raise ValueError items.extend(range(start - 1, end)) @@ -528,7 +526,7 @@ class PeekableIterator(object): def peek(self): # type: () -> Any """Return the next item without changing the state of the iterator.""" - item = next(self) # type: ignore + item = next(self) self.push(item) return item diff --git a/sphinx/util/logging.py b/sphinx/util/logging.py index 6148a5445..00c12ec4f 100644 --- a/sphinx/util/logging.py +++ b/sphinx/util/logging.py @@ -156,8 +156,8 @@ class NewLineStreamHandlerPY2(logging.StreamHandler): # remove return code forcely when nonl=True self.stream = StringIO() super(NewLineStreamHandlerPY2, self).emit(record) - stream.write(self.stream.getvalue()[:-1]) # type: ignore - stream.flush() # type: ignore + stream.write(self.stream.getvalue()[:-1]) + stream.flush() else: super(NewLineStreamHandlerPY2, self).emit(record) finally: From f07b080331f49b2b873995962c06f82ee576229c Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 16 Dec 2017 15:01:23 +0900 Subject: [PATCH 12/37] Fix mypy violations --- sphinx/ext/autodoc/inspector.py | 10 +++++----- sphinx/util/inspect.py | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sphinx/ext/autodoc/inspector.py b/sphinx/ext/autodoc/inspector.py index f1faf2043..5d157c797 100644 --- a/sphinx/ext/autodoc/inspector.py +++ b/sphinx/ext/autodoc/inspector.py @@ -65,7 +65,7 @@ def format_annotation(annotation): elif (hasattr(typing, 'UnionMeta') and isinstance(annotation, typing.UnionMeta) and # type: ignore hasattr(annotation, '__union_params__')): - params = annotation.__union_params__ # type: ignore + params = annotation.__union_params__ if params is not None: param_str = ', '.join(format_annotation(p) for p in params) return '%s[%s]' % (qualified_name, param_str) @@ -74,7 +74,7 @@ def format_annotation(annotation): getattr(annotation, '__args__', None) is not None and hasattr(annotation, '__result__')): # Skipped in the case of plain typing.Callable - args = annotation.__args__ # type: ignore + args = annotation.__args__ if args is None: return qualified_name elif args is Ellipsis: @@ -84,15 +84,15 @@ def format_annotation(annotation): args_str = '[%s]' % ', '.join(formatted_args) return '%s[%s, %s]' % (qualified_name, args_str, - format_annotation(annotation.__result__)) # type: ignore + format_annotation(annotation.__result__)) elif (hasattr(typing, 'TupleMeta') and isinstance(annotation, typing.TupleMeta) and # type: ignore hasattr(annotation, '__tuple_params__') and hasattr(annotation, '__tuple_use_ellipsis__')): - params = annotation.__tuple_params__ # type: ignore + params = annotation.__tuple_params__ if params is not None: param_strings = [format_annotation(p) for p in params] - if annotation.__tuple_use_ellipsis__: # type: ignore + if annotation.__tuple_use_ellipsis__: param_strings.append('...') return '%s[%s]' % (qualified_name, ', '.join(param_strings)) diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index 2d15c2883..da13b8af0 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -434,7 +434,7 @@ class Signature(object): elif (hasattr(typing, 'UnionMeta') and # for py35 or below isinstance(annotation, typing.UnionMeta) and # type: ignore hasattr(annotation, '__union_params__')): - params = annotation.__union_params__ # type: ignore + params = annotation.__union_params__ if params is not None: param_str = ', '.join(self.format_annotation(p) for p in params) return '%s[%s]' % (qualified_name, param_str) @@ -442,7 +442,7 @@ class Signature(object): getattr(annotation, '__args__', None) is not None and hasattr(annotation, '__result__')): # Skipped in the case of plain typing.Callable - args = annotation.__args__ # type: ignore + args = annotation.__args__ if args is None: return qualified_name elif args is Ellipsis: @@ -452,14 +452,14 @@ class Signature(object): args_str = '[%s]' % ', '.join(formatted_args) return '%s[%s, %s]' % (qualified_name, args_str, - self.format_annotation(annotation.__result__)) # type: ignore # NOQA + self.format_annotation(annotation.__result__)) elif (isinstance(annotation, typing.TupleMeta) and # type: ignore hasattr(annotation, '__tuple_params__') and hasattr(annotation, '__tuple_use_ellipsis__')): - params = annotation.__tuple_params__ # type: ignore + params = annotation.__tuple_params__ if params is not None: param_strings = [self.format_annotation(p) for p in params] - if annotation.__tuple_use_ellipsis__: # type: ignore + if annotation.__tuple_use_ellipsis__: param_strings.append('...') return '%s[%s]' % (qualified_name, ', '.join(param_strings)) From 14af6b429ef2355ba0e8342c4af0f9dd9aafab1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Thu, 7 Dec 2017 23:15:05 +0100 Subject: [PATCH 13/37] Use ensuredir() instead of os.makedirs() to fix race conditions Use the ensuredir() function consistently across Sphinx code to avoid race conditions e.g. when multiple Sphinx instances attempt to create the same output directory. The ensuredir() function that was already present in sphinx.util.osutil correctly catches EEXIST exception that occurs if the specified directory already exists (i.e. it was created between the call to os.path.isdir() and os.makedirs() that follows it). While at it, remove redundant os.path.isdir() calls when they only guarded the os.makedirs() call, and replace mkdir_p() which had pretty much the same purpose, except for being prone to race conditions. I did not modify testing-related code as race conditions mostly affect real applications and not the test environment. Fix #4281: Race conditions when creating output directory --- CHANGES | 1 + sphinx/apidoc.py | 7 +++---- sphinx/application.py | 4 ++-- sphinx/builders/__init__.py | 6 ++---- sphinx/quickstart.py | 19 ++++++------------- 5 files changed, 14 insertions(+), 23 deletions(-) diff --git a/CHANGES b/CHANGES index b7a16af4f..1b8289784 100644 --- a/CHANGES +++ b/CHANGES @@ -33,6 +33,7 @@ Bugs fixed * #4279: Sphinx crashes with pickling error when run with multiple processes and remote image * #1421: Respect the quiet flag in sphinx-quickstart +* #4281: Race conditions when creating output directory Testing -------- diff --git a/sphinx/apidoc.py b/sphinx/apidoc.py index 24ed874b0..b764cfc35 100644 --- a/sphinx/apidoc.py +++ b/sphinx/apidoc.py @@ -26,7 +26,7 @@ from fnmatch import fnmatch from sphinx import __display_version__ from sphinx.quickstart import EXTENSIONS from sphinx.util import rst -from sphinx.util.osutil import FileAvoidWrite, walk +from sphinx.util.osutil import FileAvoidWrite, ensuredir, walk if False: # For type annotation @@ -375,9 +375,8 @@ Note: By default this script will not overwrite already created files.""") if not path.isdir(rootpath): print('%s is not a directory.' % rootpath, file=sys.stderr) sys.exit(1) - if not path.isdir(opts.destdir): - if not opts.dryrun: - os.makedirs(opts.destdir) + if not opts.dryrun: + ensuredir(opts.destdir) rootpath = path.abspath(rootpath) excludes = normalize_excludes(rootpath, excludes) modules = recurse_tree(rootpath, excludes, opts) diff --git a/sphinx/application.py b/sphinx/application.py index 0d6d3d0b3..b5705face 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -41,7 +41,7 @@ from sphinx.util import import_object from sphinx.util import logging from sphinx.util import status_iterator, old_status_iterator, display_chunk from sphinx.util.tags import Tags -from sphinx.util.osutil import ENOENT +from sphinx.util.osutil import ENOENT, ensuredir from sphinx.util.console import bold, darkgreen # type: ignore from sphinx.util.docutils import is_html5_writer_available, directive_helper from sphinx.util.i18n import find_catalog_source_files @@ -160,7 +160,7 @@ class Sphinx(object): if not path.isdir(outdir): logger.info('making output directory...') - os.makedirs(outdir) + ensuredir(outdir) # read config self.tags = Tags(tags) diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 007964e82..a1d5c5d22 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -9,7 +9,6 @@ :license: BSD, see LICENSE for details. """ -import os from os import path import warnings @@ -24,7 +23,7 @@ from docutils import nodes from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.environment.adapters.asset import ImageAdapter from sphinx.util import i18n, path_stabilize, logging, status_iterator -from sphinx.util.osutil import SEP, relative_uri +from sphinx.util.osutil import SEP, ensuredir, relative_uri from sphinx.util.i18n import find_catalog from sphinx.util.console import bold # type: ignore from sphinx.util.parallel import ParallelTasks, SerialTasks, make_chunks, \ @@ -79,8 +78,7 @@ class Builder(object): self.confdir = app.confdir self.outdir = app.outdir self.doctreedir = app.doctreedir - if not path.isdir(self.doctreedir): - os.makedirs(self.doctreedir) + ensuredir(self.doctreedir) self.app = app # type: Sphinx self.env = None # type: BuildEnvironment diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index d23dc3b74..5d8738996 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -35,7 +35,7 @@ from six.moves.urllib.parse import quote as urlquote from docutils.utils import column_width from sphinx import __display_version__, package_dir -from sphinx.util.osutil import make_filename +from sphinx.util.osutil import ensuredir, make_filename from sphinx.util.console import ( # type: ignore purple, bold, red, turquoise, nocolor, color_terminal ) @@ -69,13 +69,6 @@ EXTENSIONS = ('autodoc', 'doctest', 'intersphinx', 'todo', 'coverage', PROMPT_PREFIX = '> ' -def mkdir_p(dir): - # type: (unicode) -> None - if path.isdir(dir): - return - os.makedirs(dir) - - # function to get input from terminal -- overridden by the test suite def term_input(prompt): # type: (unicode) -> unicode @@ -433,11 +426,11 @@ def generate(d, overwrite=True, silent=False, templatedir=None): d[key + '_str'] = d[key].replace('\\', '\\\\').replace("'", "\\'") if not path.isdir(d['path']): - mkdir_p(d['path']) + ensuredir(d['path']) srcdir = d['sep'] and path.join(d['path'], 'source') or d['path'] - mkdir_p(srcdir) + ensuredir(srcdir) if d['sep']: builddir = path.join(d['path'], 'build') d['exclude_patterns'] = '' @@ -448,9 +441,9 @@ def generate(d, overwrite=True, silent=False, templatedir=None): 'Thumbs.db', '.DS_Store', ]) d['exclude_patterns'] = ', '.join(exclude_patterns) - mkdir_p(builddir) - mkdir_p(path.join(srcdir, d['dot'] + 'templates')) - mkdir_p(path.join(srcdir, d['dot'] + 'static')) + ensuredir(builddir) + ensuredir(path.join(srcdir, d['dot'] + 'templates')) + ensuredir(path.join(srcdir, d['dot'] + 'static')) def write_file(fpath, content, newline=None): # type: (unicode, unicode, unicode) -> None From 070fbb97e5a8f41a398ac5de16938f2da5797a5a Mon Sep 17 00:00:00 2001 From: jfbu Date: Sat, 16 Dec 2017 11:05:33 +0100 Subject: [PATCH 14/37] Add markup and clarify numfig_secnum_depth docs --- doc/config.rst | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/doc/config.rst b/doc/config.rst index 7a6d20147..3b490c5d3 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -311,8 +311,8 @@ General configuration .. confval:: numfig If true, figures, tables and code-blocks are automatically numbered if they - have a caption. At same time, the `numref` role is enabled. For now, it - works only with the HTML builder and LaTeX builder. Default is ``False``. + have a caption. The :rst:role:`numref` role is enabled. + Obeyed so far only by HTML and LaTeX builders. Default is ``False``. .. note:: @@ -335,10 +335,12 @@ General configuration .. confval:: numfig_secnum_depth - The scope of figure numbers, that is, the numfig feature numbers figures - in which scope. ``0`` means "whole document". ``1`` means "in a section". - Sphinx numbers like x.1, x.2, x.3... ``2`` means "in a subsection". Sphinx - numbers like x.x.1, x.x.2, x.x.3..., and so on. Default is ``1``. + The scope for numbering: ``0`` means "continuous numbering", + ``1`` means "reset per section" + (i.e. numbers will be x.1, x.2, x.3 ... with x the section number), + ``2`` means "per subsection" + (i.e. numbers will be x.y.1, x.y.2, ...), + and so on. Default is ``1``. .. versionadded:: 1.3 From f780f92d1b0eb1c3f6b37dc1e75e0980d046b134 Mon Sep 17 00:00:00 2001 From: jfbu Date: Sat, 16 Dec 2017 11:32:40 +0100 Subject: [PATCH 15/37] Mention ``:numbered:`` needed in numfig_secnum_depth docs --- doc/config.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/config.rst b/doc/config.rst index 3b490c5d3..fc87c4ddc 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -337,7 +337,9 @@ General configuration The scope for numbering: ``0`` means "continuous numbering", ``1`` means "reset per section" - (i.e. numbers will be x.1, x.2, x.3 ... with x the section number), + (i.e. numbers will be x.1, x.2, x.3 ... with x the section number, + assuming that the :rst:dir:`toctree` directive was used with its option + ``:numbered:``), ``2`` means "per subsection" (i.e. numbers will be x.y.1, x.y.2, ...), and so on. Default is ``1``. From a9602e4597a75889edc91e65f226c72a5ae73d0e Mon Sep 17 00:00:00 2001 From: jfbu Date: Sat, 16 Dec 2017 11:46:33 +0100 Subject: [PATCH 16/37] Improve numref docs --- doc/markup/inline.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/markup/inline.rst b/doc/markup/inline.rst index a59585bab..09ea49003 100644 --- a/doc/markup/inline.rst +++ b/doc/markup/inline.rst @@ -222,15 +222,15 @@ Cross-referencing figures by figure number reST labels are used. When you use this role, it will insert a reference to the figure with link text by its figure number like "Fig. 1.1". - If an explicit link text is given (like usual: ``:numref:`Image of Sphinx (Fig. - %s) ```), the link caption will be the title of the reference. - As a special character, `%s` and `{number}` will be replaced to figure - number. `{name}` will be replaced to figure caption. - If no explicit link text is given, the value of :confval:`numfig_format` is - used to default value of link text. + If an explicit link text is given (as usual: ``:numref:`Image of Sphinx (Fig. + %s) ```), the link caption will serve as title of the reference. + As place holders, `%s` and `{number}` get replaced by the figure + number and `{name}` by the figure caption. + If no explicit link text is given, the :confval:`numfig_format` setting is + used as fall-back default. - If :confval:`numfig` is ``False``, figures are not numbered. - so this role inserts not a reference but labels or link text. + If :confval:`numfig` is ``False``, figures are not numbered, + so this role inserts not a reference but the label or the link text. Cross-referencing other items of interest ----------------------------------------- From ba1368072fdfc143322a38335ebcdd8d83c6f720 Mon Sep 17 00:00:00 2001 From: jfbu Date: Sat, 16 Dec 2017 11:51:00 +0100 Subject: [PATCH 17/37] Typo place holder --> placeholder --- doc/markup/inline.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/markup/inline.rst b/doc/markup/inline.rst index 09ea49003..7ed3e7207 100644 --- a/doc/markup/inline.rst +++ b/doc/markup/inline.rst @@ -224,7 +224,7 @@ Cross-referencing figures by figure number If an explicit link text is given (as usual: ``:numref:`Image of Sphinx (Fig. %s) ```), the link caption will serve as title of the reference. - As place holders, `%s` and `{number}` get replaced by the figure + As placeholders, `%s` and `{number}` get replaced by the figure number and `{name}` by the figure caption. If no explicit link text is given, the :confval:`numfig_format` setting is used as fall-back default. From c6775bce0778b76efa0bd525fba681ba239c2606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vedran=20Mileti=C4=87?= Date: Thu, 5 Oct 2017 01:07:05 +0200 Subject: [PATCH 18/37] Add more EXAMPLES --- EXAMPLES | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/EXAMPLES b/EXAMPLES index 7c88805eb..0b2de95d9 100644 --- a/EXAMPLES +++ b/EXAMPLES @@ -34,6 +34,8 @@ Documentation using the alabaster theme * pytest: https://docs.pytest.org/ (customized) * python-apt: https://apt.alioth.debian.org/python-apt-doc/ * PyVisfile: https://documen.tician.de/pyvisfile/ +* Requests: http://www.python-requests.org/ +* searx: https://asciimoo.github.io/searx/ * Tablib: http://docs.python-tablib.org/ * urllib3: https://urllib3.readthedocs.io/ (customized) * Werkzeug: http://werkzeug.pocoo.org/docs/ (customized) @@ -46,6 +48,7 @@ Documentation using the classic theme * APSW: https://rogerbinns.github.io/apsw/ * Arb: http://arblib.org/ * Bazaar: http://doc.bazaar.canonical.com/ (customized) +* Beautiful Soup: https://www.crummy.com/software/BeautifulSoup/bs4/doc/ * Blender: https://docs.blender.org/api/current/ * Bugzilla: https://bugzilla.readthedocs.io/ * Buildbot: https://docs.buildbot.net/latest/ @@ -79,6 +82,8 @@ Documentation using the classic theme * Pyevolve: http://pyevolve.sourceforge.net/ * Pygame: https://www.pygame.org/docs/ (customized) * PyMQI: https://pythonhosted.org/pymqi/ +* PyQt4: http://pyqt.sourceforge.net/Docs/PyQt4/ (customized) +* PyQt5: http://pyqt.sourceforge.net/Docs/PyQt5/ (customized) * Python 2: https://docs.python.org/2/ * Python 3: https://docs.python.org/3/ (customized) * Python Packaging Authority: https://www.pypa.io/ (customized) @@ -121,11 +126,16 @@ Documentation using the nature theme * Alembic: http://alembic.zzzcomputing.com/ * Cython: http://docs.cython.org/ +* easybuild: https://easybuild.readthedocs.io/ * jsFiddle: http://doc.jsfiddle.net/ * libLAS: https://www.liblas.org/ (customized) +* Lmod: https://lmod.readthedocs.io/ * MapServer: http://mapserver.org/ (customized) +* Pandas: https://pandas.pydata.org/pandas-docs/stable/ +* pyglet: https://pyglet.readthedocs.io/ (customized) * Setuptools: https://setuptools.readthedocs.io/ * Spring Python: https://docs.spring.io/spring-python/1.2.x/sphinx/html/ +* StatsModels: http://www.statsmodels.org/ (customized) * Sylli: http://sylli.sourceforge.net/ Documentation using another builtin theme @@ -133,6 +143,7 @@ Documentation using another builtin theme * Breathe: https://breathe.readthedocs.io/ (haiku) * MPipe: https://vmlaker.github.io/mpipe/ (sphinx13) +* NLTK: http://www.nltk.org/ (agogo) * Programmieren mit PyGTK und Glade (German): http://www.florian-diesch.de/doc/python-und-glade/online/ (agogo, customized) * PyPubSub: https://pypubsub.readthedocs.io/ (bizstyle) @@ -150,8 +161,10 @@ Documentation using sphinx_rtd_theme * ASE: https://wiki.fysik.dtu.dk/ase/ * Autofac: http://docs.autofac.org/ * BigchainDB: https://docs.bigchaindb.com/ +* Blocks: https://blocks.readthedocs.io/ * bootstrap-datepicker: https://bootstrap-datepicker.readthedocs.io/ * Certbot: https://letsencrypt.readthedocs.io/ +* Chainer: https://docs.chainer.org/ (customized) * CherryPy: http://docs.cherrypy.org/ * Chainer: https://docs.chainer.org/ * CodeIgniter: https://www.codeigniter.com/user_guide/ @@ -178,14 +191,18 @@ Documentation using sphinx_rtd_theme * Idris: http://docs.idris-lang.org/ * javasphinx: https://bronto-javasphinx.readthedocs.io/ * Julia: https://julia.readthedocs.io/ +* Jupyter Notebook: https://jupyter-notebook.readthedocs.io/ +* Lasagne: https://lasagne.readthedocs.io/ * Linguistica: https://linguistica-uchicago.github.io/lxa5/ * Linux kernel: https://www.kernel.org/doc/html/latest/index.html * MathJax: https://docs.mathjax.org/ * MDTraj: http://mdtraj.org/latest/ (customized) * MICrobial Community Analysis (micca): http://micca.org/docs/latest/ * MicroPython: https://docs.micropython.org/ +* Minds: https://www.minds.org/docs/ (customized) * Mink: http://mink.behat.org/ * Mockery: http://docs.mockery.io/ +* mod_wsgi: https://modwsgi.readthedocs.io/ * MoinMoin: https://moin-20.readthedocs.io/ * Mopidy: https://docs.mopidy.com/ * MyHDL: http://docs.myhdl.org/ @@ -224,13 +241,16 @@ Documentation using sphinx_rtd_theme * Sylius: http://docs.sylius.org/ * Tango Controls: https://tango-controls.readthedocs.io/ (customized) * Topshelf: http://docs.topshelf-project.com/ +* Theano: http://www.deeplearning.net/software/theano/ * ThreatConnect: https://docs.threatconnect.com/ * Tuleap: https://tuleap.net/doc/en/ * TYPO3: https://docs.typo3.org/ (customized) +* uWSGI: https://uwsgi-docs.readthedocs.io/ * Wagtail: http://docs.wagtail.io/ * Web Application Attack and Audit Framework (w3af): http://docs.w3af.org/ * Weblate: https://docs.weblate.org/ * x265: https://x265.readthedocs.io/ +* ZeroNet: https://zeronet.readthedocs.io/ Documentation using sphinx_bootstrap_theme ------------------------------------------ @@ -245,12 +265,14 @@ Documentation using sphinx_bootstrap_theme * Open Dylan: https://opendylan.org/documentation/ * Pootle: http://docs.translatehouse.org/projects/pootle/ * PyUblas: https://documen.tician.de/pyublas/ +* seaborn: https://seaborn.pydata.org/ Documentation using a custom theme or integrated in a website ------------------------------------------------------------- * Apache Cassandra: https://cassandra.apache.org/doc/ * Astropy: http://docs.astropy.org/ +* Bokeh: https://bokeh.pydata.org/ * Boto 3: https://boto3.readthedocs.io/ * CakePHP: https://book.cakephp.org/ * CasperJS: http://docs.casperjs.org/ @@ -263,6 +285,7 @@ Documentation using a custom theme or integrated in a website * Enterprise Toolkit for Acrobat products: https://www.adobe.com/devnet-docs/acrobatetk/ * Gameduino: http://excamera.com/sphinx/gameduino/ +* gensim: https://radimrehurek.com/gensim/ * GeoServer: http://docs.geoserver.org/ * gevent: http://www.gevent.org/ * GHC - Glasgow Haskell Compiler: http://downloads.haskell.org/~ghc/master/users-guide/ @@ -307,6 +330,7 @@ Documentation using a custom theme or integrated in a website * Sulu: http://docs.sulu.io/ * SQLAlchemy: https://docs.sqlalchemy.org/ * tinyTiM: http://tinytim.sourceforge.net/docs/2.0/ +* Twisted: http://twistedmatrix.com/documents/current/ * Ubuntu Packaging Guide: http://packaging.ubuntu.com/html/ * WebFaction: https://docs.webfaction.com/ * WTForms: https://wtforms.readthedocs.io/ @@ -320,8 +344,10 @@ Homepages and other non-documentation sites * Benoit Boissinot: https://bboissin.appspot.com/ (classic, customized) * Computer Networks, Parallelization, and Simulation Laboratory (CNPSLab): https://lab.miletic.net/ (sphinx_rtd_theme) +* Deep Learning Tutorials: http://www.deeplearning.net/tutorial/ (sphinxdoc) * Loyola University Chicago COMP 339-439 Distributed Systems course: http://books.cs.luc.edu/distributedsystems/ (sphinx_bootstrap_theme) +* Pylearn2: http://www.deeplearning.net/software/pylearn2/ (sphinxdoc, customized) * SciPy Cookbook: https://scipy-cookbook.readthedocs.io/ (sphinx_rtd_theme) * The Wine Cellar Book: https://www.thewinecellarbook.com/doc/en/ (sphinxdoc) * Thomas Cokelaer's Python, Sphinx and reStructuredText tutorials: From 464f94c2380b4cb2600735c0c0085e771da2bce4 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 17 Dec 2017 15:25:44 +0900 Subject: [PATCH 19/37] deprecate formatargspec() and format_annotation() --- CHANGES | 2 ++ sphinx/ext/autodoc/inspector.py | 8 ++++++ tests/test_autodoc.py | 50 --------------------------------- 3 files changed, 10 insertions(+), 50 deletions(-) diff --git a/CHANGES b/CHANGES index ed76ea4a7..2ec7d56bb 100644 --- a/CHANGES +++ b/CHANGES @@ -19,6 +19,8 @@ Deprecated * using a string value for :confval:`html_sidebars` is deprecated and only list values will be accepted at 2.0. +* ``format_annotation()`` and ``formatargspec()`` is deprecated. Please use + ``sphinx.util.inspect.Signature`` instead. Features added -------------- diff --git a/sphinx/ext/autodoc/inspector.py b/sphinx/ext/autodoc/inspector.py index 5d157c797..50c5a9082 100644 --- a/sphinx/ext/autodoc/inspector.py +++ b/sphinx/ext/autodoc/inspector.py @@ -10,9 +10,11 @@ """ import typing +import warnings from six import StringIO, string_types +from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.util.inspect import object_description if False: @@ -29,6 +31,9 @@ def format_annotation(annotation): Displaying complex types from ``typing`` relies on its private API. """ + warnings.warn('format_annotation() is now deprecated. ' + 'Please use sphinx.util.inspect.Signature instead.', + RemovedInSphinx20Warning) if isinstance(annotation, typing.TypeVar): # type: ignore return annotation.__name__ if annotation == Ellipsis: @@ -107,6 +112,9 @@ def formatargspec(function, args, varargs=None, varkw=None, defaults=None, An enhanced version of ``inspect.formatargspec()`` that handles typing annotations better. """ + warnings.warn('formatargspec() is now deprecated. ' + 'Please use sphinx.util.inspect.Signature instead.', + RemovedInSphinx20Warning) def format_arg_with_annotation(name): # type: (str) -> str diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 11958800a..989c367b6 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -1108,53 +1108,3 @@ class EnumCls(enum.Enum): val2 = 23 #: doc for val2 val3 = 34 """doc for val3""" - - -def test_type_hints(): - from sphinx.ext.autodoc import formatargspec - from sphinx.util.inspect import getargspec - - try: - from typing_test_data import f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11 - except (ImportError, SyntaxError): - pytest.skip('Cannot import Python code with function annotations') - - def verify_arg_spec(f, expected): - assert formatargspec(f, *getargspec(f)) == expected - - # Class annotations - verify_arg_spec(f0, '(x: int, y: numbers.Integral) -> None') - - # Generic types with concrete parameters - verify_arg_spec(f1, '(x: typing.List[int]) -> typing.List[int]') - - # TypeVars and generic types with TypeVars - verify_arg_spec(f2, '(x: typing.List[T],' - ' y: typing.List[T_co],' - ' z: T) -> typing.List[T_contra]') - - # Union types - verify_arg_spec(f3, '(x: typing.Union[str, numbers.Integral]) -> None') - - # Quoted annotations - verify_arg_spec(f4, '(x: str, y: str) -> None') - - # Keyword-only arguments - verify_arg_spec(f5, '(x: int, *, y: str, z: str) -> None') - - # Keyword-only arguments with varargs - verify_arg_spec(f6, '(x: int, *args, y: str, z: str) -> None') - - # Space around '=' for defaults - verify_arg_spec(f7, '(x: int = None, y: dict = {}) -> None') - - # Callable types - verify_arg_spec(f8, '(x: typing.Callable[[int, str], int]) -> None') - verify_arg_spec(f9, '(x: typing.Callable) -> None') - - # Tuple types - verify_arg_spec(f10, '(x: typing.Tuple[int, str],' - ' y: typing.Tuple[int, ...]) -> None') - - # Instance annotations - verify_arg_spec(f11, '(x: CustomAnnotation, y: 123) -> None') From 41f9cb200acfa506b8cebed37841fa39a625a719 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 17 Dec 2017 23:44:28 +0900 Subject: [PATCH 20/37] Fix doc: now eq role works whole of project --- doc/ext/math.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/ext/math.rst b/doc/ext/math.rst index 2aad7853f..66c24e902 100644 --- a/doc/ext/math.rst +++ b/doc/ext/math.rst @@ -102,8 +102,7 @@ or use Python raw strings (``r"raw"``). .. rst:role:: eq - Role for cross-referencing equations via their label. This currently works - only within the same document. Example:: + Role for cross-referencing equations via their label. Example:: .. math:: e^{i\pi} + 1 = 0 :label: euler From 46b18962283d4ed7cab4e87a737ab9001d6933aa Mon Sep 17 00:00:00 2001 From: jfbu Date: Sun, 17 Dec 2017 17:56:03 +0100 Subject: [PATCH 21/37] Again rewording docs about numfig_secnum_depth --- doc/config.rst | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/doc/config.rst b/doc/config.rst index fc87c4ddc..8f4656060 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -335,14 +335,21 @@ General configuration .. confval:: numfig_secnum_depth - The scope for numbering: ``0`` means "continuous numbering", - ``1`` means "reset per section" - (i.e. numbers will be x.1, x.2, x.3 ... with x the section number, - assuming that the :rst:dir:`toctree` directive was used with its option - ``:numbered:``), - ``2`` means "per subsection" - (i.e. numbers will be x.y.1, x.y.2, ...), - and so on. Default is ``1``. + - if set to ``0``, figures, tables and code-blocks are continuously numbered + starting at ``1``. + - if ``1`` (default) numbers will be ``x.1``, ``x.2``, ... with ``x`` + the section number (top level sectioning; no ``x.`` if no section). + This naturally applies only if section numbering has been activated via + the ``:numbered:`` option of the :rst:dir:`toctree` directive. + - ``2`` means that numbers will be ``x.y.1``, ``x.y.2``, ... if located in + a sub-section (but still ``x.1``, ``x.2``, ... if located directly under a + section and ``1``, ``2``, ... if not in any top level section.) + - etc... + + .. note:: + + The LaTeX builder currently ignores this configuration setting. It will + obey it at Sphinx 1.7. .. versionadded:: 1.3 From a102e8e29b23e08fb779c8153f03beb6fa8fe196 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Sat, 21 Oct 2017 12:19:04 +0100 Subject: [PATCH 22/37] setup.py: Include 'flake8' in 'test' requirements We are using this in our testing and actually need it for the flake8 plugin, so include this in the list of 'test' requirements. Signed-off-by: Stephen Finucane --- .travis.yml | 1 - setup.py | 1 + tox.ini | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1d065b178..a5214057d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,6 @@ install: - pip install -U pip setuptools - pip install docutils==$DOCUTILS - pip install .[test,websupport] - - pip install flake8 - if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then python3.6 -m pip install mypy typed-ast; fi script: - flake8 diff --git a/setup.py b/setup.py index 10a513166..5c0b70959 100644 --- a/setup.py +++ b/setup.py @@ -72,6 +72,7 @@ extras_require = { 'pytest', 'pytest-cov', 'html5lib', + 'flake8', ], 'test:python_version<"3"': [ 'enum34', diff --git a/tox.ini b/tox.ini index 00b3c99e2..2ea05641b 100644 --- a/tox.ini +++ b/tox.ini @@ -38,7 +38,6 @@ deps= {[testenv]deps} [testenv:flake8] -deps=flake8 commands=flake8 [testenv:pylint] From 41c19ddf91018e2b9a8c0001a24a78f3ecb893be Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 2 Oct 2017 22:28:22 +0100 Subject: [PATCH 23/37] tests: Skip tests with missing binaries While there are already some skips included here, they clearly aren't doing their job and the test fail locally. Resolve this. Signed-off-by: Stephen Finucane --- tests/test_ext_math.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_ext_math.py b/tests/test_ext_math.py index 92501a3db..4d2fb9836 100644 --- a/tests/test_ext_math.py +++ b/tests/test_ext_math.py @@ -9,11 +9,25 @@ :license: BSD, see LICENSE for details. """ +import os import re +import subprocess import pytest +def has_binary(binary): + try: + subprocess.check_output([binary]) + except OSError as e: + if e.errno == os.errno.ENOENT: + # handle file not found error. + return False + else: + return True + return True + + @pytest.mark.sphinx( 'html', testroot='ext-math', confoverrides = {'extensions': ['sphinx.ext.jsmath'], 'jsmath_path': 'dummy.js'}) @@ -34,6 +48,8 @@ def test_jsmath(app, status, warning): assert '
\na + 1 < b
' in content +@pytest.mark.skipif(not has_binary('dvipng'), + reason='Requires dvipng" binary') @pytest.mark.sphinx('html', testroot='ext-math-simple', confoverrides = {'extensions': ['sphinx.ext.imgmath']}) def test_imgmath_png(app, status, warning): @@ -49,6 +65,8 @@ def test_imgmath_png(app, status, warning): assert re.search(html, content, re.S) +@pytest.mark.skipif(not has_binary('dvisvgm'), + reason='Requires dvisvgm" binary') @pytest.mark.sphinx('html', testroot='ext-math-simple', confoverrides={'extensions': ['sphinx.ext.imgmath'], 'imgmath_image_format': 'svg'}) From e243e827236467b67b057b83131248ef1742aacb Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 26 Oct 2017 16:49:44 +0100 Subject: [PATCH 24/37] tests: Ignore tests using 'collect_ignore' Per the pytest docs [1], this is the preferred way to ignore tests. This necessitates removing the 'test-async' target as it no longer makes any sense. [1] https://docs.pytest.org/en/latest/example/pythoncollection.html Signed-off-by: Stephen Finucane --- .travis.yml | 2 +- Makefile | 4 ++-- tests/conftest.py | 5 +++++ tox.ini | 15 ++------------- 4 files changed, 10 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index a5214057d..1d6694b3d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,5 +42,5 @@ install: - if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then python3.6 -m pip install mypy typed-ast; fi script: - flake8 - - if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then make type-check test-async; fi + - if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then make type-check test; fi - if [[ $TRAVIS_PYTHON_VERSION != '3.6' ]]; then make test; fi diff --git a/Makefile b/Makefile index 5b3d5aad4..e4abba088 100644 --- a/Makefile +++ b/Makefile @@ -69,11 +69,11 @@ reindent: .PHONY: test test: - @cd tests; $(PYTHON) run.py --ignore py35 -v $(TEST) + @cd tests; $(PYTHON) run.py -v $(TEST) .PHONY: test-async test-async: - @cd tests; $(PYTHON) run.py -v $(TEST) + @echo "This target no longer does anything and will be removed imminently" .PHONY: covertest covertest: diff --git a/tests/conftest.py b/tests/conftest.py index 28dbd6ed4..c9719bf80 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,12 +8,17 @@ """ import os +import sys import pytest from sphinx.testing.path import path pytest_plugins = 'sphinx.testing.fixtures' +# Disable Python version-specific +if sys.version_info < (3, 5): + collect_ignore = ['py35'] + @pytest.fixture(scope='session') def rootdir(): diff --git a/tox.ini b/tox.ini index 2ea05641b..051b56381 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] minversion=2.0 -envlist=flake8,mypy,py{27,34,35,36},pypy,du{11,12,13,14} +envlist=docs,flake8,mypy,py{27,34,35,36},pypy,du{11,12,13,14} [testenv] passenv = https_proxy http_proxy no_proxy @@ -13,9 +13,7 @@ deps = setenv = SPHINX_TEST_TEMPDIR = {envdir}/testbuild commands= - {envpython} -Wall tests/run.py --ignore tests/py35 --cov=sphinx \ - --durations 25 {posargs} - sphinx-build -q -W -b html -d {envtmpdir}/doctrees doc {envtmpdir}/html + {envpython} -Wall tests/run.py --cov sphinx --durations 25 {posargs} [testenv:du11] deps= @@ -47,15 +45,6 @@ deps= commands= pylint --rcfile utils/pylintrc sphinx -[testenv:py27] -deps= - {[testenv]deps} - -[testenv:py35] -commands= - {envpython} -Wall tests/run.py --cov=sphinx --durations 25 {posargs} - sphinx-build -q -W -b html -d {envtmpdir}/doctrees doc {envtmpdir}/html - [testenv:mypy] basepython=python3 deps= From c8d56236c9286adca9664782debf3b1c768ef92d Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 26 Oct 2017 16:59:46 +0100 Subject: [PATCH 25/37] tests: Ignore roots using 'collect_ignore' This is slightly cleaner than how we're doing this at the moment. Signed-off-by: Stephen Finucane --- tests/conftest.py | 5 ++++- tests/run.py | 11 +---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index c9719bf80..9ea99dbd9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -15,9 +15,12 @@ from sphinx.testing.path import path pytest_plugins = 'sphinx.testing.fixtures' +# Exclude 'roots' dirs for pytest test collector +collect_ignore = ['roots'] + # Disable Python version-specific if sys.version_info < (3, 5): - collect_ignore = ['py35'] + collect_ignore += ['py35'] @pytest.fixture(scope='session') diff --git a/tests/run.py b/tests/run.py index a8439ba02..2116e345c 100755 --- a/tests/run.py +++ b/tests/run.py @@ -55,14 +55,5 @@ os.makedirs(tempdir) print('Running Sphinx test suite (with Python %s)...' % sys.version.split()[0]) sys.stdout.flush() -# exclude 'roots' dirs for pytest test collector -ignore_paths = [ - os.path.relpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), sub)) - for sub in ('roots',) -] -args = sys.argv[1:] -for ignore_path in ignore_paths: - args.extend(['--ignore', ignore_path]) - import pytest # NOQA -sys.exit(pytest.main(args)) +sys.exit(pytest.main(sys.argv[1:])) From 5d812ccefc034e8cc93223454f3e6fa36528546a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 26 Oct 2017 18:11:13 +0100 Subject: [PATCH 26/37] tox: Add 'coverage' target Enabling coverage results in an increase in runtime, and while this may be an acceptable compromise for some developers, it's not necessarily something we need to run all the time. Move it to a separate target so it can be run only by those who wish to use it. Signed-off-by: Stephen Finucane --- tox.ini | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 051b56381..05074761e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] minversion=2.0 -envlist=docs,flake8,mypy,py{27,34,35,36},pypy,du{11,12,13,14} +envlist=docs,flake8,mypy,coverage,py{27,34,35,36,py},du{11,12,13,14} [testenv] passenv = https_proxy http_proxy no_proxy @@ -12,8 +12,9 @@ deps = .[test,websupport] setenv = SPHINX_TEST_TEMPDIR = {envdir}/testbuild + coverage: PYTEST_ADDOPTS = --cov sphinx commands= - {envpython} -Wall tests/run.py --cov sphinx --durations 25 {posargs} + {envpython} -Wall tests/run.py --durations 25 {posargs} [testenv:du11] deps= From fd1df0815b8fd83cd7918a9fcf5eea734161f336 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 27 Oct 2017 10:11:10 +0100 Subject: [PATCH 27/37] tox: Make further use of factor-conditional deps This is how we reduce boilerplate [1]. Succinctness FTW. [1] https://tox.readthedocs.io/en/latest/config.html#factors-and-factor-conditional-settings Signed-off-by: Stephen Finucane --- tox.ini | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/tox.ini b/tox.ini index 05074761e..22a34f6f8 100644 --- a/tox.ini +++ b/tox.ini @@ -10,32 +10,16 @@ passenv = https_proxy http_proxy no_proxy # https://tox.readthedocs.io/en/latest/config.html#confval-extras=MULTI-LINE-LIST deps = .[test,websupport] + du11: docutils==0.11 + du12: docutils==0.12 + du13: docutils==0.13.1 + du14: docutils==0.14 setenv = SPHINX_TEST_TEMPDIR = {envdir}/testbuild coverage: PYTEST_ADDOPTS = --cov sphinx commands= {envpython} -Wall tests/run.py --durations 25 {posargs} -[testenv:du11] -deps= - docutils==0.11 - {[testenv]deps} - -[testenv:du12] -deps= - docutils==0.12 - {[testenv]deps} - -[testenv:du13] -deps= - docutils==0.13.1 - {[testenv]deps} - -[testenv:du14] -deps= - docutils==0.14 - {[testenv]deps} - [testenv:flake8] commands=flake8 From f74b6756ac80e7a07f2b7c8a7f304e245404c116 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Sat, 21 Oct 2017 14:19:28 +0100 Subject: [PATCH 28/37] setup.cfg: Restructure file Place configuration of the package itself first in the file, followed by configuration of other tools. Signed-off-by: Stephen Finucane --- setup.cfg | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/setup.cfg b/setup.cfg index cb6887fc3..00e7833d3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,11 +1,20 @@ +[metadata] +license_file = LICENSE + [egg_info] tag_build = .dev tag_date = true +[bdist_wheel] +universal = 1 + [aliases] release = egg_info -Db '' upload = upload --sign --identity=36580288 +[build_sphinx] +warning-is-error = 1 + [extract_messages] mapping_file = babel.cfg output_file = sphinx/locale/sphinx.pot @@ -20,12 +29,6 @@ output_dir = sphinx/locale/ domain = sphinx directory = sphinx/locale/ -[bdist_wheel] -universal = 1 - -[metadata] -license_file = LICENSE - [flake8] max-line-length = 95 ignore = E116,E241,E251,E741 @@ -40,6 +43,3 @@ follow_imports = skip incremental = True check_untyped_defs = True warn_unused_ignores = True - -[build_sphinx] -warning-is-error = 1 From 393f59da004baaac3a4d1f03a0b4585ed71f76f7 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Sat, 21 Oct 2017 15:20:21 +0100 Subject: [PATCH 29/37] Improve README We're going to be adding additional badges to this but, before we do that, we should improve the README and remove a lot of the duplication that has built up here. The 'long_description' from 'setup.py' is moved into the README as there is serious value in displaying this on GitHub, as much as there is value in including the badges and other content from README. Signed-off-by: Stephen Finucane --- README.rst | 111 +++++++++++++++++++++++++++++------------------------ setup.py | 30 +-------------- 2 files changed, 63 insertions(+), 78 deletions(-) diff --git a/README.rst b/README.rst index 1e027ec8e..54493bb8b 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,7 @@ +======== + Sphinx +======== + .. image:: https://img.shields.io/pypi/v/sphinx.svg :target: https://pypi.python.org/pypi/Sphinx .. image:: https://readthedocs.org/projects/sphinx/badge/ @@ -6,40 +10,81 @@ .. image:: https://travis-ci.org/sphinx-doc/sphinx.svg?branch=master :target: https://travis-ci.org/sphinx-doc/sphinx -================= -README for Sphinx -================= +Sphinx is a tool that makes it easy to create intelligent and beautiful +documentation for Python projects (or other documents consisting of multiple +reStructuredText sources), written by Georg Brandl. It was originally created +for the new Python documentation, and has excellent facilities for Python +project documentation, but C/C++ is supported as well, and more languages are +planned. -This is the Sphinx documentation generator, see http://www.sphinx-doc.org/. +Sphinx uses reStructuredText as its markup language, and many of its strengths +come from the power and straightforwardness of reStructuredText and its parsing +and translating suite, the Docutils. +Among its features are the following: -Installing -========== +* Output formats: HTML (including derivative formats such as HTML Help, Epub + and Qt Help), plain text, manual pages and LaTeX or direct PDF output + using rst2pdf +* Extensive cross-references: semantic markup and automatic links + for functions, classes, glossary terms and similar pieces of information +* Hierarchical structure: easy definition of a document tree, with automatic + links to siblings, parents and children +* Automatic indices: general index as well as a module index +* Code handling: automatic highlighting using the Pygments highlighter +* Flexible HTML output using the Jinja 2 templating engine +* Various extensions are available, e.g. for automatic testing of snippets + and inclusion of appropriately formatted docstrings +* Setuptools integration -Install from PyPI to use stable version:: +For more information, refer to the `the documentation`__. + +__ http://www.sphinx-doc.org/ + +Installation +============ + +Sphinx is published on `PyPI`__ and can be installed from there:: pip install -U sphinx -Install from PyPI to use beta version:: +We also publish beta releases:: pip install -U --pre sphinx -Install from newest dev version in stable branch:: +If you wish to install `Sphinx` for development purposes, refer to `the +contributors guide`__. - pip install git+https://github.com/sphinx-doc/sphinx@stable +__ https://pypi.python.org/pypi/Sphinx +__ CONTRIBUTING.rst -Install from newest dev version in master branch:: +Documentation +============= - pip install git+https://github.com/sphinx-doc/sphinx +Documentation is available from `sphinx-doc.org`__. -Install from cloned source:: +__ http://www.sphinx-doc.org/ - pip install . +Testing +======= -Install from cloned source as editable:: +Continuous testing is provided by `Travis`__ (for unit tests and style checks +on Linux), `AppVeyor`__ (for unit tests on Windows), and `CircleCI`__ (for +large processes like TeX compilation). - pip install -e . +For information on running tests locally, refer to `the contributors guide`__. +__ https://travis-ci.org/sphinx-doc/sphinx +__ https://ci.appveyor.com/project/sphinxdoc/sphinx +__ https://circleci.com/gh/sphinx-doc/sphinx +__ CONTRIBUTING.rst + +Contributing +============ + +Refer to `the contributors guide`_. + +__ CONTRIBUTING.rst Release signatures ================== @@ -48,37 +93,3 @@ Releases are signed with following keys: * `498D6B9E `_ * `5EBA0E07 `_ - -Reading the docs -================ - -You can read them online at . - -Or, after installing:: - - cd doc - make html - -Then, direct your browser to ``_build/html/index.html``. - -Testing -======= - -To run the tests with the interpreter available as ``python``, use:: - - make test - -If you want to use a different interpreter, e.g. ``python3``, use:: - - PYTHON=python3 make test - -Continuous testing runs on travis: https://travis-ci.org/sphinx-doc/sphinx - - -Contributing -============ - -See `CONTRIBUTING.rst`__ - -.. __: CONTRIBUTING.rst - diff --git a/setup.py b/setup.py index 5c0b70959..6b7de9129 100644 --- a/setup.py +++ b/setup.py @@ -8,34 +8,8 @@ from distutils.cmd import Command import sphinx -long_desc = ''' -Sphinx is a tool that makes it easy to create intelligent and beautiful -documentation for Python projects (or other documents consisting of multiple -reStructuredText sources), written by Georg Brandl. It was originally created -for the new Python documentation, and has excellent facilities for Python -project documentation, but C/C++ is supported as well, and more languages are -planned. - -Sphinx uses reStructuredText as its markup language, and many of its strengths -come from the power and straightforwardness of reStructuredText and its parsing -and translating suite, the Docutils. - -Among its features are the following: - -* Output formats: HTML (including derivative formats such as HTML Help, Epub - and Qt Help), plain text, manual pages and LaTeX or direct PDF output - using rst2pdf -* Extensive cross-references: semantic markup and automatic links - for functions, classes, glossary terms and similar pieces of information -* Hierarchical structure: easy definition of a document tree, with automatic - links to siblings, parents and children -* Automatic indices: general index as well as a module index -* Code handling: automatic highlighting using the Pygments highlighter -* Flexible HTML output using the Jinja 2 templating engine -* Various extensions are available, e.g. for automatic testing of snippets - and inclusion of appropriately formatted docstrings -* Setuptools integration -''' +with open('README.rst') as f: + long_desc = f.read() if sys.version_info < (2, 7) or (3, 0) <= sys.version_info < (3, 4): print('ERROR: Sphinx requires at least Python 2.7 or 3.4 to run.') From a6f0ba0fc95db043a08d4379481d69a9cb917453 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Sat, 21 Oct 2017 15:29:35 +0100 Subject: [PATCH 30/37] README: Cleanup badges Add AppVeyor badge and include 'alt' attributes for the others. Signed-off-by: Stephen Finucane --- README.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.rst b/README.rst index 54493bb8b..39c58143b 100644 --- a/README.rst +++ b/README.rst @@ -4,11 +4,23 @@ .. image:: https://img.shields.io/pypi/v/sphinx.svg :target: https://pypi.python.org/pypi/Sphinx + :alt: Package on PyPi + .. image:: https://readthedocs.org/projects/sphinx/badge/ :target: http://www.sphinx-doc.org/ :alt: Documentation Status + .. image:: https://travis-ci.org/sphinx-doc/sphinx.svg?branch=master :target: https://travis-ci.org/sphinx-doc/sphinx + :alt: Build Status (Travis CI) + +.. image:: https://ci.appveyor.com/api/projects/status/github/sphinx-doc/sphinx?branch=master&svg=true + :target: https://ci.appveyor.com/project/sphinxdoc/sphinx + :alt: Build Status (AppVeyor) + +.. image:: https://circleci.com/gh/sphinx-doc/sphinx.svg?style=shield + :target: https://circleci.com/gh/sphinx-doc/sphinx + :alt: Build Status (CircleCI) Sphinx is a tool that makes it easy to create intelligent and beautiful documentation for Python projects (or other documents consisting of multiple From 76bd63187232030b7cff56153e7a648a190eebb6 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 26 Oct 2017 18:15:58 +0100 Subject: [PATCH 31/37] travis: Start using tox for testing This allows us to validate and reuse the testing tools we use locally for "gate" tests and moves us closer to the eventual goal of deprecating the Makefile targets. This involves adding two new targets to the list of default targets run by tox - docs and mypy. Note that we don't use tox-travis because, wonderful though it may be, configuring it to exclude as many of the tests as we want to exclude is a little convoluted (we have a big test matrix). Signed-off-by: Stephen Finucane --- .travis.yml | 53 ++++++++++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1d6694b3d..900853cf7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,45 +2,40 @@ language: python sudo: false dist: trusty cache: pip -python: - - "pypy-5.4.1" - - "3.6" - - "3.5" - - "3.4" - - "2.7" - - "nightly" + env: global: - - TEST='-v --durations 25' - PYTHONFAULTHANDLER=x - PYTHONWARNINGS=all - SKIP_LATEX_BUILD=1 - matrix: - - DOCUTILS=0.13.1 - - DOCUTILS=0.14 + matrix: - exclude: - - python: "3.4" - env: DOCUTILS=0.13.1 - - python: "3.5" - env: DOCUTILS=0.13.1 - - python: "3.6" - env: DOCUTILS=0.13.1 - - python: nightly - env: DOCUTILS=0.13.1 - - python: "pypy-5.4.1" - env: DOCUTILS=0.13.1 + include: + - python: 'pypy' + env: TOXENV=pypy + - python: '2.7' + env: TOXENV=du13 + - python: '3.4' + env: TOXENV=py34 + - python: '3.5' + env: TOXENV=py35 + - python: '3.6' + env: TOXENV=py36 + - python: 'nightly' + env: TOXENV=py37 + - python: '3.6' + env: TOXENV=mypy + - python: '2.7' + env: TOXENV=flake8 + addons: apt: packages: - graphviz - imagemagick + install: - - pip install -U pip setuptools - - pip install docutils==$DOCUTILS - - pip install .[test,websupport] - - if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then python3.6 -m pip install mypy typed-ast; fi + - pip install -U tox + script: - - flake8 - - if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then make type-check test; fi - - if [[ $TRAVIS_PYTHON_VERSION != '3.6' ]]; then make test; fi + - tox -- -v From 638e6467669d5ebc2111a5f7fb2c5612a8bcf1e5 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 3 Oct 2017 10:00:40 +0100 Subject: [PATCH 32/37] doc: Rework CONTRIBUTING to encourage use of tox Embrace the tox-ified future. This explicitly asks users to use a virtualenv to stop them shooting themselves in the foot. Signed-off-by: Stephen Finucane --- CONTRIBUTING.rst | 68 +++++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 7c8a90c6b..c4b8569b0 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -33,10 +33,10 @@ Bug Reports and Feature Requests If you have encountered a problem with Sphinx or have an idea for a new feature, please submit it to the `issue tracker`_ on GitHub or discuss it -on the sphinx-dev mailing list. +on the `sphinx-dev`_ mailing list. For bug reports, please include the output produced during the build process -and also the log file Sphinx creates after it encounters an un-handled +and also the log file Sphinx creates after it encounters an unhandled exception. The location of this file should be shown towards the end of the error message. @@ -45,6 +45,7 @@ issue. If possible, try to create a minimal project that produces the error and post that instead. .. _`issue tracker`: https://github.com/sphinx-doc/sphinx/issues +.. _`sphinx-dev`: mailto:sphinx-dev@googlegroups.com Contributing to Sphinx @@ -58,7 +59,7 @@ of the core developers before it is merged into the main repository. #. Check for open issues or open a fresh issue to start a discussion around a feature idea or a bug. #. If you feel uncomfortable or uncertain about an issue or your changes, feel - free to email sphinx-dev@googlegroups.com. + free to email the *sphinx-dev* mailing list. #. Fork `the repository`_ on GitHub to start making your changes to the **master** branch for next major version, or **stable** branch for next minor version. @@ -98,10 +99,14 @@ These are the basic steps needed to start developing on Sphinx. For new features or other substantial changes that should wait until the next major release, use the ``master`` branch. -#. Optional: setup a virtual environment. :: +#. Setup a virtual environment. - virtualenv ~/sphinxenv - . ~/sphinxenv/bin/activate + This is not necessary for unit testing, thanks to ``tox``, but it is + necessary if you wish to run ``sphinx-build`` locally or run unit tests + without the help of ``tox``. :: + + virtualenv ~/.venv + . ~/.venv/bin/activate pip install -e . #. Create a new working branch. Choose any name you like. :: @@ -112,44 +117,53 @@ These are the basic steps needed to start developing on Sphinx. For tips on working with the code, see the `Coding Guide`_. -#. Test, test, test. Possible steps: +#. Test, test, test. - * Run the unit tests:: + Testing is best done through ``tox``, which provides a number of targets and + allows testing against multiple different Python environments: - pip install .[test,websupport] - make test + * To list all possible targets:: - * Again, it's useful to turn on deprecation warnings on so they're shown in - the test output:: + tox -av - PYTHONWARNINGS=all make test + * To run unit tests for a specific Python version, such as 3.6:: - * Arguments to pytest can be passed via tox, e.g. in order to run a + tox -e py36 + + * To run unit tests for a specific Python version and turn on deprecation + warnings on so they're shown in the test output:: + + PYTHONWARNINGS=all tox -e py36 + + * To run code style and type checks:: + + tox -e mypy + tox -e flake8 + + * Arguments to ``pytest`` can be passed via ``tox``, e.g. in order to run a particular test:: - tox -e py27 tests/test_module.py::test_new_feature + tox -e py36 tests/test_module.py::test_new_feature - * Build the documentation and check the output for different builders:: + * To build the documentation:: - make docs target="clean html latexpdf" + tox -e docs - * Run code style checks and type checks (type checks require mypy):: + * To build the documentation in multiple formats:: - make style-check - make type-check + tox -e docs -- -b html,latexpdf - * Run the unit tests under different Python environments using - :program:`tox`:: + You can also test by installing dependencies in your local environment. :: - pip install tox - tox -v + pip install .[test] - * Add a new unit test in the ``tests`` directory if you can. + New unit tests should be included in the ``tests`` directory where + necessary: * For bug fixes, first add a test that fails without your changes and passes after they are applied. - * Tests that need a sphinx-build run should be integrated in one of the + * Tests that need a ``sphinx-build`` run should be integrated in one of the existing test modules if possible. New tests that to ``@with_app`` and then ``build_all`` for a few assertions are not good since *the test suite should not take more than a minute to run*. @@ -266,7 +280,7 @@ Debugging Tips code by running the command ``make clean`` or using the :option:`sphinx-build -E` option. -* Use the :option:`sphinx-build -P` option to run Pdb on exceptions. +* Use the :option:`sphinx-build -P` option to run ``pdb`` on exceptions. * Use ``node.pformat()`` and ``node.asdom().toxml()`` to generate a printable representation of the document structure. From a2a873d58afb91143c3b71c9037384458043c080 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 19 Dec 2017 15:55:10 +0000 Subject: [PATCH 33/37] tox: Add documentation for all targets This should make 'tox -av' more helpful. Signed-off-by: Stephen Finucane --- tox.ini | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/tox.ini b/tox.ini index 22a34f6f8..4b462d612 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,15 @@ [tox] -minversion=2.0 -envlist=docs,flake8,mypy,coverage,py{27,34,35,36,py},du{11,12,13,14} +minversion = 2.0 +envlist = docs,flake8,mypy,coverage,py{27,34,35,36,py},du{11,12,13,14} [testenv] -passenv = https_proxy http_proxy no_proxy +passenv = + https_proxy http_proxy no_proxy +description = + py{27,34,35,36,py}: Run unit tests against {envname}. + du{11,12,13,14}: Run unit tests with the given version of docutils. + coverage: Run code coverage checks. + # TODO(stephenfin) Replace this with the 'extras' config option when tox 2.4 is # widely available, likely some time after the Ubuntu 18.04 release # @@ -21,22 +27,30 @@ commands= {envpython} -Wall tests/run.py --durations 25 {posargs} [testenv:flake8] -commands=flake8 +description = + Run style checks. +commands = + flake8 [testenv:pylint] -deps= +description = + Run source code analyzer. +deps = pylint {[testenv]deps} -commands= +commands = pylint --rcfile utils/pylintrc sphinx [testenv:mypy] -basepython=python3 -deps= +description = + Run type checks. +deps = mypy commands= mypy sphinx/ [testenv:docs] -commands= +description = + Build documentation. +commands = python setup.py build_sphinx {posargs} From ac9ae4d54b0ad01dd62d94811046f6e3e9af860a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 19 Dec 2017 16:00:20 +0000 Subject: [PATCH 34/37] README: Fix typo Signed-off-by: Stephen Finucane Fixes: 393f59d ("Improve README") --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 39c58143b..79d8d6632 100644 --- a/README.rst +++ b/README.rst @@ -94,7 +94,7 @@ __ CONTRIBUTING.rst Contributing ============ -Refer to `the contributors guide`_. +Refer to `the contributors guide`__. __ CONTRIBUTING.rst From b103b3c24ac8d983498f1170d8e104f8cd72c3df Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 20 Dec 2017 01:28:07 +0900 Subject: [PATCH 35/37] doc: Use anonymous link target for README --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 79d8d6632..6419503e4 100644 --- a/README.rst +++ b/README.rst @@ -51,7 +51,7 @@ Among its features are the following: For more information, refer to the `the documentation`__. -__ http://www.sphinx-doc.org/ +.. __: http://www.sphinx-doc.org/ Installation ============ From 7054c0306aafb2cfa8abe4553729385dfb928ffe Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 19 Dec 2017 22:28:54 +0900 Subject: [PATCH 36/37] doc: Remove mentions to deprecated options (both are removed at 8c2fabe v1.3) --- doc/config.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/config.rst b/doc/config.rst index d748f5706..62c4ccc73 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -138,12 +138,10 @@ General configuration - ``'library/xml.rst'`` -- ignores the ``library/xml.rst`` file (replaces entry in :confval:`unused_docs`) - - ``'library/xml'`` -- ignores the ``library/xml`` directory (replaces entry - in :confval:`exclude_trees`) + - ``'library/xml'`` -- ignores the ``library/xml`` directory - ``'library/xml*'`` -- ignores all files and directories starting with ``library/xml`` - - ``'**/.svn'`` -- ignores all ``.svn`` directories (replaces entry in - :confval:`exclude_dirnames`) + - ``'**/.svn'`` -- ignores all ``.svn`` directories :confval:`exclude_patterns` is also consulted when looking for static files in :confval:`html_static_path` and :confval:`html_extra_path`. From c7a145cc1803787a71654fe052ec9005d6a6f0a9 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Wed, 20 Dec 2017 09:05:34 +0100 Subject: [PATCH 37/37] C++, handle defaulted constrained template type parameters --- sphinx/domains/cpp.py | 115 +++++++++++++++++++++++++++++++++++---- tests/test_domain_cpp.py | 4 ++ 2 files changed, 107 insertions(+), 12 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index c77c38413..971d6ad68 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -1314,6 +1314,44 @@ class ASTTemplateParamType(ASTBase): self.data.describe_signature(signode, mode, env, symbol) +class ASTTemplateParamConstrainedTypeWithInit(ASTBase): + def __init__(self, type, init): + # type: (Any, Any) -> None + assert type + self.type = type + self.init = init + + @property + def name(self): + # type: () -> ASTNestedName + return self.type.name + + def get_id(self, version, objectType=None, symbol=None): + # type: (int, unicode, Symbol) -> unicode + # this is not part of the normal name mangling in C++ + assert version >= 2 + if symbol: + # the anchor will be our parent + return symbol.parent.declaration.get_id(version, prefixed=False) + else: + return self.type.get_id(version) + + def __unicode__(self): + # type: () -> unicode + res = text_type(self.type) + if self.init: + res += " = " + res += text_type(self.init) + return res + + def describe_signature(self, signode, mode, env, symbol): + # type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None + self.type.describe_signature(signode, mode, env, symbol) + if self.init: + signode += nodes.Text(" = ") + self.init.describe_signature(signode, mode, env, symbol) + + class ASTTemplateParamTemplateType(ASTBase): def __init__(self, nestedParams, data): # type: (Any, Any) -> None @@ -3781,6 +3819,8 @@ class DefinitionParser(object): self.last_match = None # type: Match self._previous_state = (0, None) # type: Tuple[int, Match] self.otherErrors = [] # type: List[DefinitionError] + # in our tests the following is set to False to capture bad parsing + self.allowFallbackExpressionParsing = True self.warnEnv = warnEnv self.config = config @@ -4121,6 +4161,13 @@ class DefinitionParser(object): # TODO: hmm, would we need to try both with operatorCast and with None? prefix = self._parse_type(False, 'operatorCast') prefixType = 'typeOperatorCast' + # | simple-type-specifier "(" expression-list [opt] ")" + # | simple-type-specifier braced-init-list + # | typename-specifier "(" expression-list [opt] ")" + # | typename-specifier braced-init-list + self.skip_ws() + if self.current_char != '(' and self.current_char != '{': + self.fail("Expecting '(' or '{' after type in cast expression.") except DefinitionError as eInner: self.pos = pos header = "Error in postfix expression, expected primary expression or type." @@ -4361,7 +4408,7 @@ class DefinitionParser(object): # TODO: actually parse the second production return self._parse_assignment_expression(inTemplate=inTemplate) - def _parse_expression_fallback(self, end, parser): + def _parse_expression_fallback(self, end, parser, allow=True): # Stupidly "parse" an expression. # 'end' should be a list of characters which ends the expression. @@ -4370,6 +4417,10 @@ class DefinitionParser(object): try: return parser() except DefinitionError as e: + # some places (e.g., template parameters) we really don't want to use fallback, + # and for testing we may want to globally disable it + if not allow or not self.allowFallbackExpressionParsing: + raise self.warn("Parsing of expression failed. Using fallback parser." " Error was:\n%s" % e.description) self.pos = prevPos @@ -4598,7 +4649,7 @@ class DefinitionParser(object): self.fail('Expected ")" after "..." in ' 'parameters_and_qualifiers.') break - # note: it seems that function arguments can always sbe named, + # note: it seems that function arguments can always be named, # even in function pointers and similar. arg = self._parse_type_with_init(outer=None, named='single') # TODO: parse default parameters # TODO: didn't we just do that? @@ -4919,8 +4970,8 @@ class DefinitionParser(object): header = "Error in declarator or parameters and qualifiers" raise self._make_multi_error(prevErrors, header) - def _parse_initializer(self, outer=None): - # type: (unicode) -> ASTInitializer + def _parse_initializer(self, outer=None, allowFallback=True): + # type: (unicode, bool) -> ASTInitializer self.skip_ws() # TODO: support paren and brace initialization for memberObject if not self.skip_string('='): @@ -4929,15 +4980,18 @@ class DefinitionParser(object): if outer == 'member': def parser(): return self._parse_assignment_expression(inTemplate=False) - value = self._parse_expression_fallback([], parser) + value = self._parse_expression_fallback([], parser, + allow=allowFallback) elif outer == 'templateParam': def parser(): return self._parse_assignment_expression(inTemplate=True) - value = self._parse_expression_fallback([',', '>'], parser) + value = self._parse_expression_fallback([',', '>'], parser, + allow=allowFallback) elif outer is None: # function parameter def parser(): return self._parse_assignment_expression(inTemplate=False) - value = self._parse_expression_fallback([',', ')'], parser) + value = self._parse_expression_fallback([',', ')'], parser, + allow=allowFallback) else: self.fail("Internal error, initializer for outer '%s' not " "implemented." % outer) @@ -5027,12 +5081,48 @@ class DefinitionParser(object): return ASTType(declSpecs, decl) def _parse_type_with_init(self, named, outer): - # type: (Union[bool, unicode], unicode) -> ASTTypeWithInit + # type: (Union[bool, unicode], unicode) -> Any if outer: assert outer in ('type', 'member', 'function', 'templateParam') type = self._parse_type(outer=outer, named=named) - init = self._parse_initializer(outer=outer) - return ASTTypeWithInit(type, init) + if outer != 'templateParam': + init = self._parse_initializer(outer=outer) + return ASTTypeWithInit(type, init) + # it could also be a constrained type parameter, e.g., C T = int& + pos = self.pos + eExpr = None + try: + init = self._parse_initializer(outer=outer, allowFallback=False) + # note: init may be None if there is no = + if init is None: + return ASTTypeWithInit(type, None) + # we parsed an expression, so we must have a , or a >, + # otherwise the expression didn't get everything + self.skip_ws() + if self.current_char != ',' and self.current_char != '>': + # pretend it didn't happen + self.pos = pos + init = None + else: + # we assume that it was indeed an expression + return ASTTypeWithInit(type, init) + except DefinitionError as e: + self.pos = pos + eExpr = e + if not self.skip_string("="): + return ASTTypeWithInit(type, None) + try: + typeInit = self._parse_type(named=False, outer=None) + return ASTTemplateParamConstrainedTypeWithInit(type, typeInit) + except DefinitionError as eType: + if eExpr is None: + raise eType + errs = [] + errs.append((eExpr, "If default is an expression")) + errs.append((eType, "If default is a type")) + msg = "Error in non-type template parameter" + msg += " or constrianted template paramter." + raise self._make_multi_error(errs, msg) def _parse_type_using(self): # type: () -> ASTTypeUsing @@ -5156,13 +5246,14 @@ class DefinitionParser(object): param = ASTTemplateParamType(data) templateParams.append(param) else: - # declare a non-type parameter + # declare a non-type parameter, or constrained type parameter pos = self.pos try: param = self._parse_type_with_init('maybe', 'templateParam') templateParams.append(ASTTemplateParamNonType(param)) except DefinitionError as e: - prevErrors.append((e, "If non-type template parameter")) + msg = "If non-type template parameter or constrained template parameter" + prevErrors.append((e, msg)) self.pos = pos self.skip_ws() if self.skip_string('>'): diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index f3f0037f5..e4f3a678b 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -25,6 +25,7 @@ def parse(name, string): cpp_id_attributes = ["id_attr"] cpp_paren_attributes = ["paren_attr"] parser = DefinitionParser(string, None, Config()) + parser.allowFallbackExpressionParsing = False ast = parser.parse_declaration(name) parser.assert_end() # The scopedness would usually have been set by CPPEnumObject @@ -569,6 +570,9 @@ def test_templates(): check('member', 'template int A::B::b', {2: 'IEIEN1AIiE1BIiE1bE'}, output='template<> template<> int A::B::b') # same as above + # defaulted constrained type parameters + check('type', 'template A', {2:'I_1CE1A'}) + def test_template_args(): # from breathe#218