summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml9
-rw-r--r--.circleci/config.yml2
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml64
-rw-r--r--AUTHORS1
-rw-r--r--CHANGES87
-rw-r--r--CONTRIBUTING.rst87
-rw-r--r--EXAMPLES31
-rw-r--r--LICENSE2
-rw-r--r--Makefile6
-rw-r--r--README.rst133
-rw-r--r--doc/_templates/index.html6
-rw-r--r--doc/_templates/indexsidebar.html10
-rw-r--r--doc/_themes/sphinx13/layout.html2
-rw-r--r--doc/_themes/sphinx13/static/sphinx13.css32
-rw-r--r--doc/conf.py2
-rw-r--r--doc/config.rst124
-rw-r--r--doc/develop.rst2
-rw-r--r--doc/ext/autodoc.rst8
-rw-r--r--doc/ext/doctest.rst18
-rw-r--r--doc/ext/inheritance.rst48
-rw-r--r--doc/ext/math.rst14
-rw-r--r--doc/ext/napoleon.rst9
-rw-r--r--doc/ext/thirdparty.rst2
-rw-r--r--doc/extdev/markupapi.rst22
-rw-r--r--doc/faq.rst2
-rw-r--r--doc/intro.rst6
-rw-r--r--doc/latex.rst20
-rw-r--r--doc/man/sphinx-apidoc.rst10
-rw-r--r--doc/man/sphinx-build.rst11
-rw-r--r--doc/markup/code.rst6
-rw-r--r--doc/markup/inline.rst21
-rw-r--r--setup.cfg34
-rw-r--r--setup.py34
-rw-r--r--sphinx/__init__.py2
-rw-r--r--sphinx/__main__.py2
-rw-r--r--sphinx/addnodes.py2
-rw-r--r--sphinx/apidoc.py2
-rw-r--r--sphinx/application.py85
-rw-r--r--sphinx/builders/__init__.py20
-rw-r--r--sphinx/builders/_epub_base.py2
-rw-r--r--sphinx/builders/applehelp.py2
-rw-r--r--sphinx/builders/changes.py2
-rw-r--r--sphinx/builders/devhelp.py2
-rw-r--r--sphinx/builders/gettext.py2
-rw-r--r--sphinx/builders/html.py4
-rw-r--r--sphinx/builders/htmlhelp.py2
-rw-r--r--sphinx/builders/latex.py2
-rw-r--r--sphinx/builders/linkcheck.py2
-rw-r--r--sphinx/builders/manpage.py2
-rw-r--r--sphinx/builders/qthelp.py4
-rw-r--r--sphinx/builders/texinfo.py2
-rw-r--r--sphinx/builders/text.py2
-rw-r--r--sphinx/builders/websupport.py2
-rw-r--r--sphinx/builders/xml.py2
-rw-r--r--sphinx/cmd/__init__.py2
-rw-r--r--sphinx/cmd/build.py2
-rw-r--r--sphinx/cmd/quickstart.py207
-rw-r--r--sphinx/cmdline.py58
-rw-r--r--sphinx/config.py14
-rw-r--r--sphinx/deprecation.py2
-rw-r--r--sphinx/directives/__init__.py2
-rw-r--r--sphinx/directives/code.py2
-rw-r--r--sphinx/directives/other.py2
-rw-r--r--sphinx/directives/patches.py2
-rw-r--r--sphinx/domains/__init__.py31
-rw-r--r--sphinx/domains/c.py2
-rw-r--r--sphinx/domains/cpp.py645
-rw-r--r--sphinx/domains/javascript.py2
-rw-r--r--sphinx/domains/python.py2
-rw-r--r--sphinx/domains/rst.py2
-rw-r--r--sphinx/domains/std.py18
-rw-r--r--sphinx/environment/__init__.py68
-rw-r--r--sphinx/environment/adapters/__init__.py2
-rw-r--r--sphinx/environment/adapters/asset.py2
-rw-r--r--sphinx/environment/adapters/indexentries.py2
-rw-r--r--sphinx/environment/adapters/toctree.py2
-rw-r--r--sphinx/environment/collectors/__init__.py2
-rw-r--r--sphinx/environment/collectors/asset.py2
-rw-r--r--sphinx/environment/collectors/dependencies.py2
-rw-r--r--sphinx/environment/collectors/indexentries.py2
-rw-r--r--sphinx/environment/collectors/metadata.py2
-rw-r--r--sphinx/environment/collectors/title.py2
-rw-r--r--sphinx/environment/collectors/toctree.py4
-rw-r--r--sphinx/errors.py2
-rw-r--r--sphinx/events.py2
-rw-r--r--sphinx/ext/__init__.py2
-rw-r--r--sphinx/ext/apidoc.py28
-rw-r--r--sphinx/ext/autodoc/__init__.py373
-rw-r--r--sphinx/ext/autodoc/directive.py155
-rw-r--r--sphinx/ext/autodoc/importer.py109
-rw-r--r--sphinx/ext/autodoc/inspector.py20
-rw-r--r--sphinx/ext/autosectionlabel.py2
-rw-r--r--sphinx/ext/autosummary/__init__.py29
-rw-r--r--sphinx/ext/autosummary/generate.py54
-rw-r--r--sphinx/ext/coverage.py2
-rw-r--r--sphinx/ext/doctest.py41
-rw-r--r--sphinx/ext/extlinks.py2
-rw-r--r--sphinx/ext/githubpages.py2
-rw-r--r--sphinx/ext/graphviz.py2
-rw-r--r--sphinx/ext/ifconfig.py2
-rw-r--r--sphinx/ext/imgconverter.py2
-rw-r--r--sphinx/ext/imgmath.py6
-rw-r--r--sphinx/ext/inheritance_diagram.py28
-rw-r--r--sphinx/ext/intersphinx.py3
-rw-r--r--sphinx/ext/jsmath.py6
-rw-r--r--sphinx/ext/linkcode.py2
-rw-r--r--sphinx/ext/mathbase.py39
-rw-r--r--sphinx/ext/mathjax.py6
-rw-r--r--sphinx/ext/napoleon/__init__.py8
-rw-r--r--sphinx/ext/napoleon/docstring.py24
-rw-r--r--sphinx/ext/napoleon/iterators.py2
-rw-r--r--sphinx/ext/pngmath.py6
-rw-r--r--sphinx/ext/todo.py5
-rw-r--r--sphinx/ext/viewcode.py8
-rw-r--r--sphinx/extension.py4
-rw-r--r--sphinx/highlighting.py2
-rw-r--r--sphinx/io.py213
-rw-r--r--sphinx/jinja2glue.py2
-rw-r--r--sphinx/make_mode.py2
-rw-r--r--sphinx/parsers.py34
-rw-r--r--sphinx/pycode/__init__.py8
-rw-r--r--sphinx/pycode/parser.py2
-rw-r--r--sphinx/pygments_styles.py2
-rw-r--r--sphinx/quickstart.py2
-rw-r--r--sphinx/registry.py159
-rw-r--r--sphinx/roles.py2
-rw-r--r--sphinx/search/__init__.py2
-rw-r--r--sphinx/search/en.py2
-rw-r--r--sphinx/search/ja.py2
-rw-r--r--sphinx/search/jssplitter.py2
-rw-r--r--sphinx/search/zh.py2
-rw-r--r--sphinx/setup_command.py12
-rw-r--r--sphinx/templates/epub2/container.xml6
-rw-r--r--sphinx/templates/epub2/content.opf_t37
-rw-r--r--sphinx/templates/epub2/mimetype1
-rw-r--r--sphinx/templates/epub2/toc.ncx_t15
-rw-r--r--sphinx/templates/quickstart/Makefile_t7
-rw-r--r--sphinx/templates/quickstart/conf.py_t31
-rw-r--r--sphinx/templates/quickstart/make.bat_t9
-rw-r--r--sphinx/testing/__init__.py2
-rw-r--r--sphinx/testing/fixtures.py2
-rw-r--r--sphinx/testing/path.py2
-rw-r--r--sphinx/testing/util.py2
-rw-r--r--sphinx/texinputs/sphinx.sty236
-rw-r--r--sphinx/texinputs/sphinxhowto.cls1
-rw-r--r--sphinx/themes/agogo/layout.html2
-rw-r--r--sphinx/themes/agogo/static/agogo.css_t2
-rw-r--r--sphinx/themes/basic/defindex.html2
-rw-r--r--sphinx/themes/basic/domainindex.html2
-rw-r--r--sphinx/themes/basic/genindex-single.html2
-rw-r--r--sphinx/themes/basic/genindex-split.html2
-rw-r--r--sphinx/themes/basic/genindex.html2
-rw-r--r--sphinx/themes/basic/globaltoc.html2
-rw-r--r--sphinx/themes/basic/layout.html2
-rw-r--r--sphinx/themes/basic/localtoc.html2
-rw-r--r--sphinx/themes/basic/page.html2
-rw-r--r--sphinx/themes/basic/relations.html2
-rw-r--r--sphinx/themes/basic/search.html2
-rw-r--r--sphinx/themes/basic/searchbox.html8
-rw-r--r--sphinx/themes/basic/searchresults.html2
-rw-r--r--sphinx/themes/basic/sourcelink.html2
-rw-r--r--sphinx/themes/basic/static/basic.css_t16
-rw-r--r--sphinx/themes/basic/static/doctools.js_t2
-rw-r--r--sphinx/themes/basic/static/searchtools.js_t2
-rw-r--r--sphinx/themes/basic/static/websupport.js2
-rw-r--r--sphinx/themes/classic/layout.html2
-rw-r--r--sphinx/themes/classic/static/classic.css_t2
-rw-r--r--sphinx/themes/classic/static/sidebar.js_t2
-rw-r--r--sphinx/themes/epub/epub-cover.html2
-rw-r--r--sphinx/themes/epub/layout.html2
-rw-r--r--sphinx/themes/epub/static/epub.css_t2
-rw-r--r--sphinx/themes/haiku/layout.html2
-rw-r--r--sphinx/themes/haiku/static/haiku.css_t2
-rw-r--r--sphinx/themes/nature/static/nature.css_t9
-rw-r--r--sphinx/themes/nonav/layout.html2
-rw-r--r--sphinx/themes/nonav/static/nonav.css2
-rw-r--r--sphinx/themes/pyramid/static/epub.css2
-rw-r--r--sphinx/themes/pyramid/static/pyramid.css_t9
-rw-r--r--sphinx/themes/scrolls/layout.html2
-rw-r--r--sphinx/themes/scrolls/static/scrolls.css_t2
-rw-r--r--sphinx/themes/sphinxdoc/layout.html2
-rw-r--r--sphinx/themes/sphinxdoc/static/sphinxdoc.css_t2
-rw-r--r--sphinx/themes/traditional/static/traditional.css_t2
-rw-r--r--sphinx/theming.py2
-rw-r--r--sphinx/transforms/__init__.py26
-rw-r--r--sphinx/transforms/compact_bullet_list.py8
-rw-r--r--sphinx/transforms/i18n.py11
-rw-r--r--sphinx/transforms/post_transforms/__init__.py2
-rw-r--r--sphinx/transforms/post_transforms/images.py2
-rw-r--r--sphinx/util/__init__.py20
-rw-r--r--sphinx/util/console.py2
-rw-r--r--sphinx/util/docfields.py2
-rw-r--r--sphinx/util/docstrings.py2
-rw-r--r--sphinx/util/docutils.py52
-rw-r--r--sphinx/util/fileutil.py2
-rw-r--r--sphinx/util/i18n.py2
-rw-r--r--sphinx/util/images.py2
-rw-r--r--sphinx/util/inspect.py26
-rw-r--r--sphinx/util/inventory.py2
-rw-r--r--sphinx/util/jsdump.py2
-rw-r--r--sphinx/util/jsonimpl.py2
-rw-r--r--sphinx/util/logging.py35
-rw-r--r--sphinx/util/matching.py2
-rw-r--r--sphinx/util/nodes.py2
-rw-r--r--sphinx/util/osutil.py2
-rw-r--r--sphinx/util/parallel.py2
-rw-r--r--sphinx/util/png.py2
-rw-r--r--sphinx/util/pycompat.py2
-rw-r--r--sphinx/util/requests.py2
-rw-r--r--sphinx/util/rst.py2
-rw-r--r--sphinx/util/stemmer/__init__.py2
-rw-r--r--sphinx/util/tags.py2
-rw-r--r--sphinx/util/template.py2
-rw-r--r--sphinx/util/texescape.py2
-rw-r--r--sphinx/util/typing.py2
-rw-r--r--sphinx/util/websupport.py2
-rw-r--r--sphinx/versioning.py2
-rw-r--r--sphinx/websupport/__init__.py2
-rw-r--r--sphinx/websupport/errors.py2
-rw-r--r--sphinx/websupport/search/__init__.py2
-rw-r--r--sphinx/websupport/search/nullsearch.py2
-rw-r--r--sphinx/websupport/search/whooshsearch.py2
-rw-r--r--sphinx/websupport/search/xapiansearch.py2
-rw-r--r--sphinx/websupport/storage/__init__.py2
-rw-r--r--sphinx/websupport/storage/differ.py2
-rw-r--r--sphinx/websupport/storage/sqlalchemy_db.py2
-rw-r--r--sphinx/websupport/storage/sqlalchemystorage.py2
-rw-r--r--sphinx/writers/__init__.py2
-rw-r--r--sphinx/writers/html.py8
-rw-r--r--sphinx/writers/html5.py8
-rw-r--r--sphinx/writers/latex.py118
-rw-r--r--sphinx/writers/manpage.py2
-rw-r--r--sphinx/writers/texinfo.py2
-rw-r--r--sphinx/writers/text.py2
-rw-r--r--sphinx/writers/websupport.py2
-rw-r--r--sphinx/writers/xml.py2
-rw-r--r--tests/conftest.py35
-rw-r--r--tests/py35/test_autodoc_py35.py35
-rw-r--r--tests/roots/test-apidoc-toc/mypackage/__init__.py0
-rwxr-xr-xtests/roots/test-apidoc-toc/mypackage/main.py16
-rw-r--r--tests/roots/test-apidoc-toc/mypackage/no_init/foo.py1
-rw-r--r--tests/roots/test-apidoc-toc/mypackage/resource/__init__.py0
-rw-r--r--tests/roots/test-apidoc-toc/mypackage/resource/resource.txt1
-rw-r--r--tests/roots/test-apidoc-toc/mypackage/something/__init__.py1
-rw-r--r--tests/roots/test-builder-gettext-dont-rebuild-mo/bom.po (renamed from tests/roots/test-root/bom.po)0
-rw-r--r--tests/roots/test-builder-gettext-dont-rebuild-mo/bom.rst5
-rw-r--r--tests/roots/test-builder-gettext-dont-rebuild-mo/conf.py7
-rw-r--r--tests/roots/test-builder-gettext-dont-rebuild-mo/index.rst6
-rw-r--r--tests/roots/test-directive-code/emphasize.rst7
-rw-r--r--tests/roots/test-domain-cpp/index.rst6
-rw-r--r--tests/roots/test-ext-autodoc/target/__init__.py225
-rw-r--r--tests/roots/test-ext-math/index.rst2
-rw-r--r--tests/roots/test-ext-math/page.rst9
-rw-r--r--tests/roots/test-ext-todo/index.rst2
-rw-r--r--tests/roots/test-extensions/conf.py4
-rw-r--r--tests/roots/test-extensions/read_parallel.py4
-rw-r--r--tests/roots/test-extensions/read_serial.py4
-rw-r--r--tests/roots/test-extensions/write_parallel.py4
-rw-r--r--tests/roots/test-extensions/write_serial.py4
-rw-r--r--tests/roots/test-inheritance/basic_diagram.rst5
-rw-r--r--tests/roots/test-inheritance/conf.py6
-rw-r--r--tests/roots/test-inheritance/contents.rst4
-rw-r--r--tests/roots/test-inheritance/diagram_module_w_2_top_classes.rst6
-rw-r--r--tests/roots/test-inheritance/diagram_w_1_top_class.rst7
-rw-r--r--tests/roots/test-inheritance/diagram_w_2_top_classes.rst9
-rw-r--r--tests/roots/test-inheritance/diagram_w_parts.rst7
-rw-r--r--tests/roots/test-inheritance/dummy/__init__.py0
-rw-r--r--tests/roots/test-inheritance/dummy/test.py30
-rw-r--r--tests/roots/test-latex-numfig/conf.py12
-rw-r--r--tests/roots/test-latex-numfig/index.rst9
-rw-r--r--tests/roots/test-latex-numfig/indexhowto.rst10
-rw-r--r--tests/roots/test-latex-numfig/indexmanual.rst13
-rw-r--r--tests/roots/test-latex-table/expects/longtable_having_verbatim.tex1
-rw-r--r--tests/roots/test-latex-table/expects/table_having_verbatim.tex1
-rw-r--r--tests/roots/test-manpage_url/conf.py5
-rw-r--r--tests/roots/test-manpage_url/index.rst3
-rw-r--r--tests/roots/test-root/_static/README1
-rw-r--r--tests/roots/test-root/_static/excluded.css1
-rw-r--r--tests/roots/test-root/_static/subdir/foo.css1
-rw-r--r--tests/roots/test-root/autodoc.txt4
-rw-r--r--tests/roots/test-root/autodoc_target.py225
-rw-r--r--tests/roots/test-root/conf.py5
-rw-r--r--tests/roots/test-root/robots.txt2
-rw-r--r--tests/roots/test-root/subdir.po9
-rw-r--r--tests/roots/test-root/templated.css_t2
-rw-r--r--tests/roots/test-smartquotes/conf.py7
-rw-r--r--tests/roots/test-smartquotes/index.rst4
-rw-r--r--tests/roots/test-theming/test_theme/staticfiles/layout.html (renamed from tests/roots/test-root/testtheme/layout.html)0
-rw-r--r--tests/roots/test-theming/test_theme/staticfiles/static/staticimg.png (renamed from tests/roots/test-root/testtheme/static/staticimg.png)bin120 -> 120 bytes
-rw-r--r--tests/roots/test-theming/test_theme/staticfiles/static/statictmpl.html_t (renamed from tests/roots/test-root/testtheme/static/statictmpl.html_t)0
-rw-r--r--tests/roots/test-theming/test_theme/staticfiles/theme.conf (renamed from tests/roots/test-root/testtheme/theme.conf)0
-rw-r--r--tests/roots/test-theming/ziptheme.zip (renamed from tests/roots/test-root/ziptheme.zip)bin1039 -> 1039 bytes
-rwxr-xr-xtests/run.py68
-rw-r--r--tests/test_api_translator.py2
-rw-r--r--tests/test_application.py38
-rw-r--r--tests/test_autodoc.py519
-rw-r--r--tests/test_build.py13
-rw-r--r--tests/test_build_applehelp.py2
-rw-r--r--tests/test_build_epub.py2
-rw-r--r--tests/test_build_gettext.py2
-rw-r--r--tests/test_build_html.py77
-rw-r--r--tests/test_build_html5.py10
-rw-r--r--tests/test_build_latex.py117
-rw-r--r--tests/test_build_linkcheck.py2
-rw-r--r--tests/test_build_manpage.py2
-rw-r--r--tests/test_build_qthelp.py2
-rw-r--r--tests/test_build_texinfo.py2
-rw-r--r--tests/test_build_text.py2
-rw-r--r--tests/test_catalogs.py2
-rw-r--r--tests/test_config.py2
-rw-r--r--tests/test_correct_year.py2
-rw-r--r--tests/test_directive_code.py10
-rw-r--r--tests/test_directive_only.py2
-rw-r--r--tests/test_docutilsconf.py4
-rw-r--r--tests/test_domain_cpp.py456
-rw-r--r--tests/test_domain_js.py2
-rw-r--r--tests/test_domain_py.py2
-rw-r--r--tests/test_domain_rst.py2
-rw-r--r--tests/test_domain_std.py2
-rw-r--r--tests/test_environment.py4
-rw-r--r--tests/test_environment_indexentries.py2
-rw-r--r--tests/test_environment_toctree.py2
-rw-r--r--tests/test_ext_apidoc.py79
-rw-r--r--tests/test_ext_autodoc.py2
-rw-r--r--tests/test_ext_autosectionlabel.py2
-rw-r--r--tests/test_ext_autosummary.py19
-rw-r--r--tests/test_ext_coverage.py18
-rw-r--r--tests/test_ext_doctest.py41
-rw-r--r--tests/test_ext_githubpages.py2
-rw-r--r--tests/test_ext_graphviz.py4
-rw-r--r--tests/test_ext_ifconfig.py2
-rw-r--r--tests/test_ext_imgconverter.py2
-rw-r--r--tests/test_ext_inheritance.py133
-rw-r--r--tests/test_ext_inheritance_diagram.py2
-rw-r--r--tests/test_ext_intersphinx.py11
-rw-r--r--tests/test_ext_math.py69
-rw-r--r--tests/test_ext_napoleon.py2
-rw-r--r--tests/test_ext_napoleon_docstring.py2
-rw-r--r--tests/test_ext_napoleon_iterators.py2
-rw-r--r--tests/test_ext_todo.py14
-rw-r--r--tests/test_ext_viewcode.py2
-rw-r--r--tests/test_highlighting.py2
-rw-r--r--tests/test_intl.py7
-rw-r--r--tests/test_io.py118
-rw-r--r--tests/test_markup.py17
-rw-r--r--tests/test_metadata.py2
-rw-r--r--tests/test_pycode.py3
-rw-r--r--tests/test_quickstart.py67
-rw-r--r--tests/test_search.py2
-rw-r--r--tests/test_setup_command.py2
-rw-r--r--tests/test_smartquotes.py92
-rw-r--r--tests/test_templating.py13
-rw-r--r--tests/test_theming.py25
-rw-r--r--tests/test_toctree.py2
-rw-r--r--tests/test_util.py28
-rw-r--r--tests/test_util_fileutil.py2
-rw-r--r--tests/test_util_i18n.py2
-rw-r--r--tests/test_util_images.py18
-rw-r--r--tests/test_util_inspect.py15
-rw-r--r--tests/test_util_logging.py8
-rw-r--r--tests/test_util_matching.py2
-rw-r--r--tests/test_util_nodes.py2
-rw-r--r--tests/test_util_rst.py2
-rw-r--r--tests/test_versioning.py4
-rw-r--r--tests/test_websupport.py2
-rw-r--r--tests/test_writer_latex.py2
-rw-r--r--tox.ini78
-rw-r--r--utils/checks.py2
-rw-r--r--utils/jssplitter_generator.py2
370 files changed, 4810 insertions, 2625 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index a3f83394f..600a51419 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -6,19 +6,12 @@ environment:
matrix:
- PYTHON: 27
- DOCUTILS: 0.13.1
- TEST_IGNORE: --ignore py35
- - PYTHON: 27
- DOCUTILS: 0.14
TEST_IGNORE: --ignore py35
- PYTHON: 36
- DOCUTILS: 0.14
- PYTHON: 36-x64
- DOCUTILS: 0.14
install:
- C:\Python%PYTHON%\python.exe -m pip install -U pip setuptools
- - C:\Python%PYTHON%\python.exe -m pip install docutils==%DOCUTILS% mock
- C:\Python%PYTHON%\python.exe -m pip install .[test,websupport]
# No automatic build, just run python tests
@@ -39,7 +32,7 @@ test_script:
if (-not $test_ignore) { $test_ignore = '' }
$tests = $env:TEST
if (-not $tests) { $tests = '' }
- & "C:\Python$($env:PYTHON)\python.exe" run.py $test_ignore.Split(' ') --junitxml .junit.xml $tests.Split(' ')
+ & "C:\Python$($env:PYTHON)\python.exe" -m pytest $test_ignore.Split(' ') --junitxml .junit.xml $tests.Split(' ')
Pop-Location
if ($LastExitCode -eq 1) { Write-Host "Test Failures Occurred, leaving for test result parsing" }
elseif ($LastExitCode -ne 0) { Write-Host "Other Error Occurred, aborting"; exit $LastExitCode }
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 1bbcb4884..f4d4415f1 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -6,4 +6,6 @@ jobs:
working_directory: /sphinx
steps:
- checkout
+ - run: /python3.4/bin/pip install -U pip setuptools
+ - run: /python3.4/bin/pip install -U .[test,websupport]
- run: make test PYTHON=/python3.4/bin/python
diff --git a/.gitignore b/.gitignore
index f1dc3167c..5d1026c5e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@ TAGS
.tox
.venv
.coverage
+htmlcov
.DS_Store
sphinx/pycode/Grammar*pickle
distribute-*
diff --git a/.travis.yml b/.travis.yml
index 1d065b178..e51523c19 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,46 +2,48 @@ 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
+ - PYTEST_ADDOPTS = --cov sphinx --cov-append --cov-config setup.cfg
+ - python: '3.4'
+ env: TOXENV=py34
+ - python: '3.5'
+ env: TOXENV=py35
+ - python: '3.6'
+ env:
+ - TOXENV=py36
+ - PYTEST_ADDOPTS = --cov sphinx --cov-append --cov-config setup.cfg
+ - python: 'nightly'
+ env: TOXENV=py37
+ - python: '3.6'
+ env: TOXENV=docs
+ - 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]
- - pip install flake8
- - if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then python3.6 -m pip install mypy typed-ast; fi
+ - pip install -U tox codecov
+
script:
- - flake8
- - if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then make type-check test-async; fi
- - if [[ $TRAVIS_PYTHON_VERSION != '3.6' ]]; then make test; fi
+ - tox -- -v
+
+after_success:
+ - codecov
diff --git a/AUTHORS b/AUTHORS
index f4ce16164..0f3ae77d5 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -18,6 +18,7 @@ Other co-maintainers:
Other contributors, listed alphabetically, are:
* Alastair Houghton -- Apple Help builder
+* Alexander Todorov -- inheritance_diagram tests and improvements
* Andi Albrecht -- agogo theme
* Jakob Lykke Andersen -- Rewritten C++ domain
* Henrique Bastos -- SVG support for graphviz extension
diff --git a/CHANGES b/CHANGES
index 39b9c5903..39733a6d8 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,11 @@
Release 1.7 (in development)
============================
+Dependencies
+------------
+
+* Add ``packaging`` package
+
Incompatible changes
--------------------
@@ -12,12 +17,24 @@ Incompatible changes
given directory.
* #3929: apidoc: Move sphinx.apidoc to sphinx.ext.apidoc
* #4226: apidoc: Generate new style makefile (make-mode)
+* #4274: sphinx-build returns 2 as an exit code on argument error
+* #4389: output directory will be created after loading extensions
+* autodoc does not generate warnings messages to the generated document even if
+ :confval:`keep_warnings` is True. They are only emitted to stderr.
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.
+* ``sphinx.ext.autodoc.AutodocReporter`` is replaced by ``sphinx.util.docutils.
+ switch_source_input()`` and now deprecated. It will be removed in Sphinx-2.0.
+* ``sphinx.ext.autodoc.add_documenter()`` and ``AutoDirective._register`` is now
+ deprecated. Please use ``app.add_autodocumenter()`` instead.
+* ``AutoDirective._special_attrgetters`` is now deprecated. Please use
+ ``app.add_autodoc_attrgetter()`` instead.
Features added
--------------
@@ -25,7 +42,8 @@ Features added
* C++, handle ``decltype(auto)``.
* #2406: C++, add proper parsing of expressions, including linking of identifiers.
* C++, add a ``cpp:expr`` role for inserting inline C++ expressions or types.
-* #4094: C++, allow empty template argument lists.
+* C++, support explicit member instantiations with shorthand ``template`` prefix.
+* C++, make function parameters linkable, like template params.
* #3638: Allow to change a label of reference to equation using
``math_eqref_format``
@@ -45,7 +63,18 @@ Features added
* HTML themes can set up default sidebars through ``theme.conf``
* #3160: html: Use ``<kdb>`` to represent ``:kbd:`` role
* #4212: autosummary: catch all exceptions when importing modules
-
+* #4166: Add :confval:`math_numfig` for equation numbering by section (refs:
+ #3991, #4080). Thanks to Oliver Jahn.
+* #4311: Let LaTeX obey :confval:`numfig_secnum_depth` for figures, tables, and
+ code-blocks
+* #947: autodoc now supports ignore-module-all to ignore a module's ``__all__``
+* #4332: Let LaTeX obey :confval:`math_numfig` for equation numbering
+* #4093: sphinx-build creates empty directories for unknown targets/builders
+* Add ``top-classes`` option for the ``sphinx.ext.inheritance_diagram``
+ extension to limit the scope of inheritance graphs.
+* #4183: doctest: ``:pyversion:`` option also follows PEP-440 specification
+* #4235: html: Add :confval:`manpages_url` to make manpage roles to hyperlinks
+* #3570: autodoc: Do not display 'typing.' module for type hints
Features removed
----------------
@@ -83,13 +112,26 @@ Bugs fixed
* #3882: Update the order of files for HTMLHelp and QTHelp
* #3962: sphinx-apidoc does not recognize implicit namespace packages correctly
+* #4094: C++, allow empty template argument lists.
+* C++, also hyperlink types in the name of declarations with qualified names.
+* C++, do not add index entries for declarations inside concepts.
+* C++, support the template disambiguator for dependent names.
+* #4314: For PDF 'howto' documents, numbering of code-blocks differs from the
+ one of figures and tables
+* #4330: PDF 'howto' documents have an incoherent default LaTeX tocdepth counter
+ setting
+* #4198: autosummary emits multiple 'autodoc-process-docstring' event. Thanks
+ to Joel Nothman.
+* #4081: Warnings and errors colored the same when building
+* latex: Do not display 'Release' label if :confval:`release` is not set
Testing
--------
* Add support for docutils 0.14
+* Add tests for the ``sphinx.ext.inheritance_diagram`` extension.
-Release 1.6.6 (in development)
+Release 1.6.7 (in development)
==============================
Dependencies
@@ -104,17 +146,50 @@ Deprecated
Features added
--------------
+Bugs fixed
+----------
+
+Testing
+--------
+
+Release 1.6.6 (released Jan 08, 2018)
+=====================================
+
+Features added
+--------------
+
* #4181: autodoc: Sort dictionary keys when possible
+* ``VerbatimHighlightColor`` is a new
+ :ref:`LaTeX 'sphinxsetup' <latexsphinxsetup>` key (refs: #4285)
+* Easier customizability of LaTeX macros involved in rendering of code-blocks
+* Show traceback if conf.py raises an exception (refs: #4369)
+* Add :confval:`smartquotes` to disable smart quotes through ``conf.py``
+ (refs: #3967)
+* Add :confval:`smartquotes_action` and :confval:`smartquotes_excludes`
+ (refs: #4142, #4357)
Bugs fixed
----------
+* #4334: sphinx-apidoc: Don't generate references to non-existing files in TOC
* #4206: latex: reST label between paragraphs loses paragraph break
* #4231: html: Apply fixFirefoxAnchorBug only under Firefox
+* #4221: napoleon depends on autodoc, but users need to load it manually
* #2298: automodule fails to document a class attribute
-
-Testing
---------
+* #4099: C++: properly link class reference to class from inside constructor
+* #4267: PDF build broken by Unicode U+2116 NUMERO SIGN character
+* #4249: PDF output: Pygments error highlighting increases line spacing in
+ code blocks
+* #1238: Support ``:emphasize-lines:`` in PDF output
+* #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
+* #4315: For PDF 'howto' documents, ``latex_toplevel_sectioning='part'`` generates
+ ``\chapter`` commands
+* #4214: Two todolist directives break sphinx-1.6.5
+* Fix links to external option docs with intersphinx (refs: #3769)
+* #4091: Private members not documented without :undoc-members:
Release 1.6.5 (released Oct 23, 2017)
=====================================
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 7c8a90c6b..03d26c001 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.
+
+ Testing is best done through ``tox``, which provides a number of targets and
+ allows testing against multiple different Python environments:
+
+ * To list all possible targets::
+
+ tox -av
- * Run the unit tests::
+ * To run unit tests for a specific Python version, such as 3.6::
- pip install .[test,websupport]
- make test
+ tox -e py36
- * Again, it's useful to turn on deprecation warnings on so they're shown in
- the test output::
+ * 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 make test
+ PYTHONWARNINGS=all tox -e py36
- * Arguments to pytest can be passed via tox, e.g. in order to run a
+ * 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.
@@ -303,14 +317,17 @@ There are a couple reasons that code in Sphinx might be deprecated:
no longer needs to support the older version of Python that doesn't include
the library, the library will be deprecated in Sphinx.
-As the :ref:`deprecation-policy` describes,
-the first release of Sphinx that deprecates a feature (``A.B``) should raise a
-``RemovedInSphinxXXWarning`` (where XX is the Sphinx version where the feature
-will be removed) when the deprecated feature is invoked. Assuming we have good
-test coverage, these warnings are converted to errors when running the test
-suite with warnings enabled: ``python -Wall tests/run.py``. Thus, when adding
-a ``RemovedInSphinxXXWarning`` you need to eliminate or silence any warnings
-generated when running the tests.
+As the :ref:`deprecation-policy` describes, the first release of Sphinx that
+deprecates a feature (``A.B``) should raise a ``RemovedInSphinxXXWarning``
+(where ``XX`` is the Sphinx version where the feature will be removed) when the
+deprecated feature is invoked. Assuming we have good test coverage, these
+warnings are converted to errors when running the test suite with warnings
+enabled::
+
+ pytest -Wall
+
+Thus, when adding a ``RemovedInSphinxXXWarning`` you need to eliminate or
+silence any warnings generated when running the tests.
.. _deprecation-policy:
diff --git a/EXAMPLES b/EXAMPLES
index 7c88805eb..edbf48903 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)
@@ -88,7 +93,7 @@ Documentation using the classic theme
* simuPOP: http://simupop.sourceforge.net/manual_release/build/userGuide.html (customized)
* Sprox: http://sprox.org/ (customized)
* SymPy: http://docs.sympy.org/
-* TurboGears: https://turbogears.readthedocs.org/ (customized)
+* TurboGears: https://turbogears.readthedocs.io/ (customized)
* tvtk: http://docs.enthought.com/mayavi/tvtk/
* Varnish: https://www.varnish-cache.org/docs/ (customized, alabaster for index)
* Waf: https://waf.io/apidocs/
@@ -121,18 +126,25 @@ 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
-----------------------------------------
+* Arcade: http://arcade.academy/ (sphinx_rtd_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 +162,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 +192,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 +242,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
------------------------------------------
@@ -238,19 +259,21 @@ Documentation using sphinx_bootstrap_theme
* Bootstrap Theme: https://ryan-roemer.github.io/sphinx-bootstrap-theme/
* C/C++ Software Development with Eclipse: http://eclipsebook.in/
* Dataverse: http://guides.dataverse.org/
-* e-cidadania: http://e-cidadania.readthedocs.org/
+* e-cidadania: https://e-cidadania.readthedocs.io/
* Hangfire: http://docs.hangfire.io/
* Hedge: https://documen.tician.de/hedge/
* ObsPy: https://docs.obspy.org/
* 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 +286,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 +331,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 +345,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:
diff --git a/LICENSE b/LICENSE
index a2783ab3f..19f1090fd 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,7 @@
License for Sphinx
==================
-Copyright (c) 2007-2017 by the Sphinx team (see AUTHORS file).
+Copyright (c) 2007-2018 by the Sphinx team (see AUTHORS file).
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/Makefile b/Makefile
index 5b3d5aad4..67699363f 100644
--- a/Makefile
+++ b/Makefile
@@ -69,15 +69,15 @@ reindent:
.PHONY: test
test:
- @cd tests; $(PYTHON) run.py --ignore py35 -v $(TEST)
+ @$(PYTHON) -m pytest -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:
- @cd tests; $(PYTHON) run.py -v --cov=sphinx --junitxml=.junit.xml $(TEST)
+ @$(PYTHON) -m pytest -v --cov=sphinx --junitxml=.junit.xml $(TEST)
.PHONY: build
build:
diff --git a/README.rst b/README.rst
index 1e027ec8e..2d841f78e 100644
--- a/README.rst
+++ b/README.rst
@@ -1,45 +1,106 @@
+========
+ Sphinx
+========
+
.. 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)
+
+.. image:: https://codecov.io/gh/sphinx-doc/sphinx/branch/master/graph/badge.svg
+ :target: https://codecov.io/gh/sphinx-doc/sphinx
+ :alt: Code Coverage Status (Codecov)
+
+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
+
+For more information, refer to the `the documentation`__.
+
+.. __: http://www.sphinx-doc.org/
+
+Installation
+============
-=================
-README for Sphinx
-=================
-
-This is the Sphinx documentation generator, see http://www.sphinx-doc.org/.
+Sphinx is published on `PyPI`__ and can be installed from there::
+ pip install -U sphinx
-Installing
-==========
+We also publish beta releases::
-Install from PyPI to use stable version::
+ pip install -U --pre sphinx
- pip install -U sphinx
+If you wish to install `Sphinx` for development purposes, refer to `the
+contributors guide`__.
-Install from PyPI to use beta version::
+__ https://pypi.python.org/pypi/Sphinx
+__ CONTRIBUTING.rst
- pip install -U --pre sphinx
+Documentation
+=============
-Install from newest dev version in stable branch::
+Documentation is available from `sphinx-doc.org`__.
- pip install git+https://github.com/sphinx-doc/sphinx@stable
+__ http://www.sphinx-doc.org/
-Install from newest dev version in master branch::
+Testing
+=======
- pip install git+https://github.com/sphinx-doc/sphinx
+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).
-Install from cloned source::
+For information on running tests locally, refer to `the contributors guide`__.
- pip install .
+__ https://travis-ci.org/sphinx-doc/sphinx
+__ https://ci.appveyor.com/project/sphinxdoc/sphinx
+__ https://circleci.com/gh/sphinx-doc/sphinx
+__ CONTRIBUTING.rst
-Install from cloned source as editable::
+Contributing
+============
- pip install -e .
+Refer to `the contributors guide`__.
+__ CONTRIBUTING.rst
Release signatures
==================
@@ -48,37 +109,3 @@ Releases are signed with following keys:
* `498D6B9E <https://pgp.mit.edu/pks/lookup?op=vindex&search=0x102C2C17498D6B9E>`_
* `5EBA0E07 <https://pgp.mit.edu/pks/lookup?op=vindex&search=0x1425F8CE5EBA0E07>`_
-
-Reading the docs
-================
-
-You can read them online at <http://www.sphinx-doc.org/>.
-
-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/doc/_templates/index.html b/doc/_templates/index.html
index b4bdb5985..5a8a2f025 100644
--- a/doc/_templates/index.html
+++ b/doc/_templates/index.html
@@ -74,9 +74,9 @@
<p>{%trans%}
You can also download PDF/EPUB versions of the Sphinx documentation:
- a <a href="http://readthedocs.org/projects/sphinx/downloads/pdf/stable/">PDF version</a> generated from
+ a <a href="https://media.readthedocs.org/pdf/sphinx/stable/sphinx.pdf">PDF version</a> generated from
the LaTeX Sphinx produces, and
- a <a href="http://readthedocs.org/projects/sphinx/downloads/epub/stable/">EPUB version</a>.
+ a <a href="https://media.readthedocs.org/epub/sphinx/stable/sphinx.epub">EPUB version</a>.
{%endtrans%}
</p>
@@ -106,7 +106,7 @@
<h2>{%trans%}Hosting{%endtrans%}</h2>
<p>{%trans%}Need a place to host your Sphinx docs?
- <a href="http://readthedocs.org">readthedocs.org</a> hosts a lot of Sphinx docs
+ <a href="https://readthedocs.org/">readthedocs.org</a> hosts a lot of Sphinx docs
already, and integrates well with projects' source control. It also features a
powerful built-in search that exceeds the possibilities of Sphinx' JavaScript-based
offline search.{%endtrans%}</p>
diff --git a/doc/_templates/indexsidebar.html b/doc/_templates/indexsidebar.html
index 6359921a5..b07ef2033 100644
--- a/doc/_templates/indexsidebar.html
+++ b/doc/_templates/indexsidebar.html
@@ -20,12 +20,14 @@ Index</a>, or install it with:{%endtrans%}</p>
<h3>{%trans%}Questions? Suggestions?{%endtrans%}</h3>
<p>{%trans%}Join the <a href="http://groups.google.com/group/sphinx-users">sphinx-users</a> mailing list on Google Groups:{%endtrans%}</p>
+<div class="subscribeformwrapper">
<form action="http://groups.google.com/group/sphinx-users/boxsubscribe"
- style="padding-left: 0.5em">
- <input type="text" name="email" value="your@email" style="font-size: 90%; width: 120px"
- onfocus="$(this).val('');"/>
- <input type="submit" name="sub" value="Subscribe" style="font-size: 90%; width: 70px"/>
+ class="subscribeform">
+ <input type="text" name="email" value="your@email"
+ onfocus="$(this).val('');" />
+ <input type="submit" name="sub" value="Subscribe" />
</form>
+</div>
<p>{%trans%}or come to the <tt>#sphinx-doc</tt> channel on FreeNode.{%endtrans%}</p>
<p>{%trans%}You can also open an issue at the
<a href="https://github.com/sphinx-doc/sphinx/issues">tracker</a>.{%endtrans%}</p>
diff --git a/doc/_themes/sphinx13/layout.html b/doc/_themes/sphinx13/layout.html
index 911d1287c..ce6f08daa 100644
--- a/doc/_themes/sphinx13/layout.html
+++ b/doc/_themes/sphinx13/layout.html
@@ -4,7 +4,7 @@
Sphinx layout template for the sphinxdoc theme.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- extends "basic/layout.html" %}
diff --git a/doc/_themes/sphinx13/static/sphinx13.css b/doc/_themes/sphinx13/static/sphinx13.css
index d15fbaea4..24a33fba7 100644
--- a/doc/_themes/sphinx13/static/sphinx13.css
+++ b/doc/_themes/sphinx13/static/sphinx13.css
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- sphinx13 theme.
*
- * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -140,11 +140,37 @@ div.sphinxsidebar .logo img {
vertical-align: middle;
}
+div.subscribeformwrapper {
+ display: block;
+ overflow: auto;
+ margin-bottom: 1.2em;
+}
+
div.sphinxsidebar input {
border: 1px solid #aaa;
font-family: 'Open Sans', 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
'Verdana', sans-serif;
- font-size: 1em;
+}
+
+div.sphinxsidebar .subscribeform {
+ margin-top: 0;
+}
+
+div.sphinxsidebar .subscribeform input {
+ border: 1px solid #aaa;
+ font-size: 0.9em;
+ float: left;
+ padding: 0.25em 0.5em;
+ box-sizing: border-box;
+}
+
+div.sphinxsidebar .subscribeform input[type="text"] {
+ width: 60%;
+}
+
+div.sphinxsidebar .subscribeform input[type="submit"] {
+ width: 40%;
+ border-left: none;
}
div.sphinxsidebar h3 {
@@ -281,7 +307,7 @@ tt {
border: 1px solid #ddd;
border-radius: 2px;
color: #333;
- padding: 1px;
+ padding: 1px 0.2em;
}
tt.descname, tt.descclassname, tt.xref {
diff --git a/doc/conf.py b/doc/conf.py
index db2846186..fa82cbfb7 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -15,7 +15,7 @@ templates_path = ['_templates']
exclude_patterns = ['_build']
project = 'Sphinx'
-copyright = '2007-2017, Georg Brandl and the Sphinx team'
+copyright = '2007-2018, Georg Brandl and the Sphinx team'
version = sphinx.__display_version__
release = version
show_authors = True
diff --git a/doc/config.rst b/doc/config.rst
index 415a2298a..587d5785b 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`.
@@ -295,6 +293,24 @@ General configuration
.. versionadded:: 1.3
+.. confval:: manpages_url
+
+ A URL to cross-reference :rst:role:`manpage` directives. If this is
+ defined to ``https://manpages.debian.org/{path}``, the
+ :literal:`:manpage:`man(1)`` role will like to
+ <https://manpages.debian.org/man(1)>. The patterns available are:
+
+ * ``page`` - the manual page (``man``)
+ * ``section`` - the manual section (``1``)
+ * ``path`` - the original manual page and section specified (``man(1)``)
+
+ This also supports manpages specified as ``man.1``.
+
+ .. note:: This currently affects only HTML writers but could be
+ expanded in the future.
+
+ .. versionadded:: 1.7
+
.. confval:: nitpicky
If true, Sphinx will warn about *all* references where the target cannot be
@@ -315,8 +331,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::
@@ -339,13 +355,80 @@ 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``.
+ - 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...
.. versionadded:: 1.3
+ .. versionchanged:: 1.7
+ The LaTeX builder obeys this setting (if :confval:`numfig` is set to
+ ``True``).
+
+.. confval:: smartquotes
+
+ If true, the `Docutils Smart Quotes transform`__, originally based on
+ `SmartyPants`__ (limited to English) and currently applying to many
+ languages, will be used to convert quotes and dashes to typographically
+ correct entities. Default: ``True``.
+
+ __ http://docutils.sourceforge.net/docs/user/smartquotes.html
+ __ https://daringfireball.net/projects/smartypants/
+
+ .. versionadded:: 1.6.6
+ It replaces deprecated :confval:`html_use_smartypants`.
+ It applies by default to all builders except ``man`` and ``text``
+ (see :confval:`smartquotes_excludes`.)
+
+ A `docutils.conf`__ file located in the configuration directory (or a
+ global :file:`~/.docutils` file) is obeyed unconditionally if it
+ *deactivates* smart quotes via the corresponding `Docutils option`__. But
+ if it *activates* them, then :confval:`smartquotes` does prevail.
+
+ __ http://docutils.sourceforge.net/docs/user/config.html
+ __ http://docutils.sourceforge.net/docs/user/config.html#smart-quotes
+
+.. confval:: smartquotes_action
+
+ This string, for use with Docutils ``0.14`` or later, customizes the Smart
+ Quotes transform. See the file :file:`smartquotes.py` at the `Docutils
+ repository`__ for details. The default ``'qDe'`` educates normal **q**\
+ uote characters ``"``, ``'``, em- and en-**D**\ ashes ``---``, ``--``, and
+ **e**\ llipses ``...``.
+
+ .. versionadded:: 1.6.6
+
+ __ https://sourceforge.net/p/docutils/code/HEAD/tree/trunk/docutils/
+
+.. confval:: smartquotes_excludes
+
+ This is a ``dict`` whose default is::
+
+ {'languages': ['ja'], 'builders': ['man', 'text']}
+
+ Each entry gives a sufficient condition to ignore the
+ :confval:`smartquotes` setting and deactivate the Smart Quotes transform.
+ Accepted keys are as above ``'builders'`` or ``'languages'``.
+ The values are lists.
+
+ .. note:: Currently, in case of invocation of :program:`make` with multiple
+ targets, the first target name is the only one which is tested against
+ the ``'builders'`` entry and it decides for all. Also, a ``make text``
+ following ``make html`` needs to be issued in the form ``make text
+ O="-E"`` to force re-parsing of source files, as the cached ones are
+ already transformed. On the other hand the issue does not arise with
+ direct usage of :program:`sphinx-build` as it caches
+ (in its default usage) the parsed source files in per builder locations.
+
+ .. versionadded:: 1.6.6
+
.. confval:: tls_verify
If true, Sphinx verifies server certifications. Default is ``True``.
@@ -777,15 +860,11 @@ that use Sphinx's HTMLWriter class.
.. confval:: html_use_smartypants
- If true, `SmartyPants <https://daringfireball.net/projects/smartypants/>`_
- will be used to convert quotes and dashes to typographically correct
+ If true, quotes and dashes are converted to typographically correct
entities. Default: ``True``.
.. deprecated:: 1.6
- To disable or customize smart quotes, use the Docutils configuration file
- (``docutils.conf``) instead to set there its `smart_quotes option`_.
-
- .. _`smart_quotes option`: http://docutils.sourceforge.net/docs/user/config.html#smart-quotes
+ To disable smart quotes, use rather :confval:`smartquotes`.
.. confval:: html_add_permalinks
@@ -1450,10 +1529,6 @@ the `Dublin Core metadata <http://dublincore.org/>`_.
a chapter, but can be confusing because it mixes entries of different
depth in one list. The default value is ``True``.
- .. note::
-
- ``epub3`` builder ignores ``epub_tocdup`` option(always ``False``)
-
.. confval:: epub_tocscope
This setting control the scope of the epub table of contents. The setting
@@ -1615,10 +1690,15 @@ These options influence LaTeX output. See further :doc:`latex`.
.. confval:: latex_toplevel_sectioning
This value determines the topmost sectioning unit. It should be chosen from
- ``part``, ``chapter`` or ``section``. The default is ``None``; the topmost
- sectioning unit is switched by documentclass. ``section`` is used if
+ ``'part'``, ``'chapter'`` or ``'section'``. The default is ``None``;
+ the topmost
+ sectioning unit is switched by documentclass: ``section`` is used if
documentclass will be ``howto``, otherwise ``chapter`` will be used.
+ Note that if LaTeX uses ``\part`` command, then the numbering of sectioning
+ units one level deep gets off-sync with HTML numbering, because LaTeX
+ numbers continuously ``\chapter`` (or ``\section`` for ``howto``.)
+
.. versionadded:: 1.4
.. confval:: latex_appendices
diff --git a/doc/develop.rst b/doc/develop.rst
index 4fc7792f7..19ca81ef9 100644
--- a/doc/develop.rst
+++ b/doc/develop.rst
@@ -138,7 +138,7 @@ own extensions.
.. _cmakedomain: https://bitbucket.org/klorenz/sphinxcontrib-cmakedomain
.. _GNU Make: http://www.gnu.org/software/make/
.. _makedomain: https://bitbucket.org/klorenz/sphinxcontrib-makedomain
-.. _inlinesyntaxhighlight: http://sphinxcontrib-inlinesyntaxhighlight.readthedocs.org
+.. _inlinesyntaxhighlight: https://sphinxcontrib-inlinesyntaxhighlight.readthedocs.io/
.. _CMake: https://cmake.org
.. _domaintools: https://bitbucket.org/klorenz/sphinxcontrib-domaintools
.. _restbuilder: https://pypi.python.org/pypi/sphinxcontrib-restbuilder
diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst
index bfd55c81a..09098f39c 100644
--- a/doc/ext/autodoc.rst
+++ b/doc/ext/autodoc.rst
@@ -103,8 +103,10 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
will document all non-private member functions and properties (that is,
those whose name doesn't start with ``_``).
- For modules, ``__all__`` will be respected when looking for members; the
- order of the members will also be the order in ``__all__``.
+ For modules, ``__all__`` will be respected when looking for members unless
+ you give the ``ignore-module-all`` flag option. Without
+ ``ignore-module-all``, the order of the members will also be the order in
+ ``__all__``.
You can also give an explicit list of members; only these will then be
documented::
@@ -339,7 +341,7 @@ There are also new config values that you can set:
This value is a list of autodoc directive flags that should be automatically
applied to all autodoc directives. The supported flags are ``'members'``,
``'undoc-members'``, ``'private-members'``, ``'special-members'``,
- ``'inherited-members'`` and ``'show-inheritance'``.
+ ``'inherited-members'``, ``'show-inheritance'`` and ``'ignore-module-all'``.
If you set one of these flags in this config value, you can use a negated
form, :samp:`'no-{flag}'`, in an autodoc directive, to disable it once.
diff --git a/doc/ext/doctest.rst b/doc/ext/doctest.rst
index d1cb3c31d..62221bf04 100644
--- a/doc/ext/doctest.rst
+++ b/doc/ext/doctest.rst
@@ -80,12 +80,24 @@ a comma-separated list of group names.
.. doctest::
:pyversion: > 3.3
- The supported operands are ``<``, ``<=``, ``==``, ``>=``, ``>``, and
- comparison is performed by `distutils.version.LooseVersion
- <https://www.python.org/dev/peps/pep-0386/#distutils>`__.
+ The following operands are supported:
+
+ * ``~=``: Compatible release clause
+ * ``==``: Version matching clause
+ * ``!=``: Version exclusion clause
+ * ``<=``, ``>=``: Inclusive ordered comparison clause
+ * ``<``, ``>``: Exclusive ordered comparison clause
+ * ``===``: Arbitrary equality clause.
+
+ ``pyversion`` option is followed `PEP-440: Version Specifiers
+ <https://www.python.org/dev/peps/pep-0440/#version-specifiers>`__.
.. versionadded:: 1.6
+ .. versionchanged:: 1.7
+
+ Supported PEP-440 operands and notations
+
Note that like with standard doctests, you have to use ``<BLANKLINE>`` to
signal a blank line in the expected output. The ``<BLANKLINE>`` is removed
when building presentation output (HTML, LaTeX etc.).
diff --git a/doc/ext/inheritance.rst b/doc/ext/inheritance.rst
index 231b5fdaa..0062a8afa 100644
--- a/doc/ext/inheritance.rst
+++ b/doc/ext/inheritance.rst
@@ -42,6 +42,54 @@ It adds this directive:
.. versionchanged:: 1.5
Added ``caption`` option
+ It also supports a ``top-classes`` option which requires one or more class
+ names separated by comma. If specified inheritance traversal will stop at the
+ specified class names. Given the following Python module::
+
+ """
+ A
+ / \
+ B C
+ / \ / \
+ E D F
+ """
+
+ class A(object):
+ pass
+
+ class B(A):
+ pass
+
+ class C(A):
+ pass
+
+ class D(B, C):
+ pass
+
+ class E(B):
+ pass
+
+ class F(C):
+ pass
+
+ If you have specified a module in the inheritance diagram like this::
+
+ .. inheritance-diagram:: dummy.test
+ :top-classes: dummy.test.B, dummy.test.C
+
+ any base classes which are ancestors to ``top-classes`` and are also defined
+ in the same module will be rendered as stand alone nodes. In this example
+ class A will be rendered as stand alone node in the graph. This is a known
+ issue due to how this extension works internally.
+
+ If you don't want class A (or any other ancestors) to be visible then specify
+ only the classes you would like to generate the diagram for like this::
+
+ .. inheritance-diagram:: dummy.test.D dummy.test.E dummy.test.F
+ :top-classes: dummy.test.B, dummy.test.C
+
+ .. versionchanged:: 1.7
+ Added ``top-classes`` option to limit the scope of inheritance graphs.
New config values are:
diff --git a/doc/ext/math.rst b/doc/ext/math.rst
index 54d77ed6c..4097bb29e 100644
--- a/doc/ext/math.rst
+++ b/doc/ext/math.rst
@@ -44,6 +44,15 @@ or use Python raw strings (``r"raw"``).
Example: ``'Eq.{number}'`` is rendered as ``Eq.10``
+.. confval:: math_numfig
+
+ If ``True``, displayed math equations are numbered across pages when
+ :confval:`numfig` is enabled. The :confval:`numfig_secnum_depth` setting
+ is respected. The :rst:role:`eq`, not :rst:role:`numref`, role
+ must be used to reference equation numbers. Default is ``True``.
+
+ .. versionadded:: 1.7
+
:mod:`.mathbase` defines these new markup elements:
.. rst:role:: math
@@ -85,7 +94,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
@@ -102,8 +111,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
diff --git a/doc/ext/napoleon.rst b/doc/ext/napoleon.rst
index ea3e4042f..f7e9081f7 100644
--- a/doc/ext/napoleon.rst
+++ b/doc/ext/napoleon.rst
@@ -68,8 +68,8 @@ Getting Started
# conf.py
- # Add autodoc and napoleon to the extensions list
- extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon']
+ # Add napoleon to the extensions list
+ extensions = ['sphinx.ext.napoleon']
2. Use `sphinx-apidoc` to build your API documentation::
@@ -246,13 +246,12 @@ Configuration
Listed below are all the settings used by napoleon and their default
values. These settings can be changed in the Sphinx `conf.py` file. Make
-sure that both "sphinx.ext.autodoc" and "sphinx.ext.napoleon" are
-enabled in `conf.py`::
+sure that "sphinx.ext.napoleon" is enabled in `conf.py`::
# conf.py
# Add any Sphinx extension module names here, as strings
- extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon']
+ extensions = ['sphinx.ext.napoleon']
# Napoleon settings
napoleon_google_docstring = True
diff --git a/doc/ext/thirdparty.rst b/doc/ext/thirdparty.rst
index 6304e4af3..40c24246a 100644
--- a/doc/ext/thirdparty.rst
+++ b/doc/ext/thirdparty.rst
@@ -6,7 +6,7 @@ repository. It is open for anyone who wants to maintain an extension
publicly; just send a short message asking for write permissions.
There are also several extensions hosted elsewhere. The `Sphinx extension
-survey <http://sphinxext-survey.readthedocs.org/en/latest/>`__ contains a
+survey <https://sphinxext-survey.readthedocs.io/>`__ contains a
comprehensive list.
If you write an extension that you think others will find useful or you think
diff --git a/doc/extdev/markupapi.rst b/doc/extdev/markupapi.rst
index df23f164d..ffa08cae7 100644
--- a/doc/extdev/markupapi.rst
+++ b/doc/extdev/markupapi.rst
@@ -117,12 +117,30 @@ Both APIs parse the content into a given node. They are used like this::
node = docutils.nodes.paragraph()
# either
- from sphinx.ext.autodoc import AutodocReporter
- self.state.memo.reporter = AutodocReporter(self.result, self.state.memo.reporter) # override reporter to avoid errors from "include" directive
nested_parse_with_titles(self.state, self.result, node)
# or
self.state.nested_parse(self.result, 0, node)
+.. note::
+
+ ``sphinx.util.docutils.switch_source_input()`` allows to change a target file
+ during nested_parse. It is useful to mixed contents. For example, ``sphinx.
+ ext.autodoc`` uses it to parse docstrings::
+
+ from sphinx.util.docutils import switch_source_input
+
+ # Switch source_input between parsing content.
+ # Inside this context, all parsing errors and warnings are reported as
+ # happened in new source_input (in this case, ``self.result``).
+ with switch_source_input(self.state, self.result):
+ node = docutils.nodes.paragraph()
+ self.state.nested_parse(self.result, 0, node)
+
+ .. deprecated:: 1.7
+
+ Until Sphinx-1.6, ``sphinx.ext.autodoc.AutodocReporter`` is used for this purpose.
+ For now, it is replaced by ``switch_source_input()``.
+
If you don't need the wrapping node, you can use any concrete node type and
return ``node.children`` from the Directive.
diff --git a/doc/faq.rst b/doc/faq.rst
index 1ae9a7792..fe3173749 100644
--- a/doc/faq.rst
+++ b/doc/faq.rst
@@ -58,7 +58,7 @@ Read the Docs
Sphinx. They will host sphinx documentation, along with supporting a number
of other features including version support, PDF generation, and more. The
`Getting Started
- <http://read-the-docs.readthedocs.org/en/latest/getting_started.html>`_
+ <https://read-the-docs.readthedocs.io/en/latest/getting_started.html>`_
guide is a good place to start.
Epydoc
diff --git a/doc/intro.rst b/doc/intro.rst
index d3328a5ea..a789145fe 100644
--- a/doc/intro.rst
+++ b/doc/intro.rst
@@ -3,7 +3,7 @@ Introduction
This is the documentation for the Sphinx documentation builder. Sphinx is a
tool that translates a set of reStructuredText_ source files into various output
-formats, automatically producing cross-references, indices etc. That is, if
+formats, automatically producing cross-references, indices, etc. That is, if
you have a directory containing a bunch of reST-formatted documents (and
possibly subdirectories of docs in there as well), Sphinx can generate a
nicely-organized arrangement of HTML files (in some other directory) for easy
@@ -17,7 +17,7 @@ docs have a look at `Epydoc <http://epydoc.sourceforge.net/>`_, which also
understands reST.
For a great "introduction" to writing docs in general -- the whys and hows, see
-also `Write the docs <http://write-the-docs.readthedocs.org/>`_, written by Eric
+also `Write the docs <https://write-the-docs.readthedocs.io/>`_, written by Eric
Holscher.
.. _rinohtype: https://github.com/brechtm/rinohtype
@@ -38,7 +38,7 @@ to reStructuredText/Sphinx from other documentation systems.
code to convert Python-doc-style LaTeX markup to Sphinx reST.
* Marcin Wojdyr has written a script to convert Docbook to reST with Sphinx
- markup; it is at `Google Code <https://github.com/wojdyr/db2rst>`_.
+ markup; it is at `GitHub <https://github.com/wojdyr/db2rst>`_.
* Christophe de Vienne wrote a tool to convert from Open/LibreOffice documents
to Sphinx: `odt2sphinx <https://pypi.python.org/pypi/odt2sphinx/>`_.
diff --git a/doc/latex.rst b/doc/latex.rst
index 9f43a2409..87117c164 100644
--- a/doc/latex.rst
+++ b/doc/latex.rst
@@ -267,6 +267,16 @@ The available styling options
``VerbatimBorderColor``
default ``{rgb}{0,0,0}``. The frame color, defaults to black.
+``VerbatimHighlightColor``
+ default ``{rgb}{0.878,1,1}``. The color for highlighted lines.
+
+ .. versionadded:: 1.6.6
+
+.. note::
+
+ Starting with this colour key, and for all others coming next, the actual
+ names declared to "color" or "xcolor" are prefixed with "sphinx".
+
``verbatimsep``
default ``\fboxsep``. The separation between code lines and the frame.
@@ -288,11 +298,6 @@ The available styling options
default ``{rgb}{0,0,0}`` (black). The colour for the two horizontal rules
used by Sphinx in LaTeX for styling a :dudir:`note` type admonition.
-.. note::
-
- The actual colour names declared to "color" or "xcolor" are prefixed with
- "sphinx".
-
``noteborder``, ``hintborder``, ``importantborder``, ``tipborder``
default ``0.5pt``. The width of the two horizontal rules.
@@ -441,6 +446,11 @@ Environments
.. versionadded:: 1.5
options ``verbatimwithframe``, ``verbatimwrapslines``,
``verbatimsep``, ``verbatimborder``.
+ .. versionadded:: 1.6.6
+ support for ``:emphasize-lines:`` option
+ .. versionadded:: 1.6.6
+ easier customizability of the formatting via exposed to user LaTeX macros
+ such as ``\sphinxVerbatimHighlightLine``.
- the bibliography uses ``sphinxthebibliography`` and the Python Module index
as well as the general index both use ``sphinxtheindex``; these environments
are wrappers of the ``thebibliography`` and respectively ``theindex``
diff --git a/doc/man/sphinx-apidoc.rst b/doc/man/sphinx-apidoc.rst
index 803466040..9a13f1401 100644
--- a/doc/man/sphinx-apidoc.rst
+++ b/doc/man/sphinx-apidoc.rst
@@ -91,7 +91,7 @@ Options
Interpret paths recursively according to PEP-0420.
-.. option:: -M
+.. option:: -M, --module-first
Put module documentation before submodule documentation.
@@ -118,6 +118,14 @@ These options are used when :option:`--full` is specified:
Sets the project release to put in generated files (see :confval:`release`).
+Environment
+-----------
+
+.. envvar:: SPHINX_APIDOC_OPTIONS
+
+ A comma-separated list of option to append to generated ``automodule``
+ directives. Defaults to ``members,undoc-members,show-inheritance``.
+
See also
--------
diff --git a/doc/man/sphinx-build.rst b/doc/man/sphinx-build.rst
index 4866282d4..46f213989 100644
--- a/doc/man/sphinx-build.rst
+++ b/doc/man/sphinx-build.rst
@@ -99,17 +99,16 @@ Options
:ref:`builders <builders>`, the following build pipelines are available:
**latexpdf**
- Build LaTeX files and run them through :program:`pdflatex`.
-
- **latexpdfja**
- Build LaTeX files and run them through :program:`platex/dvipdfmx`.
- We recommend using ``latexpdf`` instead.
+ Build LaTeX files and run them through :program:`pdflatex`, or as per
+ :confval:`latex_engine` setting.
+ If :confval:`language` is set to ``'ja'``, will use automatically
+ the :program:`platex/dvipdfmx` latex to PDF pipeline.
**info**
Build Texinfo files and run them through :program:`makeinfo`.
.. important::
- Sphinx only recognizes the ``-M`` option if it is placed first.
+ Sphinx only recognizes the ``-M`` option if it is placed first.
.. versionadded:: 1.2.1
diff --git a/doc/markup/code.rst b/doc/markup/code.rst
index 759008739..3b14bd6e2 100644
--- a/doc/markup/code.rst
+++ b/doc/markup/code.rst
@@ -121,6 +121,8 @@ emphasize particular lines::
.. versionchanged:: 1.3
``lineno-start`` has been added.
+.. versionchanged:: 1.6.6
+ LaTeX supports the ``emphasize-lines`` option.
Includes
^^^^^^^^
@@ -188,8 +190,8 @@ Includes
``lines``, the first allowed line having by convention the line number ``1``.
When lines have been selected in any of the ways described above, the
- line numbers in ``emphasize-lines`` also refer to the selection, with the
- first selected line having number ``1``.
+ line numbers in ``emphasize-lines`` refer to those selected lines, counted
+ consecutively starting at ``1``.
When specifying particular parts of a file to display, it can be useful to
display the original line numbers. This can be done using the
diff --git a/doc/markup/inline.rst b/doc/markup/inline.rst
index 32360baf7..c8dfb6ff7 100644
--- a/doc/markup/inline.rst
+++ b/doc/markup/inline.rst
@@ -63,7 +63,7 @@ Cross-referencing anything
by :rst:role:`doc`, :rst:role:`ref` or :rst:role:`option`.
Custom objects added to the standard domain by extensions (see
- :meth:`.add_object_type`) are also searched.
+ :meth:`Sphinx.add_object_type`) are also searched.
* Then, it looks for objects (targets) in all loaded domains. It is up to
the domains how specific a match must be. For example, in the Python
@@ -227,15 +227,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) <my-figure>```), 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) <my-figure>```), the link caption will serve as title of the reference.
+ 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.
- 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
-----------------------------------------
@@ -355,7 +355,8 @@ in a different style:
.. rst:role:: manpage
A reference to a Unix manual page including the section,
- e.g. ``:manpage:`ls(1)```.
+ e.g. ``:manpage:`ls(1)```. Creates a hyperlink to an external site
+ rendering the manpage if :confval:`manpages_url` is defined.
.. rst:role:: menuselection
diff --git a/setup.cfg b/setup.cfg
index cb6887fc3..c19d0d518 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
@@ -41,5 +44,20 @@ incremental = True
check_untyped_defs = True
warn_unused_ignores = True
-[build_sphinx]
-warning-is-error = 1
+[tool:pytest]
+filterwarnings =
+ ignore::DeprecationWarning:docutils.io
+
+[coverage:run]
+branch = True
+source = sphinx
+
+[coverage:report]
+exclude_lines =
+ # Have to re-enable the standard pragma
+ pragma: no cover
+ # Don't complain if tests don't hit defensive assertion code:
+ raise NotImplementedError
+ # Don't complain if non-runnable code isn't run:
+ if __name__ == .__main__.:
+ignore_errors = True
diff --git a/setup.py b/setup.py
index a4dd6b078..f35e5f88d 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.')
@@ -52,6 +26,7 @@ requires = [
'imagesize',
'requests>=2.0.0',
'setuptools',
+ 'packaging',
'sphinxcontrib-websupport',
]
@@ -68,13 +43,14 @@ extras_require = {
'whoosh>=2.0',
],
'test': [
+ 'mock',
'pytest',
'pytest-cov',
'html5lib',
+ 'flake8',
],
'test:python_version<"3"': [
'enum34',
- 'mock',
],
'test:python_version>="3"': [
'mypy',
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index 68844b8a7..c84e2672f 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -5,7 +5,7 @@
The Sphinx documentation toolchain.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/__main__.py b/sphinx/__main__.py
index 0022d1b0b..fbac1c4f7 100644
--- a/sphinx/__main__.py
+++ b/sphinx/__main__.py
@@ -5,7 +5,7 @@
The Sphinx documentation toolchain.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys
diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py
index 762dc9bbb..e6999bd16 100644
--- a/sphinx/addnodes.py
+++ b/sphinx/addnodes.py
@@ -5,7 +5,7 @@
Additional docutils nodes.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/apidoc.py b/sphinx/apidoc.py
index 0a924b31d..4e20fb7e4 100644
--- a/sphinx/apidoc.py
+++ b/sphinx/apidoc.py
@@ -5,7 +5,7 @@
This file has moved to :py:mod:`sphinx.ext.apidoc`.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/application.py b/sphinx/application.py
index 209c73202..e76f101a3 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -7,7 +7,7 @@
Gracefully adapted from the TextPress system by Armin.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
@@ -19,7 +19,7 @@ import posixpath
from os import path
from collections import deque
-from six import iteritems
+from six import iteritems, itervalues
from six.moves import cStringIO
from docutils import nodes
@@ -33,14 +33,13 @@ from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.environment import BuildEnvironment
from sphinx.events import EventManager
from sphinx.extension import verify_required_extensions
-from sphinx.io import SphinxStandaloneReader
from sphinx.locale import __
from sphinx.registry import SphinxComponentRegistry
from sphinx.util import pycompat # noqa: F401
from sphinx.util import import_object
from sphinx.util import logging
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 # type: ignore
from sphinx.util.docutils import is_html5_writer_available, directive_helper
from sphinx.util.i18n import find_catalog_source_files
@@ -54,7 +53,9 @@ if False:
from sphinx.domains import Domain, Index # NOQA
from sphinx.environment.collectors import EnvironmentCollector # NOQA
from sphinx.extension import Extension # NOQA
+ from sphinx.roles import XRefRole # NOQA
from sphinx.theming import Theme # NOQA
+ from sphinx.util.typing import RoleFunction # NOQA
builtin_extensions = (
'sphinx.builders.applehelp',
@@ -83,6 +84,7 @@ builtin_extensions = (
'sphinx.directives.code',
'sphinx.directives.other',
'sphinx.directives.patches',
+ 'sphinx.io',
'sphinx.parsers',
'sphinx.roles',
'sphinx.transforms.post_transforms',
@@ -119,7 +121,6 @@ class Sphinx(object):
self.env = None # type: BuildEnvironment
self.registry = SphinxComponentRegistry()
self.enumerable_nodes = {} # type: Dict[nodes.Node, Tuple[unicode, Callable]] # NOQA
- self.post_transforms = [] # type: List[Transform]
self.html_themes = {} # type: Dict[unicode, unicode]
self.srcdir = srcdir
@@ -156,10 +157,6 @@ class Sphinx(object):
# status code for command-line application
self.statuscode = 0
- if not path.isdir(outdir):
- logger.info('making output directory...')
- os.makedirs(outdir)
-
# read config
self.tags = Tags(tags)
self.config = Config(confdir, CONFIG_FILENAME,
@@ -196,6 +193,10 @@ class Sphinx(object):
# preload builder module (before init config values)
self.preload_builder(buildername)
+ if not path.isdir(outdir):
+ logger.info('making output directory...')
+ ensuredir(outdir)
+
# the config file itself can be an extension
if self.config.setup:
self._setting_up_extension = ['conf.py']
@@ -443,7 +444,6 @@ class Sphinx(object):
def add_builder(self, builder):
# type: (Type[Builder]) -> None
- logger.debug('[app] adding builder: %r', builder)
self.registry.add_builder(builder)
def add_config_value(self, name, default, rebuild, types=()):
@@ -463,7 +463,6 @@ class Sphinx(object):
def set_translator(self, name, translator_class):
# type: (unicode, Type[nodes.NodeVisitor]) -> None
- logger.info(bold(__('Change of translator for the %s builder.') % name))
self.registry.add_translator(name, translator_class)
def add_node(self, node, **kwds):
@@ -552,39 +551,30 @@ class Sphinx(object):
def add_domain(self, domain):
# type: (Type[Domain]) -> None
- logger.debug('[app] adding domain: %r', domain)
self.registry.add_domain(domain)
def override_domain(self, domain):
# type: (Type[Domain]) -> None
- logger.debug('[app] overriding domain: %r', domain)
self.registry.override_domain(domain)
def add_directive_to_domain(self, domain, name, obj,
has_content=None, argument_spec=None, **option_spec):
# type: (unicode, unicode, Any, bool, Any, Any) -> None
- logger.debug('[app] adding directive to domain: %r',
- (domain, name, obj, has_content, argument_spec, option_spec))
self.registry.add_directive_to_domain(domain, name, obj,
has_content, argument_spec, **option_spec)
def add_role_to_domain(self, domain, name, role):
- # type: (unicode, unicode, Any) -> None
- logger.debug('[app] adding role to domain: %r', (domain, name, role))
+ # type: (unicode, unicode, Union[RoleFunction, XRefRole]) -> None
self.registry.add_role_to_domain(domain, name, role)
def add_index_to_domain(self, domain, index):
# type: (unicode, Type[Index]) -> None
- logger.debug('[app] adding index to domain: %r', (domain, index))
self.registry.add_index_to_domain(domain, index)
def add_object_type(self, directivename, rolename, indextemplate='',
parse_node=None, ref_nodeclass=None, objname='',
doc_field_types=[]):
# type: (unicode, unicode, unicode, Callable, nodes.Node, unicode, List) -> None
- logger.debug('[app] adding object type: %r',
- (directivename, rolename, indextemplate, parse_node,
- ref_nodeclass, objname, doc_field_types))
self.registry.add_object_type(directivename, rolename, indextemplate, parse_node,
ref_nodeclass, objname, doc_field_types)
@@ -601,21 +591,16 @@ class Sphinx(object):
def add_crossref_type(self, directivename, rolename, indextemplate='',
ref_nodeclass=None, objname=''):
# type: (unicode, unicode, unicode, nodes.Node, unicode) -> None
- logger.debug('[app] adding crossref type: %r',
- (directivename, rolename, indextemplate, ref_nodeclass,
- objname))
self.registry.add_crossref_type(directivename, rolename,
indextemplate, ref_nodeclass, objname)
def add_transform(self, transform):
# type: (Type[Transform]) -> None
- logger.debug('[app] adding transform: %r', transform)
- SphinxStandaloneReader.transforms.append(transform)
+ self.registry.add_transform(transform)
def add_post_transform(self, transform):
# type: (Type[Transform]) -> None
- logger.debug('[app] adding post transform: %r', transform)
- self.post_transforms.append(transform)
+ self.registry.add_post_transform(transform)
def add_javascript(self, filename):
# type: (unicode) -> None
@@ -657,15 +642,14 @@ class Sphinx(object):
def add_autodocumenter(self, cls):
# type: (Any) -> None
logger.debug('[app] adding autodocumenter: %r', cls)
- from sphinx.ext import autodoc
- autodoc.add_documenter(cls)
- self.add_directive('auto' + cls.objtype, autodoc.AutoDirective)
+ from sphinx.ext.autodoc.directive import AutodocDirective
+ self.registry.add_documenter(cls.objtype, cls)
+ self.add_directive('auto' + cls.objtype, AutodocDirective)
- def add_autodoc_attrgetter(self, type, getter):
- # type: (Any, Callable) -> None
- logger.debug('[app] adding autodoc attrgetter: %r', (type, getter))
- from sphinx.ext import autodoc
- autodoc.AutoDirective._special_attrgetters[type] = getter
+ def add_autodoc_attrgetter(self, typ, getter):
+ # type: (Type, Callable[[Any, unicode, Any], Any]) -> None
+ logger.debug('[app] adding autodoc attrgetter: %r', (typ, getter))
+ self.registry.add_autodoc_attrgetter(typ, getter)
def add_search_language(self, cls):
# type: (Any) -> None
@@ -676,7 +660,6 @@ class Sphinx(object):
def add_source_parser(self, suffix, parser):
# type: (unicode, Parser) -> None
- logger.debug('[app] adding search source_parser: %r, %r', suffix, parser)
self.registry.add_source_parser(suffix, parser)
def add_env_collector(self, collector):
@@ -689,6 +672,34 @@ class Sphinx(object):
logger.debug('[app] adding HTML theme: %r, %r', name, theme_path)
self.html_themes[name] = theme_path
+ # ---- other methods -------------------------------------------------
+ def is_parallel_allowed(self, typ):
+ # type: (unicode) -> bool
+ """Check parallel processing is allowed or not.
+
+ ``typ`` is a type of processing; ``'read'`` or ``'write'``.
+ """
+ if typ == 'read':
+ attrname = 'parallel_read_safe'
+ elif typ == 'write':
+ attrname = 'parallel_write_safe'
+ else:
+ raise ValueError('parallel type %s is not supported' % typ)
+
+ for ext in itervalues(self.extensions):
+ allowed = getattr(ext, attrname, None)
+ if allowed is None:
+ logger.warning(__("the %s extension does not declare if it is safe "
+ "for parallel %sing, assuming it isn't - please "
+ "ask the extension author to check and make it "
+ "explicit"), ext.name, typ)
+ logger.warning('doing serial %s', typ)
+ return False
+ elif not allowed:
+ return False
+
+ return True
+
class TemplateBridge(object):
"""
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index 496028268..a1e360d2f 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -5,11 +5,10 @@
Builder superclass for all builders.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
-import os
from os import path
import warnings
@@ -18,13 +17,12 @@ try:
except ImportError:
multiprocessing = None
-from six import itervalues
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 +77,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
@@ -373,15 +370,10 @@ class Builder(object):
docnames = set(docnames) & self.env.found_docs
# determine if we can write in parallel
- self.parallel_ok = False
if parallel_available and self.app.parallel > 1 and self.allow_parallel:
- self.parallel_ok = True
- for extension in itervalues(self.app.extensions):
- if not extension.parallel_write_safe:
- logger.warning('the %s extension is not safe for parallel '
- 'writing, doing serial write', extension.name)
- self.parallel_ok = False
- break
+ self.parallel_ok = self.app.is_parallel_allowed('write')
+ else:
+ self.parallel_ok = False
# create a task executor to use for misc. "finish-up" tasks
# if self.parallel_ok:
diff --git a/sphinx/builders/_epub_base.py b/sphinx/builders/_epub_base.py
index a2530b1b1..f4cb8afa0 100644
--- a/sphinx/builders/_epub_base.py
+++ b/sphinx/builders/_epub_base.py
@@ -5,7 +5,7 @@
Base class of epub2/epub3 builders.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/applehelp.py b/sphinx/builders/applehelp.py
index f8df9310c..52ba2ce5c 100644
--- a/sphinx/builders/applehelp.py
+++ b/sphinx/builders/applehelp.py
@@ -5,7 +5,7 @@
Build Apple help books.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
diff --git a/sphinx/builders/changes.py b/sphinx/builders/changes.py
index a73125e30..5309649c6 100644
--- a/sphinx/builders/changes.py
+++ b/sphinx/builders/changes.py
@@ -5,7 +5,7 @@
Changelog builder.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/devhelp.py b/sphinx/builders/devhelp.py
index 9dbbf3c17..88a9be219 100644
--- a/sphinx/builders/devhelp.py
+++ b/sphinx/builders/devhelp.py
@@ -7,7 +7,7 @@
.. _Devhelp: http://live.gnome.org/devhelp
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import absolute_import
diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py
index b684104c1..464d574cc 100644
--- a/sphinx/builders/gettext.py
+++ b/sphinx/builders/gettext.py
@@ -5,7 +5,7 @@
The MessageCatalogBuilder class.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 2710c6e44..f9c9420c2 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -5,7 +5,7 @@
Several HTML builders.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -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/htmlhelp.py b/sphinx/builders/htmlhelp.py
index c2e3bbe2c..0b45601e3 100644
--- a/sphinx/builders/htmlhelp.py
+++ b/sphinx/builders/htmlhelp.py
@@ -6,7 +6,7 @@
Build HTML help support files.
Parts adapted from Python's Doc/tools/prechm.py.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py
index 8b987e658..8fdb2fa49 100644
--- a/sphinx/builders/latex.py
+++ b/sphinx/builders/latex.py
@@ -5,7 +5,7 @@
LaTeX builder.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py
index c52b808cd..ca62b9fe1 100644
--- a/sphinx/builders/linkcheck.py
+++ b/sphinx/builders/linkcheck.py
@@ -5,7 +5,7 @@
The CheckExternalLinksBuilder class.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/manpage.py b/sphinx/builders/manpage.py
index 83e354601..b57a756ee 100644
--- a/sphinx/builders/manpage.py
+++ b/sphinx/builders/manpage.py
@@ -5,7 +5,7 @@
Manual pages builder.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py
index 12c28b1a3..2f56792a9 100644
--- a/sphinx/builders/qthelp.py
+++ b/sphinx/builders/qthelp.py
@@ -5,7 +5,7 @@
Build input files for the Qt collection generator.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -269,7 +269,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
link = node['refuri']
title = htmlescape(node.astext()).replace('"', '&quot;')
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/builders/texinfo.py b/sphinx/builders/texinfo.py
index 3e6816507..82c6f1b9d 100644
--- a/sphinx/builders/texinfo.py
+++ b/sphinx/builders/texinfo.py
@@ -5,7 +5,7 @@
Texinfo builder.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/text.py b/sphinx/builders/text.py
index 29ceaa855..7b977b1b9 100644
--- a/sphinx/builders/text.py
+++ b/sphinx/builders/text.py
@@ -5,7 +5,7 @@
Plain-text Sphinx builder.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py
index 2e416b287..1fe9e2001 100644
--- a/sphinx/builders/websupport.py
+++ b/sphinx/builders/websupport.py
@@ -5,7 +5,7 @@
Builder for the web support package.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/xml.py b/sphinx/builders/xml.py
index d4ebb47ef..599530ac1 100644
--- a/sphinx/builders/xml.py
+++ b/sphinx/builders/xml.py
@@ -5,7 +5,7 @@
Docutils-native XML and pseudo-XML builders.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/cmd/__init__.py b/sphinx/cmd/__init__.py
index 9ffb9e612..a559306d6 100644
--- a/sphinx/cmd/__init__.py
+++ b/sphinx/cmd/__init__.py
@@ -5,6 +5,6 @@
Modules for command line executables.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/cmd/build.py b/sphinx/cmd/build.py
index 6c9d6e3e9..c0c31ae67 100644
--- a/sphinx/cmd/build.py
+++ b/sphinx/cmd/build.py
@@ -5,7 +5,7 @@
Build documentation from a provided source.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/cmd/quickstart.py b/sphinx/cmd/quickstart.py
index ee35b5cc1..fd9b15649 100644
--- a/sphinx/cmd/quickstart.py
+++ b/sphinx/cmd/quickstart.py
@@ -5,7 +5,7 @@
Quickly setup documentation source to work with Sphinx.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
@@ -16,6 +16,7 @@ import os
import re
import sys
import time
+from collections import OrderedDict
from io import open
from os import path
@@ -35,7 +36,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
)
@@ -48,7 +49,22 @@ if False:
TERM_ENCODING = getattr(sys.stdin, 'encoding', None)
-DEFAULT_VALUE = {
+EXTENSIONS = OrderedDict([
+ ('autodoc', 'automatically insert docstrings from modules'),
+ ('doctest', 'automatically test code snippets in doctest blocks'),
+ ('intersphinx', 'link between Sphinx documentation of different projects'),
+ ('todo', 'write "todo" entries that can be shown or hidden on build'),
+ ('coverage', 'checks for documentation coverage'),
+ ('imgmath', 'include math, rendered as PNG or SVG images'),
+ ('mathjax', 'include math, rendered in the browser by MathJax'),
+ ('ifconfig', 'conditional inclusion of content based on config values'),
+ ('viewcode',
+ 'include links to the source code of documented Python objects'),
+ ('githubpages',
+ 'create .nojekyll file to publish the document on GitHub pages'),
+])
+
+DEFAULTS = {
'path': '.',
'sep': False,
'dot': '_',
@@ -56,26 +72,13 @@ DEFAULT_VALUE = {
'suffix': '.rst',
'master': 'index',
'epub': False,
- 'ext_autodoc': False,
- 'ext_doctest': False,
- 'ext_todo': False,
'makefile': True,
'batchfile': True,
}
-EXTENSIONS = ('autodoc', 'doctest', 'intersphinx', 'todo', 'coverage',
- 'imgmath', 'mathjax', 'ifconfig', 'viewcode', 'githubpages')
-
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
@@ -159,8 +162,8 @@ def term_decode(text):
return text.decode('latin1')
-def do_prompt(d, key, text, default=None, validator=nonempty):
- # type: (Dict, unicode, unicode, unicode, Callable[[unicode], Any]) -> None
+def do_prompt(text, default=None, validator=nonempty):
+ # type: (unicode, unicode, Callable[[unicode], Any]) -> Union[unicode, bool]
while True:
if default is not None:
prompt = PROMPT_PREFIX + '%s [%s]: ' % (text, default) # type: unicode
@@ -191,7 +194,7 @@ def do_prompt(d, key, text, default=None, validator=nonempty):
print(red('* ' + str(err)))
continue
break
- d[key] = x
+ return x
def convert_python_source(source, rex=re.compile(r"[uU]('.*?')")):
@@ -235,7 +238,7 @@ def ask_user(d):
* suffix: source file suffix
* master: master document name
* epub: use epub (bool)
- * ext_*: extensions to use (bools)
+ * extensions: extensions to use (list)
* makefile: make Makefile
* batchfile: make command file
"""
@@ -251,7 +254,7 @@ Selected root path: %s''' % d['path']))
else:
print('''
Enter the root path for documentation.''')
- do_prompt(d, 'path', 'Root path for the documentation', '.', is_path)
+ d['path'] = do_prompt('Root path for the documentation', '.', is_path)
while path.isfile(path.join(d['path'], 'conf.py')) or \
path.isfile(path.join(d['path'], 'source', 'conf.py')):
@@ -260,8 +263,8 @@ Enter the root path for documentation.''')
'selected root path.'))
print('sphinx-quickstart will not overwrite existing Sphinx projects.')
print()
- do_prompt(d, 'path', 'Please enter a new root path (or just Enter '
- 'to exit)', '', is_path)
+ d['path'] = do_prompt('Please enter a new root path (or just Enter '
+ 'to exit)', '', is_path)
if not d['path']:
sys.exit(1)
@@ -270,22 +273,22 @@ Enter the root path for documentation.''')
You have two options for placing the build directory for Sphinx output.
Either, you use a directory "_build" within the root path, or you separate
"source" and "build" directories within the root path.''')
- do_prompt(d, 'sep', 'Separate source and build directories (y/n)', 'n',
- boolean)
+ d['sep'] = do_prompt('Separate source and build directories (y/n)',
+ 'n', boolean)
if 'dot' not in d:
print('''
Inside the root directory, two more directories will be created; "_templates"
for custom HTML templates and "_static" for custom stylesheets and other static
files. You can enter another prefix (such as ".") to replace the underscore.''')
- do_prompt(d, 'dot', 'Name prefix for templates and static dir', '_', ok)
+ d['dot'] = do_prompt('Name prefix for templates and static dir', '_', ok)
if 'project' not in d:
print('''
The project name will occur in several places in the built documentation.''')
- do_prompt(d, 'project', 'Project name')
+ d['project'] = do_prompt('Project name')
if 'author' not in d:
- do_prompt(d, 'author', 'Author name(s)')
+ d['author'] = do_prompt('Author name(s)')
if 'version' not in d:
print('''
@@ -294,9 +297,9 @@ software. Each version can have multiple releases. For example, for
Python the version is something like 2.5 or 3.0, while the release is
something like 2.5.1 or 3.0a1. If you don't need this dual structure,
just set both to the same value.''')
- do_prompt(d, 'version', 'Project version', '', allow_empty)
+ d['version'] = do_prompt('Project version', '', allow_empty)
if 'release' not in d:
- do_prompt(d, 'release', 'Project release', d['version'], allow_empty)
+ d['release'] = do_prompt('Project release', d['version'], allow_empty)
if 'language' not in d:
print('''
@@ -306,7 +309,7 @@ translate text that it generates into that language.
For a list of supported codes, see
http://sphinx-doc.org/config.html#confval-language.''')
- do_prompt(d, 'language', 'Project language', 'en')
+ d['language'] = do_prompt('Project language', 'en')
if d['language'] == 'en':
d['language'] = None
@@ -314,7 +317,7 @@ http://sphinx-doc.org/config.html#confval-language.''')
print('''
The file name suffix for source files. Commonly, this is either ".txt"
or ".rst". Only files with this suffix are considered documents.''')
- do_prompt(d, 'suffix', 'Source file suffix', '.rst', suffix)
+ d['suffix'] = do_prompt('Source file suffix', '.rst', suffix)
if 'master' not in d:
print('''
@@ -322,8 +325,8 @@ One document is special in that it is considered the top node of the
"contents tree", that is, it is the root of the hierarchical structure
of the documents. Normally, this is "index", but if your "index"
document is a custom template, you can also set this to another filename.''')
- do_prompt(d, 'master', 'Name of your master document (without suffix)',
- 'index')
+ d['master'] = do_prompt('Name of your master document (without suffix)',
+ 'index')
while path.isfile(path.join(d['path'], d['master'] + d['suffix'])) or \
path.isfile(path.join(d['path'], 'source', d['master'] + d['suffix'])):
@@ -332,65 +335,40 @@ document is a custom template, you can also set this to another filename.''')
'selected root path.' % (d['master'] + d['suffix'])))
print('sphinx-quickstart will not overwrite the existing file.')
print()
- do_prompt(d, 'master', 'Please enter a new file name, or rename the '
- 'existing file and press Enter', d['master'])
+ d['master'] = do_prompt('Please enter a new file name, or rename the '
+ 'existing file and press Enter', d['master'])
if 'epub' not in d:
print('''
Sphinx can also add configuration for epub output:''')
- do_prompt(d, 'epub', 'Do you want to use the epub builder (y/n)',
- 'n', boolean)
-
- if 'ext_autodoc' not in d:
- print('''
-Please indicate if you want to use one of the following Sphinx extensions:''')
- do_prompt(d, 'ext_autodoc', 'autodoc: automatically insert docstrings '
- 'from modules (y/n)', 'n', boolean)
- if 'ext_doctest' not in d:
- do_prompt(d, 'ext_doctest', 'doctest: automatically test code snippets '
- 'in doctest blocks (y/n)', 'n', boolean)
- if 'ext_intersphinx' not in d:
- do_prompt(d, 'ext_intersphinx', 'intersphinx: link between Sphinx '
- 'documentation of different projects (y/n)', 'n', boolean)
- if 'ext_todo' not in d:
- do_prompt(d, 'ext_todo', 'todo: write "todo" entries '
- 'that can be shown or hidden on build (y/n)', 'n', boolean)
- if 'ext_coverage' not in d:
- do_prompt(d, 'ext_coverage', 'coverage: checks for documentation '
- 'coverage (y/n)', 'n', boolean)
- if 'ext_imgmath' not in d:
- do_prompt(d, 'ext_imgmath', 'imgmath: include math, rendered '
- 'as PNG or SVG images (y/n)', 'n', boolean)
- if 'ext_mathjax' not in d:
- do_prompt(d, 'ext_mathjax', 'mathjax: include math, rendered in the '
- 'browser by MathJax (y/n)', 'n', boolean)
- if d['ext_imgmath'] and d['ext_mathjax']:
- print('''Note: imgmath and mathjax cannot be enabled at the same time.
-imgmath has been deselected.''')
- d['ext_imgmath'] = False
- if 'ext_ifconfig' not in d:
- do_prompt(d, 'ext_ifconfig', 'ifconfig: conditional inclusion of '
- 'content based on config values (y/n)', 'n', boolean)
- if 'ext_viewcode' not in d:
- do_prompt(d, 'ext_viewcode', 'viewcode: include links to the source '
- 'code of documented Python objects (y/n)', 'n', boolean)
- if 'ext_githubpages' not in d:
- do_prompt(d, 'ext_githubpages', 'githubpages: create .nojekyll file '
- 'to publish the document on GitHub pages (y/n)', 'n', boolean)
-
- if 'no_makefile' in d:
- d['makefile'] = False
- elif 'makefile' not in d:
+ d['epub'] = do_prompt('Do you want to use the epub builder (y/n)',
+ 'n', boolean)
+
+ if 'extensions' not in d:
+ print('Indicate which of the following Sphinx extensions should be '
+ 'enabled:')
+ d['extensions'] = []
+ for name, description in EXTENSIONS.items():
+ if do_prompt('%s: %s (y/n)' % (name, description), 'n', boolean):
+ d['extensions'].append('sphinx.ext.%s' % name)
+
+ # Handle conflicting options
+ if set(['sphinx.ext.imgmath', 'sphinx.ext.mathjax']).issubset(
+ d['extensions']):
+ print('Note: imgmath and mathjax cannot be enabled at the same '
+ 'time. imgmath has been deselected.')
+ d['extensions'].remove('sphinx.ext.imgmath')
+
+ if 'makefile' not in d:
print('''
A Makefile and a Windows command file can be generated for you so that you
only have to run e.g. `make html' instead of invoking sphinx-build
directly.''')
- do_prompt(d, 'makefile', 'Create Makefile? (y/n)', 'y', boolean)
- if 'no_batchfile' in d:
- d['batchfile'] = False
- elif 'batchfile' not in d:
- do_prompt(d, 'batchfile', 'Create Windows command file? (y/n)',
- 'y', boolean)
+ d['makefile'] = do_prompt('Create Makefile? (y/n)', 'y', boolean)
+
+ if 'batchfile' not in d:
+ d['batchfile'] = do_prompt('Create Windows command file? (y/n)',
+ 'y', boolean)
print()
@@ -400,7 +378,6 @@ def generate(d, overwrite=True, silent=False, templatedir=None):
template = QuickstartRenderer(templatedir=templatedir)
texescape.init()
- indent = ' ' * 4
if 'mastertoctree' not in d:
d['mastertoctree'] = ''
@@ -414,10 +391,6 @@ def generate(d, overwrite=True, silent=False, templatedir=None):
d['now'] = time.asctime()
d['project_underline'] = column_width(d['project']) * '='
d.setdefault('extensions', [])
- for name in EXTENSIONS:
- if d.get('ext_' + name):
- d['extensions'].append('sphinx.ext.' + name)
- d['extensions'] = (',\n' + indent).join(repr(name) for name in d['extensions'])
d['copyright'] = time.strftime('%Y') + ', ' + d['author']
d['author_texescaped'] = text_type(d['author']).\
translate(texescape.tex_escape_map)
@@ -433,11 +406,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,18 +421,20 @@ 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
if overwrite or not path.isfile(fpath):
- print('Creating file %s.' % fpath)
+ if 'quiet' not in d:
+ print('Creating file %s.' % fpath)
with open(fpath, 'wt', encoding='utf-8', newline=newline) as f:
f.write(content)
else:
- print('File %s already exists, skipping.' % fpath)
+ if 'quiet' not in d:
+ print('File %s already exists, skipping.' % fpath)
conf_path = os.path.join(templatedir, 'conf.py_t') if templatedir else None
if not conf_path or not path.isfile(conf_path):
@@ -587,28 +562,28 @@ Makefile to be used with sphinx-build.
group = parser.add_argument_group('Extension options')
for ext in EXTENSIONS:
- group.add_argument('--ext-' + ext, action='store_true',
- dest='ext_' + ext, default=False,
+ group.add_argument('--ext-%s' % ext, action='append_const',
+ const='sphinx.ext.%s' % ext, dest='extensions',
help='enable %s extension' % ext)
group.add_argument('--extensions', metavar='EXTENSIONS', dest='extensions',
- action='append', help='enable extensions')
+ action='append', help='enable arbitrary extensions')
- # TODO(stephenfin): Consider using mutually exclusive groups here
group = parser.add_argument_group('Makefile and Batchfile creation')
- group.add_argument('--makefile', action='store_true', default=False,
+ group.add_argument('--makefile', action='store_true', dest='makefile',
help='create makefile')
- group.add_argument('--no-makefile', action='store_true', default=False,
- help='not create makefile')
- group.add_argument('--batchfile', action='store_true', default=False,
+ group.add_argument('--no-makefile', action='store_false', dest='makefile',
+ help='do not create makefile')
+ group.add_argument('--batchfile', action='store_true', dest='batchfile',
help='create batchfile')
- group.add_argument('--no-batchfile', action='store_true', default=False,
- help='not create batchfile')
- group.add_argument('-M', '--no-use-make-mode', action='store_false',
- dest='make_mode', default=False,
- help='not use make-mode for Makefile/make.bat')
+ group.add_argument('--no-batchfile', action='store_false',
+ dest='batchfile',
+ help='do not create batchfile')
group.add_argument('-m', '--use-make-mode', action='store_true',
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',
+ help='do not use make-mode for Makefile/make.bat')
group = parser.add_argument_group('Project templating')
group.add_argument('-t', '--templatedir', metavar='TEMPLATEDIR',
@@ -648,14 +623,9 @@ def main(argv=sys.argv[1:]):
# quiet mode with all required params satisfied, use default
d.setdefault('version', '')
d.setdefault('release', d['version'])
- d2 = DEFAULT_VALUE.copy()
- d2.update(dict(("ext_" + ext, False) for ext in EXTENSIONS))
+ d2 = DEFAULTS.copy()
d2.update(d)
d = d2
- if 'no_makefile' in d:
- d['makefile'] = False
- if 'no_batchfile' in d:
- d['batchfile'] = False
if not valid_dir(d):
print()
@@ -676,13 +646,12 @@ def main(argv=sys.argv[1:]):
if isinstance(value, binary_type):
d[key] = term_decode(value)
- # parse extensions list
+ # handle use of CSV-style extension values
d.setdefault('extensions', [])
for ext in d['extensions'][:]:
if ',' in ext:
d['extensions'].remove(ext)
- for modname in ext.split(','):
- d['extensions'].append(modname)
+ d['extensions'].extend(ext.split(','))
for variable in d.get('variables', []):
try:
diff --git a/sphinx/cmdline.py b/sphinx/cmdline.py
index 55b0cec98..310760d24 100644
--- a/sphinx/cmdline.py
+++ b/sphinx/cmdline.py
@@ -5,7 +5,7 @@
sphinx-build command-line handling.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
@@ -195,11 +195,7 @@ def main(argv=sys.argv[1:]): # type: ignore
# type: (List[unicode]) -> int
parser = get_parser()
- # parse options
- try:
- args = parser.parse_args(argv)
- except SystemExit as err:
- return err.code
+ args = parser.parse_args(argv)
# get paths (first and second positional argument)
try:
@@ -207,34 +203,28 @@ def main(argv=sys.argv[1:]): # type: ignore
confdir = abspath(args.confdir or srcdir)
if args.noconfig:
confdir = None
+
if not path.isdir(srcdir):
- print('Error: Cannot find source directory `%s\'.' % srcdir,
- file=sys.stderr)
- return 1
+ parser.error('cannot find source directory (%s)' % srcdir)
if not args.noconfig and not path.isfile(path.join(confdir, 'conf.py')):
- print('Error: Config directory doesn\'t contain a conf.py file.',
- file=sys.stderr)
- return 1
+ parser.error("config directory doesn't contain a conf.py file "
+ "(%s)" % confdir)
+
outdir = abspath(args.outputdir)
if srcdir == outdir:
- print('Error: source directory and destination directory are same.',
- file=sys.stderr)
- return 1
+ parser.error('source directory and destination directory are same')
except UnicodeError:
- print(
- 'Error: Multibyte filename not supported on this filesystem '
- 'encoding (%r).' % fs_encoding, file=sys.stderr)
- return 1
+ parser.error('multibyte filename not supported on this filesystem '
+ 'encoding (%r)' % fs_encoding)
# handle remaining filename arguments
filenames = args.filenames
- errored = False
+ missing_files = []
for filename in filenames:
if not path.isfile(filename):
- print('Error: Cannot find file %r.' % filename, file=sys.stderr)
- errored = True
- if errored:
- return 1
+ missing_files.append(filename)
+ if missing_files:
+ parser.error('cannot find files %r' % missing_files)
# likely encoding used for command-line arguments
try:
@@ -244,8 +234,7 @@ def main(argv=sys.argv[1:]): # type: ignore
likely_encoding = None
if args.force_all and filenames:
- print('Error: Cannot combine -a option and filenames.', file=sys.stderr)
- return 1
+ parser.error('cannot combine -a option and filenames')
if args.color == 'no' or (args.color == 'auto' and not color_terminal()):
nocolor()
@@ -258,15 +247,16 @@ def main(argv=sys.argv[1:]): # type: ignore
if args.quiet:
status = None
+
if args.really_quiet:
status = warning = None
+
if warning and args.warnfile:
try:
warnfp = open(args.warnfile, 'w')
except Exception as exc:
- print('Error: Cannot open warning file %r: %s' %
- (args.warnfile, exc), file=sys.stderr)
- sys.exit(1)
+ parser.error('cannot open warning file %r: %s' % (
+ args.warnfile, exc))
warning = Tee(warning, warnfp) # type: ignore
error = warning
@@ -275,9 +265,7 @@ def main(argv=sys.argv[1:]): # type: ignore
try:
key, val = val.split('=', 1)
except ValueError:
- print('Error: -D option argument must be in the form name=value.',
- file=sys.stderr)
- return 1
+ parser.error('-D option argument must be in the form name=value')
if likely_encoding and isinstance(val, binary_type):
try:
val = val.decode(likely_encoding)
@@ -289,9 +277,7 @@ def main(argv=sys.argv[1:]): # type: ignore
try:
key, val = val.split('=')
except ValueError:
- print('Error: -A option argument must be in the form name=value.',
- file=sys.stderr)
- return 1
+ parser.error('-A option argument must be in the form name=value')
try:
val = int(val)
except ValueError:
@@ -315,4 +301,4 @@ def main(argv=sys.argv[1:]): # type: ignore
return app.statuscode
except (Exception, KeyboardInterrupt) as exc:
handle_exception(app, args, exc, error)
- return 1
+ return 2
diff --git a/sphinx/config.py b/sphinx/config.py
index 6abdbc55c..1b3f51a6e 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -5,11 +5,12 @@
Build configuration file handling.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
+import traceback
from os import path, getenv
from six import PY2, PY3, iteritems, string_types, binary_type, text_type, integer_types
@@ -35,6 +36,7 @@ copyright_year_re = re.compile(r'^((\d{4}-)?)(\d{4})(?=[ ,])')
CONFIG_SYNTAX_ERROR = "There is a syntax error in your configuration file: %s"
if PY3:
CONFIG_SYNTAX_ERROR += "\nDid you change the syntax from 2.x to 3.x?"
+CONFIG_ERROR = "There is a programable error in your configuration file:\n\n%s"
CONFIG_EXIT_ERROR = "The configuration file (or one of the modules it imports) " \
"called sys.exit()"
CONFIG_ENUM_WARNING = "The config value `{name}` has to be a one of {candidates}, " \
@@ -123,6 +125,7 @@ class Config(object):
primary_domain = ('py', 'env', [NoneType]),
needs_sphinx = (None, None, string_classes),
needs_extensions = ({}, None),
+ manpages_url = (None, 'env'),
nitpicky = (False, None),
nitpick_ignore = ([], None),
numfig = (False, 'env'),
@@ -135,6 +138,11 @@ class Config(object):
tls_verify = (True, 'env'),
tls_cacerts = (None, 'env'),
+ smartquotes = (True, 'env'),
+ smartquotes_action = ('qDe', 'env'),
+ smartquotes_excludes = ({'languages': ['ja'],
+ 'builders': ['man', 'text']},
+ 'env'),
) # type: Dict[unicode, Tuple]
def __init__(self, dirname, filename, overrides, tags):
@@ -155,6 +163,8 @@ class Config(object):
raise ConfigError(CONFIG_SYNTAX_ERROR % err)
except SystemExit:
raise ConfigError(CONFIG_EXIT_ERROR)
+ except Exception:
+ raise ConfigError(CONFIG_ERROR % traceback.format_exc())
self._raw_config = config
# these two must be preinitialized because extensions can add their
@@ -291,7 +301,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/deprecation.py b/sphinx/deprecation.py
index bb63df330..e28e0f916 100644
--- a/sphinx/deprecation.py
+++ b/sphinx/deprecation.py
@@ -5,7 +5,7 @@
Sphinx deprecation classes and utilities.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py
index dc0cc4f6c..dc51810d3 100644
--- a/sphinx/directives/__init__.py
+++ b/sphinx/directives/__init__.py
@@ -5,7 +5,7 @@
Handlers for additional ReST directives.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py
index 4d7e7a48d..b951e704d 100644
--- a/sphinx/directives/code.py
+++ b/sphinx/directives/code.py
@@ -3,7 +3,7 @@
sphinx.directives.code
~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py
index 626218ca2..4ce709a63 100644
--- a/sphinx/directives/other.py
+++ b/sphinx/directives/other.py
@@ -3,7 +3,7 @@
sphinx.directives.other
~~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/directives/patches.py b/sphinx/directives/patches.py
index 880377ff7..c97340a81 100644
--- a/sphinx/directives/patches.py
+++ b/sphinx/directives/patches.py
@@ -3,7 +3,7 @@
sphinx.directives.patches
~~~~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py
index 6edc3cdb0..c68d37472 100644
--- a/sphinx/domains/__init__.py
+++ b/sphinx/domains/__init__.py
@@ -6,7 +6,7 @@
Support for domains, which are groupings of description directives
and roles describing e.g. constructs of one programming language.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -141,7 +141,7 @@ class Domain(object):
#: domain label: longer, more descriptive (used in messages)
label = ''
#: type (usually directive) name -> ObjType instance
- object_types = {} # type: Dict[unicode, Any]
+ object_types = {} # type: Dict[unicode, ObjType]
#: directive name -> directive class
directives = {} # type: Dict[unicode, Any]
#: role name -> role callable
@@ -161,6 +161,17 @@ class Domain(object):
def __init__(self, env):
# type: (BuildEnvironment) -> None
self.env = env # type: BuildEnvironment
+ self._role_cache = {} # type: Dict[unicode, Callable]
+ self._directive_cache = {} # type: Dict[unicode, Callable]
+ self._role2type = {} # type: Dict[unicode, List[unicode]]
+ self._type2role = {} # type: Dict[unicode, unicode]
+
+ # convert class variables to instance one (to enhance through API)
+ self.object_types = dict(self.object_types)
+ self.directives = dict(self.directives)
+ self.roles = dict(self.roles)
+ self.indices = list(self.indices)
+
if self.name not in env.domaindata:
assert isinstance(self.initial_data, dict)
new_data = copy.deepcopy(self.initial_data)
@@ -170,10 +181,6 @@ class Domain(object):
self.data = env.domaindata[self.name]
if self.data['version'] != self.data_version:
raise IOError('data of %r domain out of date' % self.label)
- self._role_cache = {} # type: Dict[unicode, Callable]
- self._directive_cache = {} # type: Dict[unicode, Callable]
- self._role2type = {} # type: Dict[unicode, List[unicode]]
- self._type2role = {} # type: Dict[unicode, unicode]
for name, obj in iteritems(self.object_types):
for rolename in obj.roles:
self._role2type.setdefault(rolename, []).append(name)
@@ -181,6 +188,18 @@ class Domain(object):
self.objtypes_for_role = self._role2type.get # type: Callable[[unicode], List[unicode]] # NOQA
self.role_for_objtype = self._type2role.get # type: Callable[[unicode], unicode]
+ def add_object_type(self, name, objtype):
+ # type: (unicode, ObjType) -> None
+ """Add an object type."""
+ self.object_types[name] = objtype
+ if objtype.roles:
+ self._type2role[name] = objtype.roles[0]
+ else:
+ self._type2role[name] = ''
+
+ for role in objtype.roles:
+ self._role2type.setdefault(role, []).append(name)
+
def role(self, name):
# type: (unicode) -> Callable
"""Return a role adapter function that always gives the registered
diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py
index b9afd10c2..3030cff8a 100644
--- a/sphinx/domains/c.py
+++ b/sphinx/domains/c.py
@@ -5,7 +5,7 @@
The C language domain.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index 37adb1525..39955d4f4 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -5,7 +5,7 @@
The C++ language domain.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -555,12 +555,12 @@ class DefinitionError(UnicodeMixin, Exception):
class _DuplicateSymbolError(UnicodeMixin, Exception):
- def __init__(self, symbol, candSymbol):
- # type: (Symbol, Symbol) -> None
+ def __init__(self, symbol, declaration):
+ # type: (Symbol, Any) -> None
assert symbol
- assert candSymbol
+ assert declaration
self.symbol = symbol
- self.candSymbol = candSymbol
+ self.declaration = declaration
def __unicode__(self):
# type: () -> unicode
@@ -573,7 +573,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:
@@ -960,7 +960,8 @@ class ASTSizeofParamPack(ASTBase):
def describe_signature(self, signode, mode, env, symbol):
signode.append(nodes.Text('sizeof...('))
- self.identifier.describe_signature(signode, mode, env, symbol=symbol, prefix="")
+ self.identifier.describe_signature(signode, mode, env,
+ symbol=symbol, prefix="", templateArgs="")
signode.append(nodes.Text(')'))
@@ -1195,11 +1196,11 @@ class ASTIdentifier(ASTBase):
# type: () -> unicode
return self.identifier
- def describe_signature(self, signode, mode, env, prefix, symbol):
- # type: (addnodes.desc_signature, unicode, BuildEnvironment, unicode, Symbol) -> None
+ def describe_signature(self, signode, mode, env, prefix, templateArgs, symbol):
+ # type: (Any, unicode, BuildEnvironment, unicode, unicode, Symbol) -> None
_verify_description_mode(mode)
if mode == 'markType':
- targetText = prefix + self.identifier
+ targetText = prefix + self.identifier + templateArgs
pnode = addnodes.pending_xref('', refdomain='cpp',
reftype='identifier',
reftarget=targetText, modname=None,
@@ -1268,7 +1269,7 @@ class ASTTemplateKeyParamPackIdDefault(ASTBase):
if self.identifier:
if not self.parameterPack:
signode += nodes.Text(' ')
- self.identifier.describe_signature(signode, mode, env, '', symbol)
+ self.identifier.describe_signature(signode, mode, env, '', '', symbol)
if self.default:
signode += nodes.Text(' = ')
self.default.describe_signature(signode, 'markType', env, symbol)
@@ -1284,7 +1285,11 @@ class ASTTemplateParamType(ASTBase):
def name(self):
# type: () -> ASTNestedName
id = self.get_identifier()
- return ASTNestedName([ASTNestedNameElement(id, None)], rooted=False)
+ return ASTNestedName([ASTNestedNameElement(id, None)], [False], rooted=False)
+
+ @property
+ def isPack(self):
+ return self.data.parameterPack
def get_identifier(self):
# type: () -> unicode
@@ -1309,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
@@ -1321,7 +1364,7 @@ class ASTTemplateParamTemplateType(ASTBase):
def name(self):
# type: () -> ASTNestedName
id = self.get_identifier()
- return ASTNestedName([ASTNestedNameElement(id, None)], rooted=False)
+ return ASTNestedName([ASTNestedNameElement(id, None)], [False], rooted=False)
def get_identifier(self):
# type: () -> unicode
@@ -1358,16 +1401,16 @@ class ASTTemplateParamNonType(ASTBase):
def name(self):
# type: () -> ASTNestedName
id = self.get_identifier()
- return ASTNestedName([ASTNestedNameElement(id, None)], rooted=False)
+ return ASTNestedName([ASTNestedNameElement(id, None)], [False], rooted=False)
def get_identifier(self):
# type: () -> unicode
name = self.param.name
if name:
assert len(name.names) == 1
- assert name.names[0].identifier
+ assert name.names[0].identOrOp
assert not name.names[0].templateArgs
- return name.names[0].identifier
+ return name.names[0].identOrOp
else:
return None
@@ -1447,6 +1490,16 @@ class ASTTemplateIntroductionParameter(ASTBase):
self.identifier = identifier
self.parameterPack = parameterPack
+ @property
+ def name(self):
+ # type: () -> ASTNestedName
+ id = self.get_identifier()
+ return ASTNestedName([ASTNestedNameElement(id, None)], [False], rooted=False)
+
+ @property
+ def isPack(self):
+ return self.parameterPack
+
def get_identifier(self):
# type: () -> unicode
return self.identifier
@@ -1486,7 +1539,7 @@ class ASTTemplateIntroductionParameter(ASTBase):
# type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None
if self.parameterPack:
signode += nodes.Text('...')
- self.identifier.describe_signature(signode, mode, env, '', symbol)
+ self.identifier.describe_signature(signode, mode, env, '', '', symbol)
class ASTTemplateIntroduction(ASTBase):
@@ -1544,8 +1597,7 @@ class ASTTemplateIntroduction(ASTBase):
class ASTTemplateDeclarationPrefix(ASTBase):
def __init__(self, templates):
# type: (List[Any]) -> None
- assert templates is not None
- assert len(templates) > 0
+ # template is None means it's an explicit instantiation of a variable
self.templates = templates
def get_id(self, version):
@@ -1598,8 +1650,8 @@ class ASTOperatorBuildIn(ASTBase):
else:
return u'operator' + self.op
- def describe_signature(self, signode, mode, env, prefix, symbol):
- # type: (addnodes.desc_signature, unicode, BuildEnvironment, unicode, Symbol) -> None
+ def describe_signature(self, signode, mode, env, prefix, templateArgs, symbol):
+ # type: (addnodes.desc_signature, unicode, Any, unicode, unicode, Symbol) -> None
_verify_description_mode(mode)
identifier = text_type(self)
if mode == 'lastIsName':
@@ -1632,8 +1684,8 @@ class ASTOperatorType(ASTBase):
# type: () -> unicode
return text_type(self)
- def describe_signature(self, signode, mode, env, prefix, symbol):
- # type: (addnodes.desc_signature, unicode, BuildEnvironment, unicode, Symbol) -> None
+ def describe_signature(self, signode, mode, env, prefix, templateArgs, symbol):
+ # type: (addnodes.desc_signature, unicode, Any, unicode, unicode, Symbol) -> None
_verify_description_mode(mode)
identifier = text_type(self)
if mode == 'lastIsName':
@@ -1662,8 +1714,8 @@ class ASTOperatorLiteral(ASTBase):
# type: () -> unicode
return u'operator""' + text_type(self.identifier)
- def describe_signature(self, signode, mode, env, prefix, symbol):
- # type: (addnodes.desc_signature, unicode, BuildEnvironment, unicode, Symbol) -> None
+ def describe_signature(self, signode, mode, env, prefix, templateArgs, symbol):
+ # type: (addnodes.desc_signature, unicode, Any, unicode, unicode, Symbol) -> None
_verify_description_mode(mode)
identifier = text_type(self)
if mode == 'lastIsName':
@@ -1736,9 +1788,9 @@ class ASTTemplateArgs(ASTBase):
class ASTNestedNameElement(ASTBase):
- def __init__(self, identifier, templateArgs):
+ def __init__(self, identOrOp, templateArgs):
# type: (Any, Any) -> None
- self.identifier = identifier
+ self.identOrOp = identOrOp
self.templateArgs = templateArgs
def is_operator(self):
@@ -1747,30 +1799,33 @@ class ASTNestedNameElement(ASTBase):
def get_id(self, version):
# type: (int) -> unicode
- res = self.identifier.get_id(version)
+ res = self.identOrOp.get_id(version)
if self.templateArgs:
res += self.templateArgs.get_id(version)
return res
def __unicode__(self):
# type: () -> unicode
- res = text_type(self.identifier)
+ res = text_type(self.identOrOp)
if self.templateArgs:
res += text_type(self.templateArgs)
return res
def describe_signature(self, signode, mode, env, prefix, symbol):
# type: (addnodes.desc_signature, unicode, BuildEnvironment, unicode, Symbol) -> None
- self.identifier.describe_signature(signode, mode, env, prefix, symbol)
- if self.templateArgs:
+ tArgs = text_type(self.templateArgs) if self.templateArgs is not None else ''
+ self.identOrOp.describe_signature(signode, mode, env, prefix, tArgs, symbol)
+ if self.templateArgs is not None:
self.templateArgs.describe_signature(signode, mode, env, symbol)
class ASTNestedName(ASTBase):
- def __init__(self, names, rooted):
- # type: (List[Any], bool) -> None
+ def __init__(self, names, templates, rooted):
+ # type: (List[Any], List[bool], bool) -> None
assert len(names) > 0
self.names = names
+ self.templates = templates
+ assert len(self.names) == len(self.templates)
self.rooted = rooted
@property
@@ -1811,44 +1866,62 @@ class ASTNestedName(ASTBase):
res = [] # type: List[unicode]
if self.rooted:
res.append('')
- for n in self.names:
- res.append(text_type(n))
+ for i in range(len(self.names)):
+ n = self.names[i]
+ t = self.templates[i]
+ if t:
+ res.append("template " + text_type(n))
+ else:
+ res.append(text_type(n))
return '::'.join(res)
def describe_signature(self, signode, mode, env, symbol):
# type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None
_verify_description_mode(mode)
# just print the name part, with template args, not template params
- if mode == 'lastIsName':
- addname = [] # type: List[unicode]
- if self.rooted:
- addname.append('')
- for n in self.names[:-1]:
- addname.append(text_type(n))
- addname = '::'.join(addname) # type: ignore
- if len(self.names) > 1:
- addname += '::'
- signode += addnodes.desc_addname(addname, addname)
- self.names[-1].describe_signature(signode, mode, env, '', symbol)
- elif mode == 'noneIsName':
+ if mode == 'noneIsName':
signode += nodes.Text(text_type(self))
elif mode == 'param':
name = text_type(self)
signode += nodes.emphasis(name, name)
- elif mode == 'markType':
- # each element should be a pending xref targeting the complete
+ elif mode == 'markType' or mode == 'lastIsName':
+ # Each element should be a pending xref targeting the complete
# prefix. however, only the identifier part should be a link, such
# that template args can be a link as well.
+ # For 'lastIsName' we should also prepend template parameter lists.
+ templateParams = [] # type: List[Any]
+ if mode == 'lastIsName':
+ assert symbol is not None
+ if symbol.declaration.templatePrefix is not None:
+ templateParams = symbol.declaration.templatePrefix.templates
+ iTemplateParams = 0
+ templateParamsPrefix = u''
prefix = '' # type: unicode
first = True
- for name in self.names:
+ names = self.names[:-1] if mode == 'lastIsName' else self.names
+ for i in range(len(names)):
+ name = names[i]
+ template = self.templates[i]
if not first:
signode += nodes.Text('::')
prefix += '::'
+ if template:
+ signode += nodes.Text("template ")
first = False
if name != '':
- name.describe_signature(signode, mode, env, prefix, symbol) # type: ignore
+ if (name.templateArgs and # type: ignore
+ iTemplateParams < len(templateParams)):
+ templateParamsPrefix += text_type(templateParams[iTemplateParams])
+ iTemplateParams += 1
+ name.describe_signature(signode, 'markType', # type: ignore
+ env, templateParamsPrefix + prefix, symbol)
prefix += text_type(name)
+ if mode == 'lastIsName':
+ if len(self.names) > 1:
+ signode += addnodes.desc_addname('::', '::')
+ if self.templates[-1]:
+ signode += nodes.Text("template ")
+ self.names[-1].describe_signature(signode, mode, env, '', symbol)
else:
raise Exception('Unknown description mode: %s' % mode)
@@ -1955,8 +2028,13 @@ class ASTFunctionParameter(ASTBase):
self.arg = arg
self.ellipsis = ellipsis
- def get_id(self, version):
- # type: (int) -> unicode
+ 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++
+ if symbol:
+ # the anchor will be our parent
+ return symbol.parent.declaration.get_id(version, prefixed=None)
+ # else, do the usual
if self.ellipsis:
return 'z'
else:
@@ -1991,6 +2069,11 @@ class ASTParametersQualifiers(ASTBase):
self.final = final
self.initializer = initializer
+ @property
+ def function_params(self):
+ # type: () -> Any
+ return self.args
+
def get_modifiers_id(self, version):
# type: (int) -> unicode
res = []
@@ -2294,6 +2377,11 @@ class ASTDeclaratorPtr(ASTBase):
# type: () -> unicode
return self.next.name
+ @property
+ def function_params(self):
+ # type: () -> Any
+ return self.next.function_params
+
def require_space_after_declSpecs(self):
# type: () -> bool
# TODO: if has paramPack, then False ?
@@ -2386,6 +2474,11 @@ class ASTDeclaratorRef(ASTBase):
# type: () -> unicode
return self.next.name
+ @property
+ def function_params(self):
+ # type: () -> Any
+ return self.next.function_params
+
def require_space_after_declSpecs(self):
# type: () -> bool
return self.next.require_space_after_declSpecs()
@@ -2437,6 +2530,11 @@ class ASTDeclaratorParamPack(ASTBase):
# type: () -> unicode
return self.next.name
+ @property
+ def function_params(self):
+ # type: () -> Any
+ return self.next.function_params
+
def require_space_after_declSpecs(self):
# type: () -> bool
return False
@@ -2497,6 +2595,11 @@ class ASTDeclaratorMemPtr(ASTBase):
# type: () -> unicode
return self.next.name
+ @property
+ def function_params(self):
+ # type: () -> Any
+ return self.next.function_params
+
def require_space_after_declSpecs(self):
# type: () -> bool
return True
@@ -2589,6 +2692,11 @@ class ASTDeclaratorParen(ASTBase):
# type: () -> unicode
return self.inner.name
+ @property
+ def function_params(self):
+ # type: () -> Any
+ return self.inner.function_params
+
def require_space_after_declSpecs(self):
# type: () -> bool
return True
@@ -2651,6 +2759,11 @@ class ASTDeclaratorNameParamQual(ASTBase):
# type: () -> unicode
return self.declId
+ @property
+ def function_params(self):
+ # type: () -> Any
+ return self.paramQual.function_params
+
def get_modifiers_id(self, version): # only the modifiers for a function, e.g.,
# type: (int) -> unicode
# cv-qualifiers
@@ -2747,6 +2860,11 @@ class ASTType(ASTBase):
name = self.decl.name
return name
+ @property
+ def function_params(self):
+ # type: () -> Any
+ return self.decl.function_params
+
def get_id(self, version, objectType=None, symbol=None):
# type: (int, unicode, Symbol) -> unicode
if version == 1:
@@ -3093,6 +3211,13 @@ class ASTDeclaration(ASTBase):
# type: () -> unicode
return self.declaration.name
+ @property
+ def function_params(self):
+ # type: () -> Any
+ if self.objectType != 'function':
+ return None
+ return self.declaration.function_params
+
def get_id(self, version, prefixed=True):
# type: (int, bool) -> unicode
if version == 1:
@@ -3131,15 +3256,15 @@ class ASTDeclaration(ASTBase):
def describe_signature(self, signode, mode, env, options):
# type: (addnodes.desc_signature, unicode, BuildEnvironment, Dict) -> None
_verify_description_mode(mode)
+ assert self.symbol
# The caller of the domain added a desc_signature node.
# Always enable multiline:
signode['is_multiline'] = True
# Put each line in a desc_signature_line node.
mainDeclNode = addnodes.desc_signature_line()
mainDeclNode.sphinx_cpp_tagname = 'declarator'
- mainDeclNode['add_permalink'] = True
+ mainDeclNode['add_permalink'] = not self.symbol.isRedeclaration
- assert self.symbol
if self.templatePrefix:
self.templatePrefix.describe_signature(signode, mode, env,
symbol=self.symbol,
@@ -3185,27 +3310,25 @@ class Symbol(object):
# type: () -> None
if not self.parent:
# parent == None means global scope, so declaration means a parent
- assert not self.identifier
+ assert not self.identOrOp
assert not self.templateParams
assert not self.templateArgs
assert not self.declaration
assert not self.docname
else:
- if not self.identifier:
- # in case it's an operator
- assert self.declaration
if self.declaration:
assert self.docname
- def __init__(self, parent, identifier,
+ def __init__(self, parent, identOrOp,
templateParams, templateArgs, declaration, docname):
# type: (Any, Any, Any, Any, Any, unicode) -> None
self.parent = parent
- self.identifier = identifier
+ self.identOrOp = identOrOp
self.templateParams = templateParams # template<templateParams>
self.templateArgs = templateArgs # identifier<templateArgs>
self.declaration = declaration
self.docname = docname
+ self.isRedeclaration = False
self._assert_invariants()
self.children = [] # type: List[Any]
@@ -3219,14 +3342,30 @@ class Symbol(object):
for p in self.templateParams.params:
if not p.get_identifier():
continue
- # only add a declaration if we our selfs from a declaration
+ # only add a declaration if we our selfs are from a declaration
if declaration:
decl = ASTDeclaration('templateParam', None, None, p)
else:
decl = None
nne = ASTNestedNameElement(p.get_identifier(), None)
- nn = ASTNestedName([nne], rooted=False)
+ nn = ASTNestedName([nne], [False], rooted=False)
self._add_symbols(nn, [], decl, docname)
+ # add symbols for function parameters, if any
+ if declaration is not None and declaration.function_params is not None:
+ for p in declaration.function_params:
+ if p.arg is None:
+ continue
+ nn = p.arg.name
+ if nn is None:
+ continue
+ # (comparing to the template params: we have checked that we are a declaration)
+ decl = ASTDeclaration('functionParam', None, None, p)
+ assert not nn.rooted
+ assert len(nn.names) == 1
+ identOrOp = nn.names[0].identOrOp
+ Symbol(parent=self, identOrOp=identOrOp,
+ templateParams=None, templateArgs=None,
+ declaration=decl, docname=docname)
def _fill_empty(self, declaration, docname):
# type: (Any, unicode) -> None
@@ -3248,12 +3387,7 @@ class Symbol(object):
if sChild.declaration and sChild.docname == docname:
sChild.declaration = None
sChild.docname = None
- # Just remove operators, because there is no identification if
- # they got removed.
- # Don't remove other symbols because they may be used in namespace
- # directives.
- if sChild.identifier or sChild.declaration:
- newChildren.append(sChild)
+ newChildren.append(sChild)
self.children = newChildren
def get_all_symbols(self):
@@ -3273,39 +3407,51 @@ class Symbol(object):
symbols.reverse()
key = []
for s in symbols:
- if s.identifier:
- nne = ASTNestedNameElement(s.identifier, s.templateArgs)
- else:
- assert s.declaration
- nne = s.declaration.name.names[-1]
+ nne = ASTNestedNameElement(s.identOrOp, s.templateArgs)
key.append((nne, s.templateParams))
return key
def get_full_nested_name(self):
# type: () -> ASTNestedName
names = []
+ templates = []
for nne, templateParams in self.get_lookup_key():
names.append(nne)
- return ASTNestedName(names, rooted=False)
+ templates.append(False)
+ return ASTNestedName(names, templates, rooted=False)
- def _find_named_symbol(self, identifier, templateParams,
- templateArgs, operator,
+ def _find_named_symbol(self, identOrOp, templateParams, templateArgs,
templateShorthand, matchSelf):
- # type: (Any, Any, Any, Any, Any, bool) -> Symbol
- assert (identifier is None) != (operator is None)
+ # type: (Any, Any, Any, Any, bool) -> Symbol
+
+ def isSpecialization():
+ # the names of the template parameters must be given exactly as args
+ # and params that are packs must in the args be the name expanded
+ if len(templateParams.params) != len(templateArgs.args):
+ return True
+ for i in range(len(templateParams.params)):
+ param = templateParams.params[i]
+ arg = templateArgs.args[i]
+ # TODO: doing this by string manipulation is probably not the most efficient
+ paramName = text_type(param.name)
+ argTxt = text_type(arg)
+ isArgPackExpansion = argTxt.endswith('...')
+ if param.isPack != isArgPackExpansion:
+ return True
+ argName = argTxt[:-3] if isArgPackExpansion else argTxt
+ if paramName != argName:
+ return True
+ return False
+ if templateParams is not None and templateArgs is not None:
+ # If both are given, but it's not a specialization, then do lookup as if
+ # there is no argument list.
+ # For example: template<typename T> int A<T>::var;
+ if not isSpecialization():
+ templateArgs = None
def matches(s):
- if s.identifier != identifier:
+ if s.identOrOp != identOrOp:
return False
- if not s.identifier:
- if not s.declaration:
- return False
- assert operator
- name = s.declaration.name.names[-1]
- if not name.is_operator():
- return False
- if text_type(name) != text_type(operator):
- return False
if (s.templateParams is None) != (templateParams is None):
if templateParams is not None:
# we query with params, they must match params
@@ -3346,10 +3492,7 @@ class Symbol(object):
names = nestedName.names
iTemplateDecl = 0
for name in names[:-1]:
- # there shouldn't be anything inside an operator
- # (other than template parameters, which are not added this way, right?)
- assert not name.is_operator()
- identifier = name.identifier
+ identOrOp = name.identOrOp
templateArgs = name.templateArgs
if templateArgs:
assert iTemplateDecl < len(templateDecls)
@@ -3357,27 +3500,20 @@ class Symbol(object):
iTemplateDecl += 1
else:
templateParams = None
- symbol = parentSymbol._find_named_symbol(identifier,
+ symbol = parentSymbol._find_named_symbol(identOrOp,
templateParams,
templateArgs,
- operator=None,
templateShorthand=False,
matchSelf=False)
if symbol is None:
- symbol = Symbol(parent=parentSymbol, identifier=identifier,
+ symbol = Symbol(parent=parentSymbol, identOrOp=identOrOp,
templateParams=templateParams,
templateArgs=templateArgs, declaration=None,
docname=None)
parentSymbol = symbol
name = names[-1]
- if name.is_operator():
- identifier = None
- templateArgs = None
- operator = name
- else:
- identifier = name.identifier
- templateArgs = name.templateArgs
- operator = None
+ identOrOp = name.identOrOp
+ templateArgs = name.templateArgs
if iTemplateDecl < len(templateDecls):
if iTemplateDecl + 1 != len(templateDecls):
print(text_type(templateDecls))
@@ -3387,10 +3523,9 @@ class Symbol(object):
else:
assert iTemplateDecl == len(templateDecls)
templateParams = None
- symbol = parentSymbol._find_named_symbol(identifier,
+ symbol = parentSymbol._find_named_symbol(identOrOp,
templateParams,
templateArgs,
- operator,
templateShorthand=False,
matchSelf=False)
if symbol:
@@ -3405,25 +3540,29 @@ class Symbol(object):
# .. class:: Test
symbol._fill_empty(declaration, docname)
return symbol
- # It may simply be a functin overload, so let's compare ids.
- candSymbol = Symbol(parent=parentSymbol, identifier=identifier,
+ # It may simply be a function overload, so let's compare ids.
+ isRedeclaration = True
+ candSymbol = Symbol(parent=parentSymbol, identOrOp=identOrOp,
templateParams=templateParams,
templateArgs=templateArgs,
declaration=declaration,
docname=docname)
- newId = declaration.get_newest_id()
- oldId = symbol.declaration.get_newest_id()
- if newId != oldId:
- # we already inserted the symbol, so return the new one
- symbol = candSymbol
- else:
+ if declaration.objectType == "function":
+ newId = declaration.get_newest_id()
+ oldId = symbol.declaration.get_newest_id()
+ if newId != oldId:
+ # we already inserted the symbol, so return the new one
+ symbol = candSymbol
+ isRedeclaration = False
+ if isRedeclaration:
# Redeclaration of the same symbol.
# Let the new one be there, but raise an error to the client
# so it can use the real symbol as subscope.
# This will probably result in a duplicate id warning.
- raise _DuplicateSymbolError(symbol, candSymbol)
+ candSymbol.isRedeclaration = True
+ raise _DuplicateSymbolError(symbol, declaration)
else:
- symbol = Symbol(parent=parentSymbol, identifier=identifier,
+ symbol = Symbol(parent=parentSymbol, identOrOp=identOrOp,
templateParams=templateParams,
templateArgs=templateArgs,
declaration=declaration,
@@ -3434,22 +3573,9 @@ class Symbol(object):
# type: (Any, List[unicode], BuildEnvironment) -> None
assert other is not None
for otherChild in other.children:
- if not otherChild.identifier:
- if not otherChild.declaration:
- print("Problem in symbol tree merging")
- print("OtherChild.dump:")
- print(otherChild.dump(0))
- print("Other.dump:")
- print(other.dump(0))
- assert otherChild.declaration
- operator = otherChild.declaration.name.names[-1]
- assert operator.is_operator()
- else:
- operator = None
- ourChild = self._find_named_symbol(otherChild.identifier,
+ ourChild = self._find_named_symbol(otherChild.identOrOp,
otherChild.templateParams,
otherChild.templateArgs,
- operator,
templateShorthand=False,
matchSelf=False)
if ourChild is None:
@@ -3494,12 +3620,12 @@ class Symbol(object):
templateDecls = []
return self._add_symbols(nestedName, templateDecls, declaration, docname)
- def find_identifier(self, identifier, matchSelf):
+ def find_identifier(self, identOrOp, matchSelf):
# type: (Any, bool) -> Symbol
- if matchSelf and self.identifier and self.identifier == identifier:
+ if matchSelf and self.identOrOp == identOrOp:
return self
for s in self.children:
- if s.identifier and s.identifier == identifier:
+ if s.identOrOp == identOrOp:
return s
return None
@@ -3507,24 +3633,18 @@ class Symbol(object):
# type: (List[Tuple[Any, Any]]) -> Symbol
s = self
for name, templateParams in key:
- if name.is_operator():
- identifier = None
- templateArgs = None
- operator = name
- else:
- identifier = name.identifier
- templateArgs = name.templateArgs
- operator = None
- s = s._find_named_symbol(identifier, templateParams,
- templateArgs, operator,
+ identOrOp = name.identOrOp
+ templateArgs = name.templateArgs
+ s = s._find_named_symbol(identOrOp,
+ templateParams, templateArgs,
templateShorthand=False,
matchSelf=False)
if not s:
return None
return s
- def find_name(self, nestedName, templateDecls, templateShorthand, matchSelf):
- # type: (Any, Any, Any, bool) -> Symbol
+ def find_name(self, nestedName, templateDecls, typ, templateShorthand, matchSelf):
+ # type: (Any, Any, Any, Any, bool) -> Symbol
# templateShorthand: missing template parameter lists for templates is ok
# TODO: unify this with the _add_symbols
@@ -3540,53 +3660,51 @@ class Symbol(object):
firstName = names[0]
if not firstName.is_operator():
while parentSymbol.parent:
- if parentSymbol.find_identifier(firstName.identifier,
+ if parentSymbol.find_identifier(firstName.identOrOp,
matchSelf=matchSelf):
- break
+ # if we are in the scope of a constructor but wants to reference the class
+ # we need to walk one extra up
+ if (len(names) == 1 and typ == 'class' and matchSelf and
+ parentSymbol.parent and
+ parentSymbol.parent.identOrOp == firstName.identOrOp):
+ pass
+ else:
+ break
parentSymbol = parentSymbol.parent
-
iTemplateDecl = 0
for iName in range(len(names)):
name = names[iName]
if iName + 1 == len(names):
- if name.is_operator():
- identifier = None
- templateArgs = None
- operator = name
- else:
- identifier = name.identifier
- templateArgs = name.templateArgs
- operator = None
+ identOrOp = name.identOrOp
+ templateArgs = name.templateArgs
if iTemplateDecl < len(templateDecls):
assert iTemplateDecl + 1 == len(templateDecls)
templateParams = templateDecls[iTemplateDecl]
else:
assert iTemplateDecl == len(templateDecls)
templateParams = None
- symbol = parentSymbol._find_named_symbol(identifier,
- templateParams,
- templateArgs,
- operator,
+ symbol = parentSymbol._find_named_symbol(identOrOp,
+ templateParams, templateArgs,
templateShorthand=templateShorthand,
matchSelf=matchSelf)
- if symbol:
+ if symbol is not None:
return symbol
- else:
- return None
+ # try without template params and args
+ symbol = parentSymbol._find_named_symbol(identOrOp,
+ None, None,
+ templateShorthand=templateShorthand,
+ matchSelf=matchSelf)
+ return symbol
else:
- # there shouldn't be anything inside an operator
- assert not name.is_operator()
- identifier = name.identifier
+ identOrOp = name.identOrOp
templateArgs = name.templateArgs
if templateArgs and iTemplateDecl < len(templateDecls):
templateParams = templateDecls[iTemplateDecl]
iTemplateDecl += 1
else:
templateParams = None
- symbol = parentSymbol._find_named_symbol(identifier,
- templateParams,
- templateArgs,
- operator=None,
+ symbol = parentSymbol._find_named_symbol(identOrOp,
+ templateParams, templateArgs,
templateShorthand=templateShorthand,
matchSelf=matchSelf)
if symbol is None:
@@ -3609,8 +3727,8 @@ class Symbol(object):
res.append(text_type(self.templateParams))
res.append('\n')
res.append('\t' * indent)
- if self.identifier:
- res.append(text_type(self.identifier))
+ if self.identOrOp:
+ res.append(text_type(self.identOrOp))
else:
res.append(text_type(self.declaration))
if self.templateArgs:
@@ -3651,6 +3769,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
@@ -3991,6 +4111,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."
@@ -4231,7 +4358,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.
@@ -4240,6 +4367,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
@@ -4350,7 +4481,8 @@ class DefinitionParser(object):
def _parse_nested_name(self, memberPointer=False):
# type: (bool) -> ASTNestedName
- names = []
+ names = [] # type: List[Any]
+ templates = [] # type: List[bool]
self.skip_ws()
rooted = False
@@ -4358,14 +4490,17 @@ class DefinitionParser(object):
rooted = True
while 1:
self.skip_ws()
- if self.skip_word_and_ws('template'):
- self.fail("'template' in nested name not implemented.")
- elif self.skip_word_and_ws('operator'):
- op = self._parse_operator()
- names.append(op)
+ if len(names) > 0:
+ template = self.skip_word_and_ws('template')
+ else:
+ template = False
+ templates.append(template)
+ if self.skip_word_and_ws('operator'):
+ identOrOp = self._parse_operator()
else:
if not self.match(_identifier_re):
if memberPointer and len(names) > 0:
+ templates.pop()
break
self.fail("Expected identifier in nested name.")
identifier = self.matched_text
@@ -4373,24 +4508,24 @@ class DefinitionParser(object):
if identifier in _keywords:
self.fail("Expected identifier in nested name, "
"got keyword: %s" % identifier)
- # try greedily to get template parameters,
- # but otherwise a < might be because we are in an expression
- pos = self.pos
- try:
- templateArgs = self._parse_template_argument_list()
- except DefinitionError as ex:
- self.pos = pos
- templateArgs = None
- self.otherErrors.append(ex)
- identifier = ASTIdentifier(identifier) # type: ignore
- names.append(ASTNestedNameElement(identifier, templateArgs))
+ identOrOp = ASTIdentifier(identifier)
+ # try greedily to get template arguments,
+ # but otherwise a < might be because we are in an expression
+ pos = self.pos
+ try:
+ templateArgs = self._parse_template_argument_list()
+ except DefinitionError as ex:
+ self.pos = pos
+ templateArgs = None
+ self.otherErrors.append(ex)
+ names.append(ASTNestedNameElement(identOrOp, templateArgs))
self.skip_ws()
if not self.skip_string('::'):
if memberPointer:
self.fail("Expected '::' in pointer to member (function).")
break
- return ASTNestedName(names, rooted)
+ return ASTNestedName(names, templates, rooted)
def _parse_trailing_type_spec(self):
# type: () -> Any
@@ -4468,7 +4603,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?
@@ -4655,7 +4790,7 @@ class DefinitionParser(object):
if self.match(_identifier_re):
identifier = ASTIdentifier(self.matched_text)
nne = ASTNestedNameElement(identifier, None)
- declId = ASTNestedName([nne], rooted=False)
+ declId = ASTNestedName([nne], [False], rooted=False)
# if it's a member pointer, we may have '::', which should be an error
self.skip_ws()
if self.current_char == ':':
@@ -4789,8 +4924,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('='):
@@ -4799,15 +4934,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)
@@ -4897,12 +5035,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
@@ -5026,13 +5200,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('>'):
@@ -5094,7 +5269,13 @@ class DefinitionParser(object):
# the saved position is only used to provide a better error message
pos = self.pos
if self.skip_word("template"):
- params = self._parse_template_parameter_list() # type: Any
+ try:
+ params = self._parse_template_parameter_list() # type: Any
+ except DefinitionError as e:
+ if objectType == 'member' and len(templates) == 0:
+ return ASTTemplateDeclarationPrefix(None)
+ else:
+ raise e
else:
params = self._parse_template_introduction()
if not params:
@@ -5111,20 +5292,25 @@ class DefinitionParser(object):
return ASTTemplateDeclarationPrefix(templates)
def _check_template_consistency(self, nestedName, templatePrefix,
- fullSpecShorthand):
- # type: (Any, Any, bool) -> ASTTemplateDeclarationPrefix
+ fullSpecShorthand, isMember=False):
+ # type: (Any, Any, Any, bool) -> ASTTemplateDeclarationPrefix
numArgs = nestedName.num_templates()
+ isMemberInstantiation = False
if not templatePrefix:
numParams = 0
else:
- numParams = len(templatePrefix.templates)
+ if isMember and templatePrefix.templates is None:
+ numParams = 0
+ isMemberInstantiation = True
+ else:
+ numParams = len(templatePrefix.templates)
if numArgs + 1 < numParams:
self.fail("Too few template argument lists comapred to parameter"
" lists. Argument lists: %d, Parameter lists: %d."
% (numArgs, numParams))
if numArgs > numParams:
numExtra = numArgs - numParams
- if not fullSpecShorthand:
+ if not fullSpecShorthand and not isMemberInstantiation:
msg = "Too many template argument lists compared to parameter" \
" lists. Argument lists: %d, Parameter lists: %d," \
" Extra empty parameters lists prepended: %d." \
@@ -5138,7 +5324,7 @@ class DefinitionParser(object):
newTemplates = []
for i in range(numExtra):
newTemplates.append(ASTTemplateParams([]))
- if templatePrefix:
+ if templatePrefix and not isMemberInstantiation:
newTemplates.extend(templatePrefix.templates)
templatePrefix = ASTTemplateDeclarationPrefix(newTemplates)
return templatePrefix
@@ -5193,7 +5379,8 @@ class DefinitionParser(object):
assert False
templatePrefix = self._check_template_consistency(declaration.name,
templatePrefix,
- fullSpecShorthand=False)
+ fullSpecShorthand=False,
+ isMember=objectType == 'member')
return ASTDeclaration(objectType, visibility,
templatePrefix, declaration)
@@ -5244,7 +5431,7 @@ class DefinitionParser(object):
def _make_phony_error_name():
# type: () -> ASTNestedName
nne = ASTNestedNameElement(ASTIdentifier("PhonyNameDueToError"), None)
- return ASTNestedName([nne], rooted=False)
+ return ASTNestedName([nne], [False], rooted=False)
class CPPObject(ObjectDescription):
@@ -5279,7 +5466,7 @@ class CPPObject(ObjectDescription):
# then add the name to the parent scope
symbol = ast.symbol
assert symbol
- assert symbol.identifier is not None
+ assert symbol.identOrOp is not None
assert symbol.templateParams is None
assert symbol.templateArgs is None
parentSymbol = symbol.parent
@@ -5292,7 +5479,7 @@ class CPPObject(ObjectDescription):
if parentDecl is None:
# the parent is not explicitly declared
# TODO: we could warn, but it could be a style to just assume
- # enumerator parnets to be scoped
+ # enumerator parents to be scoped
return
if parentDecl.objectType != 'enum':
# TODO: maybe issue a warning, enumerators in non-enums is weird,
@@ -5302,13 +5489,13 @@ class CPPObject(ObjectDescription):
return
targetSymbol = parentSymbol.parent
- s = targetSymbol.find_identifier(symbol.identifier, matchSelf=False)
+ s = targetSymbol.find_identifier(symbol.identOrOp, matchSelf=False)
if s is not None:
# something is already declared with that name
return
declClone = symbol.declaration.clone()
declClone.enumeratorScopedSymbol = symbol
- Symbol(parent=targetSymbol, identifier=symbol.identifier,
+ Symbol(parent=targetSymbol, identOrOp=symbol.identOrOp,
templateParams=None, templateArgs=None,
declaration=declClone,
docname=self.env.docname)
@@ -5332,13 +5519,25 @@ class CPPObject(ObjectDescription):
'report as bug (id=%s).' % (text_type(ast), newestId))
name = text_type(ast.symbol.get_full_nested_name()).lstrip(':')
- strippedName = name
- for prefix in self.env.config.cpp_index_common_prefix:
- if name.startswith(prefix):
- strippedName = strippedName[len(prefix):]
+ # Add index entry, but not if it's a declaration inside a concept
+ isInConcept = False
+ s = ast.symbol.parent
+ while s is not None:
+ decl = s.declaration
+ s = s.parent
+ if decl is None:
+ continue
+ if decl.objectType == 'concept':
+ isInConcept = True
break
- indexText = self.get_index_text(strippedName)
- self.indexnode['entries'].append(('single', indexText, newestId, '', None))
+ if not isInConcept:
+ strippedName = name
+ for prefix in self.env.config.cpp_index_common_prefix:
+ if name.startswith(prefix):
+ strippedName = strippedName[len(prefix):]
+ break
+ indexText = self.get_index_text(strippedName)
+ self.indexnode['entries'].append(('single', indexText, newestId, '', None))
if newestId not in self.state.document.ids:
# if the name is not unique, the first one will win
@@ -5397,6 +5596,7 @@ class CPPObject(ObjectDescription):
# Assume we are actually in the old symbol,
# instead of the newly created duplicate.
self.env.temp_data['cpp:last_symbol'] = e.symbol
+ self.warn("Duplicate declaration.")
if ast.objectType == 'enumerator':
self._add_enumerator_to_parent(ast)
@@ -5738,6 +5938,7 @@ class CPPDomain(Domain):
def _resolve_xref_inner(self, env, fromdocname, builder, typ,
target, node, contnode, emitWarnings=True):
# type: (BuildEnvironment, unicode, Builder, unicode, unicode, nodes.Node, nodes.Node, bool) -> nodes.Node # NOQA
+
class Warner(object):
def warn(self, msg):
if emitWarnings:
@@ -5783,7 +5984,7 @@ class CPPDomain(Domain):
templateDecls = ast.templatePrefix.templates
else:
templateDecls = []
- s = parentSymbol.find_name(name, templateDecls,
+ s = parentSymbol.find_name(name, templateDecls, typ,
templateShorthand=True,
matchSelf=True)
if s is None or s.declaration is None:
diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py
index 9ecf4a4b0..81f86f754 100644
--- a/sphinx/domains/javascript.py
+++ b/sphinx/domains/javascript.py
@@ -5,7 +5,7 @@
The JavaScript domain.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
index d64938452..fa96590b3 100644
--- a/sphinx/domains/python.py
+++ b/sphinx/domains/python.py
@@ -5,7 +5,7 @@
The Python domain.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py
index 2a7dffc4d..936dd1b9f 100644
--- a/sphinx/domains/rst.py
+++ b/sphinx/domains/rst.py
@@ -5,7 +5,7 @@
The reStructuredText domain.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
index bfaa57c4f..68baa04aa 100644
--- a/sphinx/domains/std.py
+++ b/sphinx/domains/std.py
@@ -5,7 +5,7 @@
The standard domain.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -959,12 +959,18 @@ class StandardDomain(Domain):
def get_full_qualified_name(self, node):
# type: (nodes.Node) -> unicode
- progname = node.get('std:program')
- target = node.get('reftarget')
- if progname is None or target is None:
- return None
+ if node.get('reftype') == 'option':
+ progname = node.get('std:program')
+ command = ws_re.split(node.get('reftarget'))
+ if progname:
+ command.insert(0, progname)
+ option = command.pop()
+ if command:
+ return '.'.join(['-'.join(command), option])
+ else:
+ return None
else:
- return '.'.join([progname, target])
+ return None
def setup(app):
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index 5ebf9d4d5..251a88589 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -5,7 +5,7 @@
Global creation environment.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -19,8 +19,9 @@ import warnings
from os import path
from copy import copy
from collections import defaultdict
+from contextlib import contextmanager
-from six import BytesIO, itervalues, class_types, next
+from six import BytesIO, itervalues, class_types, next, iteritems
from six.moves import cPickle as pickle
from docutils.utils import Reporter, get_source_line, normalize_language_tag
@@ -40,15 +41,14 @@ from sphinx.util.matching import compile_matchers
from sphinx.util.parallel import ParallelTasks, parallel_available, make_chunks
from sphinx.util.websupport import is_commentable
from sphinx.errors import SphinxError, ExtensionError
-from sphinx.locale import __
-from sphinx.transforms import SphinxTransformer
+from sphinx.transforms import SphinxTransformer, SphinxSmartQuotes
from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.environment.adapters.indexentries import IndexEntries
from sphinx.environment.adapters.toctree import TocTree
if False:
# For type annotation
- from typing import Any, Callable, Dict, IO, Iterator, List, Pattern, Set, Tuple, Type, Union # NOQA
+ from typing import Any, Callable, Dict, IO, Iterator, List, Pattern, Set, Tuple, Type, Union, Generator # NOQA
from docutils import nodes # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
@@ -67,6 +67,7 @@ default_settings = {
'sectsubtitle_xform': False,
'halt_level': 5,
'file_insertion_enabled': True,
+ 'smartquotes_locales': [],
}
# This is increased every time an environment attribute is added
@@ -83,6 +84,22 @@ versioning_conditions = {
} # type: Dict[unicode, Union[bool, Callable]]
+@contextmanager
+def sphinx_smartquotes_action(env):
+ # type: (BuildEnvironment) -> Generator
+ if not hasattr(SphinxSmartQuotes, 'smartquotes_action'):
+ # less than docutils-0.14
+ yield
+ else:
+ # docutils-0.14 or above
+ try:
+ original = SphinxSmartQuotes.smartquotes_action
+ SphinxSmartQuotes.smartquotes_action = env.config.smartquotes_action
+ yield
+ finally:
+ SphinxSmartQuotes.smartquotes_action = original
+
+
class NoUri(Exception):
"""Raised by get_relative_uri if there is no URI available."""
pass
@@ -558,21 +575,10 @@ class BuildEnvironment(object):
self.app.emit('env-before-read-docs', self, docnames)
# check if we should do parallel or serial read
- par_ok = False
if parallel_available and len(docnames) > 5 and self.app.parallel > 1:
- for ext in itervalues(self.app.extensions):
- if ext.parallel_read_safe is None:
- logger.warning(__('the %s extension does not declare if it is safe '
- 'for parallel reading, assuming it isn\'t - please '
- 'ask the extension author to check and make it '
- 'explicit'), ext.name)
- logger.warning('doing serial read')
- break
- elif ext.parallel_read_safe is False:
- break
- else:
- # all extensions support parallel-read
- par_ok = True
+ par_ok = self.app.is_parallel_allowed('read')
+ else:
+ par_ok = False
if par_ok:
self._read_parallel(docnames, self.app, nproc=self.app.parallel)
@@ -596,7 +602,8 @@ class BuildEnvironment(object):
# remove all inventory entries for that file
app.emit('env-purge-doc', self, docname)
self.clear_doc(docname)
- self.read_doc(docname, app)
+ with sphinx_smartquotes_action(self):
+ self.read_doc(docname, app)
def _read_parallel(self, docnames, app, nproc):
# type: (List[unicode], Sphinx, int) -> None
@@ -608,8 +615,9 @@ class BuildEnvironment(object):
def read_process(docs):
# type: (List[unicode]) -> unicode
self.app = app
- for docname in docs:
- self.read_doc(docname, app)
+ with sphinx_smartquotes_action(self):
+ for docname in docs:
+ self.read_doc(docname, app)
# allow pickling self to send it back
return BuildEnvironment.dumps(self)
@@ -657,7 +665,19 @@ class BuildEnvironment(object):
language = self.config.language or 'en'
self.settings['language_code'] = language
if 'smart_quotes' not in self.settings:
- self.settings['smart_quotes'] = True
+ self.settings['smart_quotes'] = self.config.smartquotes
+
+ # some conditions exclude smart quotes, overriding smart_quotes
+ for valname, vallist in iteritems(self.config.smartquotes_excludes):
+ if valname == 'builders':
+ # this will work only for checking first build target
+ if self.app.builder.name in vallist:
+ self.settings['smart_quotes'] = False
+ break
+ elif valname == 'languages':
+ if self.config.language in vallist:
+ self.settings['smart_quotes'] = False
+ break
# confirm selected language supports smart_quotes or not
for tag in normalize_language_tag(language):
@@ -878,7 +898,7 @@ class BuildEnvironment(object):
transformer = SphinxTransformer(doctree)
transformer.set_environment(self)
- transformer.add_transforms(self.app.post_transforms)
+ transformer.add_transforms(self.app.registry.get_post_transforms())
transformer.apply_transforms()
finally:
self.temp_data = backup
diff --git a/sphinx/environment/adapters/__init__.py b/sphinx/environment/adapters/__init__.py
index 9171ac0be..f945c4250 100644
--- a/sphinx/environment/adapters/__init__.py
+++ b/sphinx/environment/adapters/__init__.py
@@ -5,6 +5,6 @@
Sphinx environment adapters
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/environment/adapters/asset.py b/sphinx/environment/adapters/asset.py
index 02557a8c4..91f2cf8eb 100644
--- a/sphinx/environment/adapters/asset.py
+++ b/sphinx/environment/adapters/asset.py
@@ -5,7 +5,7 @@
Assets adapter for sphinx.environment.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/environment/adapters/indexentries.py b/sphinx/environment/adapters/indexentries.py
index 946e635ef..4a39b1bd0 100644
--- a/sphinx/environment/adapters/indexentries.py
+++ b/sphinx/environment/adapters/indexentries.py
@@ -5,7 +5,7 @@
Index entries adapters for sphinx.environment.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
diff --git a/sphinx/environment/adapters/toctree.py b/sphinx/environment/adapters/toctree.py
index 03c1d8aa9..bf725b619 100644
--- a/sphinx/environment/adapters/toctree.py
+++ b/sphinx/environment/adapters/toctree.py
@@ -5,7 +5,7 @@
Toctree adapter for sphinx.environment.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/environment/collectors/__init__.py b/sphinx/environment/collectors/__init__.py
index 917b34afb..9d9f5347c 100644
--- a/sphinx/environment/collectors/__init__.py
+++ b/sphinx/environment/collectors/__init__.py
@@ -5,7 +5,7 @@
The data collector components for sphinx.environment.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/environment/collectors/asset.py b/sphinx/environment/collectors/asset.py
index 3a0e1fefd..0d7a193e3 100644
--- a/sphinx/environment/collectors/asset.py
+++ b/sphinx/environment/collectors/asset.py
@@ -5,7 +5,7 @@
The image collector for sphinx.environment.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/environment/collectors/dependencies.py b/sphinx/environment/collectors/dependencies.py
index 5e20d1245..bf42b12e9 100644
--- a/sphinx/environment/collectors/dependencies.py
+++ b/sphinx/environment/collectors/dependencies.py
@@ -5,7 +5,7 @@
The dependencies collector components for sphinx.environment.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/environment/collectors/indexentries.py b/sphinx/environment/collectors/indexentries.py
index 0b1c35934..f9fa8bab7 100644
--- a/sphinx/environment/collectors/indexentries.py
+++ b/sphinx/environment/collectors/indexentries.py
@@ -5,7 +5,7 @@
Index entries collector for sphinx.environment.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/environment/collectors/metadata.py b/sphinx/environment/collectors/metadata.py
index ae8a8cb4c..7d54d2fe6 100644
--- a/sphinx/environment/collectors/metadata.py
+++ b/sphinx/environment/collectors/metadata.py
@@ -5,7 +5,7 @@
The metadata collector components for sphinx.environment.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/environment/collectors/title.py b/sphinx/environment/collectors/title.py
index 3335c2cc7..eb23b975f 100644
--- a/sphinx/environment/collectors/title.py
+++ b/sphinx/environment/collectors/title.py
@@ -5,7 +5,7 @@
The title collector components for sphinx.environment.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/environment/collectors/toctree.py b/sphinx/environment/collectors/toctree.py
index f19fd5d26..53e1045d9 100644
--- a/sphinx/environment/collectors/toctree.py
+++ b/sphinx/environment/collectors/toctree.py
@@ -5,7 +5,7 @@
Toctree collector for sphinx.environment.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -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/errors.py b/sphinx/errors.py
index 7662c95a3..eef1a157a 100644
--- a/sphinx/errors.py
+++ b/sphinx/errors.py
@@ -6,7 +6,7 @@
Contains SphinxError and a few subclasses (in an extra module to avoid
circular import problems).
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/events.py b/sphinx/events.py
index 99decfff5..097f61fc6 100644
--- a/sphinx/events.py
+++ b/sphinx/events.py
@@ -7,7 +7,7 @@
Gracefully adapted from the TextPress system by Armin.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
diff --git a/sphinx/ext/__init__.py b/sphinx/ext/__init__.py
index e529ee9e5..440c01a15 100644
--- a/sphinx/ext/__init__.py
+++ b/sphinx/ext/__init__.py
@@ -5,6 +5,6 @@
Contains Sphinx features not activated by default.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/apidoc.py b/sphinx/ext/apidoc.py
index 6edab379b..cec9d8138 100644
--- a/sphinx/ext/apidoc.py
+++ b/sphinx/ext/apidoc.py
@@ -11,7 +11,7 @@
Copyright 2008 Société des arts technologiques (SAT),
http://www.sat.qc.ca/
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -27,7 +27,7 @@ from fnmatch import fnmatch
from sphinx import __display_version__
from sphinx.cmd.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
@@ -117,7 +117,11 @@ def create_package_file(root, master_package, subroot, py_files, opts, subs, is_
text += '\n'
# build a list of directories that are szvpackages (contain an INITPY file)
- subs = [sub for sub in subs if path.isfile(path.join(root, sub, INITPY))]
+ # and also checks the INITPY file is not empty, or there are other python
+ # source files in that folder.
+ # (depending on settings - but shall_skip() takes care of that)
+ subs = [sub for sub in subs if not
+ shall_skip(path.join(root, sub, INITPY), opts)]
# if there are some package directories, add a TOC for theses subpackages
if subs:
text += format_heading(2, 'Subpackages')
@@ -358,8 +362,8 @@ Note: By default this script will not overwrite already created files.""")
group = parser.add_argument_group('extension options')
for ext in EXTENSIONS:
- group.add_argument('--ext-' + ext, action='store_true',
- dest='ext_' + ext, default=False,
+ group.add_argument('--ext-%s' % ext, action='append_const',
+ const='sphinx.ext.%s' % ext, dest='extensions',
help='enable %s extension' % ext)
return parser
@@ -382,8 +386,8 @@ def main(argv=sys.argv[1:]):
if not path.isdir(rootpath):
print('%s is not a directory.' % rootpath, file=sys.stderr)
sys.exit(1)
- if not path.isdir(args.destdir) and not args.dryrun:
- os.makedirs(args.destdir)
+ if not args.dryrun:
+ ensuredir(args.destdir)
excludes = [path.abspath(exclude) for exclude in args.exclude_pattern]
modules = recurse_tree(rootpath, excludes, args)
@@ -408,9 +412,8 @@ def main(argv=sys.argv[1:]):
suffix = '.' + args.suffix,
master = 'index',
epub = True,
- ext_autodoc = True,
- ext_viewcode = True,
- ext_todo = True,
+ extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode',
+ 'sphinx.ext.todo'],
makefile = True,
batchfile = True,
make_mode = True,
@@ -420,9 +423,8 @@ def main(argv=sys.argv[1:]):
module_path = rootpath,
append_syspath = args.append_syspath,
)
- enabled_exts = {'ext_' + ext: getattr(args, 'ext_' + ext)
- for ext in EXTENSIONS if getattr(args, 'ext_' + ext)}
- d.update(enabled_exts)
+ if args.extensions:
+ d['extensions'].extend(args.extensions)
if isinstance(args.header, binary_type):
d['project'] = d['project'].decode('utf-8')
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 305242f66..d9e82813d 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -7,45 +7,46 @@
the doctree, thus avoiding duplication between docstrings and documentation
for those who like elaborate docstrings.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
import sys
import inspect
-import traceback
+import warnings
-from six import PY2, iterkeys, iteritems, itervalues, text_type, class_types, string_types
+from six import iteritems, itervalues, text_type, class_types, string_types
-from docutils import nodes
-from docutils.utils import assemble_option_dict
-from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
import sphinx
-from sphinx.ext.autodoc.importer import _MockImporter, import_module
+from sphinx.deprecation import RemovedInSphinx20Warning
+from sphinx.ext.autodoc.importer import mock, import_object, get_object_members
+from sphinx.ext.autodoc.importer import _MockImporter # to keep compatibility # NOQA
from sphinx.ext.autodoc.inspector import format_annotation, formatargspec # to keep compatibility # NOQA
from sphinx.util import rpartition, force_decode
from sphinx.locale import _
from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.application import ExtensionError
from sphinx.util import logging
-from sphinx.util.nodes import nested_parse_with_titles
from sphinx.util.inspect import Signature, isdescriptor, safe_getmembers, \
safe_getattr, object_description, is_builtin_class_method, \
- isenumclass, isenumattribute, getdoc
+ isenumattribute, getdoc
from sphinx.util.docstrings import prepare_docstring
if False:
# For type annotation
from types import ModuleType # NOQA
from typing import Any, Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union # NOQA
+ from docutils import nodes # NOQA
from docutils.utils import Reporter # NOQA
from sphinx.application import Sphinx # NOQA
+ from sphinx.ext.autodoc.directive import DocumenterBridge # NOQA
logger = logging.getLogger(__name__)
+
# This type isn't exposed directly in any modules, but can be found
# here in most Python versions
MethodDescriptorType = type(type.__subclasses__)
@@ -62,42 +63,11 @@ py_ext_sig_re = re.compile(
''', re.VERBOSE)
-class DefDict(dict):
- """A dict that returns a default on nonexisting keys."""
- def __init__(self, default):
- # type: (Any) -> None
- dict.__init__(self)
- self.default = default
-
- def __getitem__(self, key):
- # type: (Any) -> Any
- try:
- return dict.__getitem__(self, key)
- except KeyError:
- return self.default
-
- def __bool__(self):
- # type: () -> bool
- # docutils check "if option_spec"
- return True
- __nonzero__ = __bool__ # for python2 compatibility
-
-
def identity(x):
# type: (Any) -> Any
return x
-class Options(dict):
- """A dict/attribute hybrid that returns None on nonexisting keys."""
- def __getattr__(self, name):
- # type: (unicode) -> Any
- try:
- return self[name.replace('_', '-')]
- except KeyError:
- return None
-
-
ALL = object()
INSTANCEATTR = object()
@@ -145,6 +115,9 @@ class AutodocReporter(object):
"""
def __init__(self, viewlist, reporter):
# type: (ViewList, Reporter) -> None
+ warnings.warn('AutodocReporter is now deprecated. '
+ 'Use sphinx.util.docutils.switch_source_input() instead.',
+ RemovedInSphinx20Warning)
self.viewlist = viewlist
self.reporter = reporter
@@ -283,14 +256,10 @@ class Documenter(object):
option_spec = {'noindex': bool_option} # type: Dict[unicode, Callable]
- @staticmethod
- def get_attr(obj, name, *defargs):
+ def get_attr(self, obj, name, *defargs):
# type: (Any, unicode, Any) -> Any
"""getattr() override for types such as Zope interfaces."""
- for typ, func in iteritems(AutoDirective._special_attrgetters):
- if isinstance(obj, typ):
- return func(obj, name, *defargs)
- return safe_getattr(obj, name, *defargs)
+ return autodoc_attrgetter(self.env.app, obj, name, *defargs)
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
@@ -299,7 +268,7 @@ class Documenter(object):
raise NotImplementedError('must be implemented in subclasses')
def __init__(self, directive, name, indent=u''):
- # type: (Directive, unicode, unicode) -> None
+ # type: (DocumenterBridge, unicode, unicode) -> None
self.directive = directive
self.env = directive.env
self.options = directive.genopt
@@ -323,6 +292,12 @@ class Documenter(object):
# the module analyzer to get at attribute docs, or None
self.analyzer = None # type: Any
+ @property
+ def documenters(self):
+ # type: () -> Dict[unicode, Type[Documenter]]
+ """Returns registered Documenter classes"""
+ return get_documenters(self.env.app)
+
def add_line(self, line, source, *lineno):
# type: (unicode, unicode, int) -> None
"""Append one line of generated reST to the output."""
@@ -353,8 +328,7 @@ class Documenter(object):
explicit_modname, path, base, args, retann = \
py_ext_sig_re.match(self.name).groups() # type: ignore
except AttributeError:
- self.directive.warn('invalid signature for auto%s (%r)' %
- (self.objtype, self.name))
+ logger.warning('invalid signature for auto%s (%r)' % (self.objtype, self.name))
return False
# support explicit module and class name separation via ::
@@ -383,52 +357,17 @@ class Documenter(object):
Returns True if successful, False if an error occurred.
"""
- if self.objpath:
- logger.debug('[autodoc] from %s import %s',
- self.modname, '.'.join(self.objpath))
- # always enable mock import hook
- # it will do nothing if autodoc_mock_imports is empty
- import_hook = _MockImporter(self.env.config.autodoc_mock_imports)
- try:
- logger.debug('[autodoc] import %s', self.modname)
- obj = import_module(self.modname, self.env.config.autodoc_warningiserror)
- parent = None
- self.module = obj
- logger.debug('[autodoc] => %r', obj)
- for part in self.objpath:
- parent = obj
- logger.debug('[autodoc] getattr(_, %r)', part)
- obj = self.get_attr(obj, part)
- logger.debug('[autodoc] => %r', obj)
- self.object_name = part
- self.parent = parent
- self.object = obj
- return True
- # this used to only catch SyntaxError, ImportError and AttributeError,
- # but importing modules with side effects can raise all kinds of errors
- except (Exception, SystemExit) as e:
- if self.objpath:
- errmsg = 'autodoc: failed to import %s %r from module %r' % \
- (self.objtype, '.'.join(self.objpath), self.modname)
- else:
- errmsg = 'autodoc: failed to import %s %r' % \
- (self.objtype, self.fullname)
- if isinstance(e, SystemExit):
- errmsg += ('; the module executes module level statement ' +
- 'and it might call sys.exit().')
- elif isinstance(e, ImportError):
- errmsg += '; the following exception was raised:\n%s' % e.args[0]
- else:
- errmsg += '; the following exception was raised:\n%s' % \
- traceback.format_exc()
- if PY2:
- errmsg = errmsg.decode('utf-8') # type: ignore
- logger.debug(errmsg)
- self.directive.warn(errmsg)
- self.env.note_reread()
- return False
- finally:
- import_hook.disable()
+ with mock(self.env.config.autodoc_mock_imports):
+ try:
+ ret = import_object(self.modname, self.objpath, self.objtype,
+ attrgetter=self.get_attr,
+ warningiserror=self.env.config.autodoc_warningiserror)
+ self.module, self.parent, self.object_name, self.object = ret
+ return True
+ except ImportError as exc:
+ logger.warning(exc.args[0])
+ self.env.note_reread()
+ return False
def get_real_modname(self):
# type: () -> str
@@ -486,8 +425,8 @@ class Documenter(object):
try:
args = self.format_args()
except Exception as err:
- self.directive.warn('error while formatting arguments for '
- '%s: %s' % (self.fullname, err))
+ logger.warning('error while formatting arguments for %s: %s' %
+ (self.fullname, err))
args = None
retann = self.retann
@@ -599,57 +538,24 @@ class Documenter(object):
If *want_all* is True, return all members. Else, only return those
members given by *self.options.members* (which may also be none).
"""
- analyzed_member_names = set()
- if self.analyzer:
- attr_docs = self.analyzer.find_attr_docs()
- namespace = '.'.join(self.objpath)
- for item in iteritems(attr_docs):
- if item[0][0] == namespace:
- analyzed_member_names.add(item[0][1])
+ members = get_object_members(self.object, self.objpath, self.get_attr, self.analyzer)
if not want_all:
if not self.options.members:
return False, []
# specific members given
- members = []
- for mname in self.options.members:
- try:
- members.append((mname, self.get_attr(self.object, mname)))
- except AttributeError:
- if mname not in analyzed_member_names:
- self.directive.warn('missing attribute %s in object %s'
- % (mname, self.fullname))
+ selected = []
+ for name in self.options.members:
+ if name in members:
+ selected.append((name, members[name].value))
+ else:
+ logger.warning('missing attribute %s in object %s' %
+ (name, self.fullname))
+ return False, sorted(selected)
elif self.options.inherited_members:
- # safe_getmembers() uses dir() which pulls in members from all
- # base classes
- members = safe_getmembers(self.object, attr_getter=self.get_attr)
+ return False, sorted((m.name, m.value) for m in itervalues(members))
else:
- # __dict__ contains only the members directly defined in
- # the class (but get them via getattr anyway, to e.g. get
- # unbound method objects instead of function objects);
- # using list(iterkeys()) because apparently there are objects for which
- # __dict__ changes while getting attributes
- try:
- obj_dict = self.get_attr(self.object, '__dict__')
- except AttributeError:
- members = []
- else:
- members = [(mname, self.get_attr(self.object, mname, None))
- for mname in list(iterkeys(obj_dict))]
-
- # Py34 doesn't have enum members in __dict__.
- if isenumclass(self.object):
- members.extend(
- item for item in self.object.__members__.items()
- if item not in members
- )
-
- membernames = set(m[0] for m in members)
- # add instance attributes from the analyzer
- for aname in analyzed_member_names:
- if aname not in membernames and \
- (want_all or aname in self.options.members):
- members.append((aname, INSTANCEATTR))
- return False, sorted(members)
+ return False, sorted((m.name, m.value) for m in itervalues(members)
+ if m.directly_defined)
def filter_members(self, members, want_all):
# type: (List[Tuple[unicode, Any]], bool) -> List[Tuple[unicode, Any, bool]]
@@ -708,8 +614,7 @@ class Documenter(object):
elif (namespace, membername) in attr_docs:
if want_all and membername.startswith('_'):
# ignore members whose name starts with _ by default
- keep = self.options.private_members and \
- (has_doc or self.options.undoc_members)
+ keep = self.options.private_members
else:
# keep documented attributes
keep = True
@@ -762,7 +667,7 @@ class Documenter(object):
# document non-skipped members
memberdocumenters = [] # type: List[Tuple[Documenter, bool]]
for (mname, member, isattr) in self.filter_members(members, want_all):
- classes = [cls for cls in itervalues(AutoDirective._registry)
+ classes = [cls for cls in itervalues(self.documenters)
if cls.can_document_member(member, mname, isattr, self)]
if not classes:
# don't know how to document this member
@@ -813,11 +718,11 @@ class Documenter(object):
"""
if not self.parse_name():
# need a module to import
- self.directive.warn(
+ logger.warning(
'don\'t know which module to import for autodocumenting '
'%r (try placing a "module" or "currentmodule" directive '
- 'in the document, or giving an explicit module name)'
- % self.name)
+ 'in the document, or giving an explicit module name)' %
+ self.name)
return
# now, import the module and get object to document
@@ -891,7 +796,7 @@ class ModuleDocumenter(Documenter):
'platform': identity, 'deprecated': bool_option,
'member-order': identity, 'exclude-members': members_set_option,
'private-members': bool_option, 'special-members': members_option,
- 'imported-members': bool_option,
+ 'imported-members': bool_option, 'ignore-module-all': bool_option
} # type: Dict[unicode, Callable]
@classmethod
@@ -903,15 +808,15 @@ class ModuleDocumenter(Documenter):
def resolve_name(self, modname, parents, path, base):
# type: (str, Any, str, Any) -> Tuple[str, List[unicode]]
if modname is not None:
- self.directive.warn('"::" in automodule name doesn\'t make sense')
+ logger.warning('"::" in automodule name doesn\'t make sense')
return (path or '') + base, []
def parse_name(self):
# type: () -> bool
ret = Documenter.parse_name(self)
if self.args or self.retann:
- self.directive.warn('signature arguments or return annotation '
- 'given for automodule %s' % self.fullname)
+ logger.warning('signature arguments or return annotation '
+ 'given for automodule %s' % self.fullname)
return ret
def add_directive_header(self, sig):
@@ -933,7 +838,8 @@ class ModuleDocumenter(Documenter):
def get_object_members(self, want_all):
# type: (bool) -> Tuple[bool, List[Tuple[unicode, object]]]
if want_all:
- if not hasattr(self.object, '__all__'):
+ if (self.options.ignore_module_all or not
+ hasattr(self.object, '__all__')):
# for implicit module members, check __module__ to avoid
# documenting imported objects
return True, safe_getmembers(self.object)
@@ -942,7 +848,7 @@ class ModuleDocumenter(Documenter):
# Sometimes __all__ is broken...
if not isinstance(memberlist, (list, tuple)) or not \
all(isinstance(entry, string_types) for entry in memberlist):
- self.directive.warn(
+ logger.warning(
'__all__ should be a list of strings, not %r '
'(in module %s) -- ignoring __all__' %
(memberlist, self.fullname))
@@ -955,10 +861,10 @@ class ModuleDocumenter(Documenter):
try:
ret.append((mname, safe_getattr(self.object, mname)))
except AttributeError:
- self.directive.warn(
+ logger.warning(
'missing attribute mentioned in :members: or __all__: '
- 'module %s, attribute %s' % (
- safe_getattr(self.object, '__name__', '???'), mname))
+ 'module %s, attribute %s' %
+ (safe_getattr(self.object, '__name__', '???'), mname))
return False, ret
@@ -1497,118 +1403,56 @@ class InstanceAttributeDocumenter(AttributeDocumenter):
AttributeDocumenter.add_content(self, more_content, no_docstring=True)
-class AutoDirective(Directive):
- """
- The AutoDirective class is used for all autodoc directives. It dispatches
- most of the work to one of the Documenters, which it selects through its
- *_registry* dictionary.
+class DeprecatedDict(dict):
+ def __init__(self, message):
+ self.message = message
+ super(DeprecatedDict, self).__init__()
- The *_special_attrgetters* attribute is used to customize ``getattr()``
- calls that the Documenters make; its entries are of the form ``type:
- getattr_function``.
+ def __setitem__(self, key, value):
+ warnings.warn(self.message, RemovedInSphinx20Warning)
+ super(DeprecatedDict, self).__setitem__(key, value)
+
+ def setdefault(self, key, default=None):
+ warnings.warn(self.message, RemovedInSphinx20Warning)
+ super(DeprecatedDict, self).setdefault(key, default)
+
+ def update(self, other=None):
+ warnings.warn(self.message, RemovedInSphinx20Warning)
+ super(DeprecatedDict, self).update(other)
+
+
+class AutodocRegistry(object):
+ """
+ A registry of Documenters and attrgetters.
Note: When importing an object, all items along the import chain are
accessed using the descendant's *_special_attrgetters*, thus this
dictionary should include all necessary functions for accessing
attributes of the parents.
"""
- # a registry of objtype -> documenter class
- _registry = {} # type: Dict[unicode, Type[Documenter]]
+ # a registry of objtype -> documenter class (Deprecated)
+ _registry = DeprecatedDict(
+ 'AutoDirective._registry has been deprecated. '
+ 'Please use app.add_autodocumenter() instead.'
+ ) # type: Dict[unicode, Type[Documenter]]
# a registry of type -> getattr function
- _special_attrgetters = {} # type: Dict[Type, Callable]
-
- # flags that can be given in autodoc_default_flags
- _default_flags = set([
- 'members', 'undoc-members', 'inherited-members', 'show-inheritance',
- 'private-members', 'special-members',
- ])
-
- # standard docutils directive settings
- has_content = True
- required_arguments = 1
- optional_arguments = 0
- final_argument_whitespace = True
- # allow any options to be passed; the options are parsed further
- # by the selected Documenter
- option_spec = DefDict(identity)
-
- def warn(self, msg):
- # type: (unicode) -> None
- self.warnings.append(self.reporter.warning(msg, line=self.lineno))
+ _special_attrgetters = DeprecatedDict(
+ 'AutoDirective._special_attrgetters has been deprecated. '
+ 'Please use app.add_autodoc_attrgetter() instead.'
+ ) # type: Dict[Type, Callable]
- def run(self):
- # type: () -> List[nodes.Node]
- self.filename_set = set() # type: Set[unicode]
- # a set of dependent filenames
- self.reporter = self.state.document.reporter
- self.env = self.state.document.settings.env
- self.warnings = [] # type: List[unicode]
- self.result = ViewList()
- try:
- source, lineno = self.reporter.get_source_and_line(self.lineno)
- except AttributeError:
- source = lineno = None
- logger.debug('[autodoc] %s:%s: input:\n%s',
- source, lineno, self.block_text)
-
- # find out what documenter to call
- objtype = self.name[4:]
- doc_class = self._registry[objtype]
- # add default flags
- for flag in self._default_flags:
- if flag not in doc_class.option_spec:
- continue
- negated = self.options.pop('no-' + flag, 'not given') is None
- if flag in self.env.config.autodoc_default_flags and \
- not negated:
- self.options[flag] = None
- # process the options with the selected documenter's option_spec
- try:
- self.genopt = Options(assemble_option_dict(
- self.options.items(), doc_class.option_spec))
- except (KeyError, ValueError, TypeError) as err:
- # an option is either unknown or has a wrong type
- msg = self.reporter.error('An option to %s is either unknown or '
- 'has an invalid value: %s' % (self.name, err),
- line=self.lineno)
- return [msg]
- # generate the output
- documenter = doc_class(self, self.arguments[0])
- documenter.generate(more_content=self.content)
- if not self.result:
- return self.warnings
-
- logger.debug('[autodoc] output:\n%s', '\n'.join(self.result))
-
- # record all filenames as dependencies -- this will at least
- # partially make automatic invalidation possible
- for fn in self.filename_set:
- self.state.document.settings.record_dependencies.add(fn)
-
- # use a custom reporter that correctly assigns lines to source
- # filename/description and lineno
- old_reporter = self.state.memo.reporter
- self.state.memo.reporter = AutodocReporter(self.result,
- self.state.memo.reporter)
-
- if documenter.titles_allowed:
- node = nodes.section()
- # necessary so that the child nodes get the right source/line set
- node.document = self.state.document
- nested_parse_with_titles(self.state, self.result, node)
- else:
- node = nodes.paragraph()
- node.document = self.state.document
- self.state.nested_parse(self.result, 0, node)
- self.state.memo.reporter = old_reporter
- return self.warnings + node.children
+AutoDirective = AutodocRegistry # for backward compatibility
def add_documenter(cls):
# type: (Type[Documenter]) -> None
"""Register a new Documenter."""
+ warnings.warn('sphinx.ext.autodoc.add_documenter() has been deprecated. '
+ 'Please use app.add_autodocumenter() instead.',
+ RemovedInSphinx20Warning)
+
if not issubclass(cls, Documenter):
raise ExtensionError('autodoc documenter %r must be a subclass '
'of Documenter' % cls)
@@ -1619,6 +1463,29 @@ def add_documenter(cls):
AutoDirective._registry[cls.objtype] = cls
+def get_documenters(app):
+ # type: (Sphinx) -> Dict[unicode, Type[Documenter]]
+ """Returns registered Documenter classes"""
+ classes = dict(AutoDirective._registry) # registered directly
+ if app:
+ classes.update(app.registry.documenters) # registered by API
+ return classes
+
+
+def autodoc_attrgetter(app, obj, name, *defargs):
+ # type: (Sphinx, Any, unicode, Any) -> Any
+ """Alternative getattr() for types"""
+ candidates = dict(AutoDirective._special_attrgetters)
+ if app:
+ candidates.update(app.registry.autodoc_attrgettrs)
+
+ for typ, func in iteritems(candidates):
+ if isinstance(obj, typ):
+ return func(obj, name, *defargs)
+
+ return safe_getattr(obj, name, *defargs)
+
+
def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
app.add_autodocumenter(ModuleDocumenter)
diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py
new file mode 100644
index 000000000..6de6e5517
--- /dev/null
+++ b/sphinx/ext/autodoc/directive.py
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.ext.autodoc.directive
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from docutils import nodes
+from docutils.parsers.rst import Directive
+from docutils.statemachine import ViewList
+from docutils.utils import assemble_option_dict
+
+from sphinx.ext.autodoc import get_documenters
+from sphinx.util import logging
+from sphinx.util.docutils import switch_source_input
+from sphinx.util.nodes import nested_parse_with_titles
+
+if False:
+ # For type annotation
+ from typing import Any, Dict, List, Set, Type # NOQA
+ from docutils.statemachine import State, StateMachine, StringList # NOQA
+ from docutils.utils import Reporter # NOQA
+ from sphinx.config import Config # NOQA
+ from sphinx.environment import BuildEnvironment # NOQA
+ from sphinx.ext.autodoc import Documenter # NOQA
+
+logger = logging.getLogger(__name__)
+
+
+# common option names for autodoc directives
+AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members',
+ 'show-inheritance', 'private-members', 'special-members',
+ 'ignore-module-all']
+
+
+class DummyOptionSpec(object):
+ """An option_spec allows any options."""
+
+ def __getitem__(self, key):
+ # type: (Any) -> Any
+ return lambda x: x
+
+
+class Options(dict):
+ """A dict/attribute hybrid that returns None on nonexisting keys."""
+ def __getattr__(self, name):
+ # type: (unicode) -> Any
+ try:
+ return self[name.replace('_', '-')]
+ except KeyError:
+ return None
+
+
+class DocumenterBridge(object):
+ """A parameters container for Documenters."""
+
+ def __init__(self, env, reporter, options, lineno):
+ # type: (BuildEnvironment, Reporter, Options, int) -> None
+ self.env = env
+ self.reporter = reporter
+ self.genopt = options
+ self.lineno = lineno
+ self.filename_set = set() # type: Set[unicode]
+ self.result = ViewList()
+
+ def warn(self, msg):
+ # type: (unicode) -> None
+ logger.warning(msg, line=self.lineno)
+
+
+def process_documenter_options(documenter, config, options):
+ # type: (Type[Documenter], Config, Dict) -> Options
+ """Recognize options of Documenter from user input."""
+ for name in AUTODOC_DEFAULT_OPTIONS:
+ if name not in documenter.option_spec:
+ continue
+ else:
+ negated = options.pop('no-' + name, True) is None
+ if name in config.autodoc_default_flags and not negated:
+ options[name] = None
+
+ return Options(assemble_option_dict(options.items(), documenter.option_spec))
+
+
+def parse_generated_content(state, content, documenter):
+ # type: (State, StringList, Documenter) -> List[nodes.Node]
+ """Parse a generated content by Documenter."""
+ with switch_source_input(state, content):
+ if documenter.titles_allowed:
+ node = nodes.section()
+ # necessary so that the child nodes get the right source/line set
+ node.document = state.document
+ nested_parse_with_titles(state, content, node)
+ else:
+ node = nodes.paragraph()
+ node.document = state.document
+ state.nested_parse(content, 0, node)
+
+ return node.children
+
+
+class AutodocDirective(Directive):
+ """A directive class for all autodoc directives. It works as a dispatcher of Documenters.
+
+ It invokes a Documenter on running. After the processing, it parses and returns
+ the generated content by Documenter.
+ """
+ option_spec = DummyOptionSpec()
+ has_content = True
+ required_arguments = 1
+ optional_arguments = 0
+ final_argument_whitespace = True
+
+ def run(self):
+ # type: () -> List[nodes.Node]
+ env = self.state.document.settings.env
+ reporter = self.state.document.reporter
+
+ try:
+ source, lineno = reporter.get_source_and_line(self.lineno)
+ except AttributeError:
+ source, lineno = (None, None)
+ logger.debug('[autodoc] %s:%s: input:\n%s', source, lineno, self.block_text)
+
+ # look up target Documenter
+ objtype = self.name[4:] # strip prefix (auto-).
+ doccls = get_documenters(env.app)[objtype]
+
+ # process the options with the selected documenter's option_spec
+ try:
+ documenter_options = process_documenter_options(doccls, env.config, self.options)
+ except (KeyError, ValueError, TypeError) as exc:
+ # an option is either unknown or has a wrong type
+ logger.error('An option to %s is either unknown or has an invalid value: %s' %
+ (self.name, exc), line=lineno)
+ return []
+
+ # generate the output
+ params = DocumenterBridge(env, reporter, documenter_options, lineno)
+ documenter = doccls(params, self.arguments[0])
+ documenter.generate(more_content=self.content)
+ if not params.result:
+ return []
+
+ logger.debug('[autodoc] output:\n%s', '\n'.join(params.result))
+
+ # record all filenames as dependencies -- this will at least
+ # partially make automatic invalidation possible
+ for fn in params.filename_set:
+ self.state.document.settings.record_dependencies.add(fn)
+
+ result = parse_generated_content(self.state, params.result, documenter)
+ return result
diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py
index e024d38f3..101cb930f 100644
--- a/sphinx/ext/autodoc/importer.py
+++ b/sphinx/ext/autodoc/importer.py
@@ -5,20 +5,25 @@
Importer utilities for autodoc
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys
-import traceback
import warnings
+import traceback
+import contextlib
+from collections import namedtuple
from types import FunctionType, MethodType, ModuleType
+from six import PY2
+
from sphinx.util import logging
+from sphinx.util.inspect import isenumclass, safe_getattr
if False:
# For type annotation
- from typing import Any, List, Set # NOQA
+ from typing import Any, Callable, Dict, Generator, List, Optional, Set # NOQA
logger = logging.getLogger(__name__)
@@ -77,7 +82,6 @@ class _MockModule(ModuleType):
class _MockImporter(object):
-
def __init__(self, names):
# type: (List[str]) -> None
self.base_packages = set() # type: Set[str]
@@ -120,6 +124,16 @@ class _MockImporter(object):
return module
+@contextlib.contextmanager
+def mock(names):
+ # type: (List[str]) -> Generator
+ try:
+ importer = _MockImporter(names)
+ yield
+ finally:
+ importer.disable()
+
+
def import_module(modname, warningiserror=False):
"""
Call __import__(modname), convert exceptions to ImportError
@@ -130,7 +144,90 @@ def import_module(modname, warningiserror=False):
with logging.skip_warningiserror(not warningiserror):
__import__(modname)
return sys.modules[modname]
- except BaseException:
+ except BaseException as exc:
# Importing modules may cause any side effects, including
# SystemExit, so we need to catch all errors.
- raise ImportError(traceback.format_exc())
+ raise ImportError(exc, traceback.format_exc())
+
+
+def import_object(modname, objpath, objtype='', attrgetter=safe_getattr, warningiserror=False):
+ # type: (str, List[unicode], str, Callable[[Any, unicode], Any], bool) -> Any
+ if objpath:
+ logger.debug('[autodoc] from %s import %s', modname, '.'.join(objpath))
+ else:
+ logger.debug('[autodoc] import %s', modname)
+
+ try:
+ module = import_module(modname, warningiserror=warningiserror)
+ logger.debug('[autodoc] => %r', module)
+ obj = module
+ parent = None
+ object_name = None
+ for attrname in objpath:
+ parent = obj
+ logger.debug('[autodoc] getattr(_, %r)', attrname)
+ obj = attrgetter(obj, attrname)
+ logger.debug('[autodoc] => %r', obj)
+ object_name = attrname
+ return [module, parent, object_name, obj]
+ except (AttributeError, ImportError) as exc:
+ if objpath:
+ errmsg = ('autodoc: failed to import %s %r from module %r' %
+ (objtype, '.'.join(objpath), modname))
+ else:
+ errmsg = 'autodoc: failed to import %s %r' % (objtype, modname)
+
+ if isinstance(exc, ImportError):
+ # import_module() raises ImportError having real exception obj and
+ # traceback
+ real_exc, traceback_msg = exc.args
+ if isinstance(real_exc, SystemExit):
+ errmsg += ('; the module executes module level statement '
+ 'and it might call sys.exit().')
+ elif isinstance(real_exc, ImportError):
+ errmsg += '; the following exception was raised:\n%s' % real_exc.args[0]
+ else:
+ errmsg += '; the following exception was raised:\n%s' % traceback_msg
+ else:
+ errmsg += '; the following exception was raised:\n%s' % traceback.format_exc()
+
+ if PY2:
+ errmsg = errmsg.decode('utf-8') # type: ignore
+ logger.debug(errmsg)
+ raise ImportError(errmsg)
+
+
+Attribute = namedtuple('Attribute', ['name', 'directly_defined', 'value'])
+
+
+def get_object_members(subject, objpath, attrgetter, analyzer=None):
+ # type: (Any, List[unicode], Callable, Any) -> Dict[str, Attribute] # NOQA
+ """Get members and attributes of target object."""
+ # the members directly defined in the class
+ obj_dict = attrgetter(subject, '__dict__', {})
+
+ # Py34 doesn't have enum members in __dict__.
+ if sys.version_info[:2] == (3, 4) and isenumclass(subject):
+ obj_dict = dict(obj_dict)
+ for name, value in subject.__members__.items():
+ obj_dict[name] = value
+
+ members = {}
+ for name in dir(subject):
+ try:
+ value = attrgetter(subject, name)
+ directly_defined = name in obj_dict
+ members[name] = Attribute(name, directly_defined, value)
+ except AttributeError:
+ continue
+
+ if analyzer:
+ # append instance attributes (cf. self.attr1) if analyzer knows
+ from sphinx.ext.autodoc import INSTANCEATTR
+
+ namespace = '.'.join(objpath)
+ for (ns, name) in analyzer.find_attr_docs():
+ if namespace == ns and name not in members:
+ members[name] = Attribute(name, True, INSTANCEATTR)
+
+ return members
diff --git a/sphinx/ext/autodoc/inspector.py b/sphinx/ext/autodoc/inspector.py
index f1faf2043..6e07c9547 100644
--- a/sphinx/ext/autodoc/inspector.py
+++ b/sphinx/ext/autodoc/inspector.py
@@ -5,14 +5,16 @@
Inspect utilities for autodoc
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
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:
@@ -65,7 +70,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 +79,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 +89,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))
@@ -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/sphinx/ext/autosectionlabel.py b/sphinx/ext/autosectionlabel.py
index 69b8c6873..fbb7d037f 100644
--- a/sphinx/ext/autosectionlabel.py
+++ b/sphinx/ext/autosectionlabel.py
@@ -5,7 +5,7 @@
Allow reference sections by :ref: role using its title.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py
index 21bfe7b13..7a9e59c73 100644
--- a/sphinx/ext/autosummary/__init__.py
+++ b/sphinx/ext/autosummary/__init__.py
@@ -49,7 +49,7 @@
resolved to a Python object, and otherwise it becomes simple emphasis.
This can be used as the default role to make links 'smart'.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -72,7 +72,8 @@ from sphinx import addnodes
from sphinx.environment.adapters.toctree import TocTree
from sphinx.util import import_object, rst, logging
from sphinx.pycode import ModuleAnalyzer, PycodeError
-from sphinx.ext.autodoc import Options
+from sphinx.ext.autodoc import get_documenters
+from sphinx.ext.autodoc.directive import DocumenterBridge, Options
from sphinx.ext.autodoc.importer import import_module
if False:
@@ -153,13 +154,13 @@ def autosummary_table_visit_html(self, node):
# -- autodoc integration -------------------------------------------------------
-class FakeDirective(object):
- env = {} # type: Dict
- genopt = Options()
+class FakeDirective(DocumenterBridge):
+ def __init__(self):
+ super(FakeDirective, self).__init__({}, None, Options(), 0) # type: ignore
-def get_documenter(obj, parent):
- # type: (Any, Any) -> Type[Documenter]
+def get_documenter(app, obj, parent):
+ # type: (Sphinx, Any, Any) -> Type[Documenter]
"""Get an autodoc.Documenter class suitable for documenting the given
object.
@@ -167,8 +168,7 @@ def get_documenter(obj, parent):
another Python object (e.g. a module or a class) to which *obj*
belongs to.
"""
- from sphinx.ext.autodoc import AutoDirective, DataDocumenter, \
- ModuleDocumenter
+ from sphinx.ext.autodoc import DataDocumenter, ModuleDocumenter
if inspect.ismodule(obj):
# ModuleDocumenter.can_document_member always returns False
@@ -176,7 +176,7 @@ def get_documenter(obj, parent):
# Construct a fake documenter for *parent*
if parent is not None:
- parent_doc_cls = get_documenter(parent, None)
+ parent_doc_cls = get_documenter(app, parent, None)
else:
parent_doc_cls = ModuleDocumenter
@@ -186,7 +186,7 @@ def get_documenter(obj, parent):
parent_doc = parent_doc_cls(FakeDirective(), "")
# Get the corrent documenter class for *obj*
- classes = [cls for cls in AutoDirective._registry.values()
+ classes = [cls for cls in get_documenters(app).values()
if cls.can_document_member(obj, '', False, parent_doc)]
if classes:
classes.sort(key=lambda cls: cls.priority)
@@ -289,7 +289,7 @@ class Autosummary(Directive):
full_name = modname + '::' + full_name[len(modname) + 1:]
# NB. using full_name here is important, since Documenters
# handle module prefixes slightly differently
- documenter = get_documenter(obj, parent)(self, full_name)
+ documenter = get_documenter(self.env.app, obj, parent)(self, full_name)
if not documenter.parse_name():
self.warn('failed to parse name %s' % real_name)
items.append((display_name, '', '', real_name))
@@ -325,7 +325,7 @@ class Autosummary(Directive):
# -- Grab the summary
documenter.add_content(None)
- doc = list(documenter.process_doc([self.result.data]))
+ doc = self.result.data
while doc and not doc[0].strip():
doc.pop(0)
@@ -615,7 +615,8 @@ def process_generate_options(app):
generate_autosummary_docs(genfiles, builder=app.builder,
warn=logger.warning, info=logger.info,
- suffix=suffix, base_path=app.srcdir)
+ suffix=suffix, base_path=app.srcdir,
+ app=app)
def setup(app):
diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py
index f02c50692..aeffcb564 100644
--- a/sphinx/ext/autosummary/generate.py
+++ b/sphinx/ext/autosummary/generate.py
@@ -14,7 +14,7 @@
generate:
sphinx-autogen -o source/generated source/*.rst
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
@@ -33,24 +33,11 @@ from sphinx import __display_version__
from sphinx import package_dir
from sphinx.ext.autosummary import import_by_name, get_documenter
from sphinx.jinja2glue import BuiltinTemplateLoader
+from sphinx.registry import SphinxComponentRegistry
from sphinx.util.osutil import ensuredir
from sphinx.util.inspect import safe_getattr
from sphinx.util.rst import escape as rst_escape
-# Add documenters to AutoDirective registry
-from sphinx.ext.autodoc import add_documenter, \
- ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter, \
- FunctionDocumenter, MethodDocumenter, AttributeDocumenter, \
- InstanceAttributeDocumenter
-add_documenter(ModuleDocumenter)
-add_documenter(ClassDocumenter)
-add_documenter(ExceptionDocumenter)
-add_documenter(DataDocumenter)
-add_documenter(FunctionDocumenter)
-add_documenter(MethodDocumenter)
-add_documenter(AttributeDocumenter)
-add_documenter(InstanceAttributeDocumenter)
-
if False:
# For type annotation
from typing import Any, Callable, Dict, Tuple, List # NOQA
@@ -60,6 +47,30 @@ if False:
from sphinx.environment import BuildEnvironment # NOQA
+class DummyApplication(object):
+ """Dummy Application class for sphinx-autogen command."""
+
+ def __init__(self):
+ # type: () -> None
+ self.registry = SphinxComponentRegistry()
+
+
+def setup_documenters(app):
+ # type: (Any) -> None
+ from sphinx.ext.autodoc import (
+ ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
+ FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
+ InstanceAttributeDocumenter
+ )
+ documenters = [
+ ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
+ FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
+ InstanceAttributeDocumenter
+ ]
+ for documenter in documenters:
+ app.registry.add_documenter(documenter.objtype, documenter)
+
+
def _simple_info(msg):
# type: (unicode) -> None
print(msg)
@@ -81,8 +92,8 @@ def _underline(title, line='='):
def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
warn=_simple_warn, info=_simple_info,
base_path=None, builder=None, template_dir=None,
- imported_members=False):
- # type: (List[unicode], unicode, unicode, Callable, Callable, unicode, Builder, unicode, bool) -> None # NOQA
+ imported_members=False, app=None):
+ # type: (List[unicode], unicode, unicode, Callable, Callable, unicode, Builder, unicode, bool, Any) -> None # NOQA
showed_sources = list(sorted(sources))
if len(showed_sources) > 20:
@@ -148,7 +159,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
new_files.append(fn)
with open(fn, 'w') as f:
- doc = get_documenter(obj, parent)
+ doc = get_documenter(app, obj, parent)
if template_name is not None:
template = template_env.get_template(template_name)
@@ -167,7 +178,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
value = safe_getattr(obj, name)
except AttributeError:
continue
- documenter = get_documenter(value, obj)
+ documenter = get_documenter(app, value, obj)
if documenter.objtype == typ:
if typ == 'method':
items.append(name)
@@ -392,11 +403,14 @@ The format of the autosummary directive is documented in the
def main(argv=sys.argv[1:]):
# type: (List[str]) -> None
+ app = DummyApplication()
+ setup_documenters(app)
args = get_parser().parse_args(argv)
generate_autosummary_docs(args.source_file, args.output_dir,
'.' + args.suffix,
template_dir=args.templates,
- imported_members=args.imported_members)
+ imported_members=args.imported_members,
+ app=app)
if __name__ == '__main__':
diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py
index 02843ac83..476a0ed46 100644
--- a/sphinx/ext/coverage.py
+++ b/sphinx/ext/coverage.py
@@ -6,7 +6,7 @@
Check Python modules and C API for coverage. Mostly written by Josip
Dzolonga for the Google Highly Open Participation contest.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py
index 4110d9c90..948ddfec8 100644
--- a/sphinx/ext/doctest.py
+++ b/sphinx/ext/doctest.py
@@ -6,7 +6,7 @@
Mimic doctest by automatically executing code snippets and checking
their results.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import absolute_import
@@ -20,7 +20,8 @@ from os import path
import doctest
from six import itervalues, StringIO, binary_type, text_type, PY2
-from distutils.version import LooseVersion
+from packaging.specifiers import SpecifierSet, InvalidSpecifier
+from packaging.version import Version
from docutils import nodes
from docutils.parsers.rst import Directive, directives
@@ -57,28 +58,23 @@ else:
return text
-def compare_version(ver1, ver2, operand):
- # type: (unicode, unicode, unicode) -> bool
- """Compare `ver1` to `ver2`, relying on `operand`.
+def is_allowed_version(spec, version):
+ # type: (unicode, unicode) -> bool
+ """Check `spec` satisfies `version` or not.
+
+ This obeys PEP-440 specifiers:
+ https://www.python.org/dev/peps/pep-0440/#version-specifiers
Some examples:
- >>> compare_version('3.3', '3.5', '<=')
+ >>> is_allowed_version('3.3', '<=3.5')
True
- >>> compare_version('3.3', '3.2', '<=')
+ >>> is_allowed_version('3.3', '<=3.2')
False
- >>> compare_version('3.3a0', '3.3', '<=')
+ >>> is_allowed_version('3.3', '>3.2, <4.0')
True
"""
- if operand not in ('<=', '<', '==', '>=', '>'):
- raise ValueError("'%s' is not a valid operand.")
- v1 = LooseVersion(ver1)
- v2 = LooseVersion(ver2)
- return ((operand == '<=' and (v1 <= v2)) or
- (operand == '<' and (v1 < v2)) or
- (operand == '==' and (v1 == v2)) or
- (operand == '>=' and (v1 >= v2)) or
- (operand == '>' and (v1 > v2)))
+ return Version(version) in SpecifierSet(spec)
# set up the necessary directives
@@ -143,16 +139,13 @@ class TestDirective(Directive):
node['options'][flag] = (option[0] == '+')
if self.name == 'doctest' and 'pyversion' in self.options:
try:
- option = self.options['pyversion']
- # :pyversion: >= 3.6 --> operand='>=', option_version='3.6'
- operand, option_version = [item.strip() for item in option.split()]
- running_version = platform.python_version()
- if not compare_version(running_version, option_version, operand):
+ spec = self.options['pyversion']
+ if not is_allowed_version(spec, platform.python_version()):
flag = doctest.OPTIONFLAGS_BY_NAME['SKIP']
node['options'][flag] = True # Skip the test
- except ValueError:
+ except InvalidSpecifier:
self.state.document.reporter.warning(
- _("'%s' is not a valid pyversion option") % option,
+ _("'%s' is not a valid pyversion option") % spec,
line=self.lineno)
return [node]
diff --git a/sphinx/ext/extlinks.py b/sphinx/ext/extlinks.py
index 00180b35c..c247e6722 100644
--- a/sphinx/ext/extlinks.py
+++ b/sphinx/ext/extlinks.py
@@ -20,7 +20,7 @@
You can also give an explicit caption, e.g. :exmpl:`Foo <foo>`.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/githubpages.py b/sphinx/ext/githubpages.py
index 028b65622..7d673a72d 100644
--- a/sphinx/ext/githubpages.py
+++ b/sphinx/ext/githubpages.py
@@ -5,7 +5,7 @@
To publish HTML docs at GitHub Pages, create .nojekyll file.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py
index 2a83474ce..546594843 100644
--- a/sphinx/ext/graphviz.py
+++ b/sphinx/ext/graphviz.py
@@ -6,7 +6,7 @@
Allow graphviz-formatted graphs to be included in Sphinx-generated
documents inline.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/ifconfig.py b/sphinx/ext/ifconfig.py
index c700649dd..16042ac3f 100644
--- a/sphinx/ext/ifconfig.py
+++ b/sphinx/ext/ifconfig.py
@@ -16,7 +16,7 @@
namespace of the project configuration (that is, all variables from
``conf.py`` are available.)
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/imgconverter.py b/sphinx/ext/imgconverter.py
index d2894b2a3..95f579e36 100644
--- a/sphinx/ext/imgconverter.py
+++ b/sphinx/ext/imgconverter.py
@@ -5,7 +5,7 @@
Image converter extension for Sphinx
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import subprocess
diff --git a/sphinx/ext/imgmath.py b/sphinx/ext/imgmath.py
index 8bf4fcad5..b5f67e713 100644
--- a/sphinx/ext/imgmath.py
+++ b/sphinx/ext/imgmath.py
@@ -5,7 +5,7 @@
Render math in HTML via dvipng or dvisvgm.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -30,6 +30,7 @@ from sphinx.util.png import read_png_depth, write_png_depth
from sphinx.util.osutil import ensuredir, ENOENT, cd
from sphinx.util.pycompat import sys_encoding
from sphinx.ext.mathbase import setup_math as mathbase_setup, wrap_displaymath
+from sphinx.ext.mathbase import get_node_equation_number
if False:
# For type annotation
@@ -333,7 +334,8 @@ def html_visit_displaymath(self, node):
self.body.append(self.starttag(node, 'div', CLASS='math'))
self.body.append('<p>')
if node['number']:
- self.body.append('<span class="eqno">(%s)' % node['number'])
+ number = get_node_equation_number(self, node)
+ self.body.append('<span class="eqno">(%s)' % number)
self.add_permalink_ref(node, _('Permalink to this equation'))
self.body.append('</span>')
if fname is None:
diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py
index a6e6e7aba..d9affb804 100644
--- a/sphinx/ext/inheritance_diagram.py
+++ b/sphinx/ext/inheritance_diagram.py
@@ -32,7 +32,7 @@ r"""
The graph is inserted as a PNG+image map into HTML and a PDF in
LaTeX.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -130,8 +130,8 @@ class InheritanceGraph(object):
graphviz dot graph from them.
"""
def __init__(self, class_names, currmodule, show_builtins=False,
- private_bases=False, parts=0, aliases=None):
- # type: (unicode, str, bool, bool, int, Optional[Dict[unicode, unicode]]) -> None
+ private_bases=False, parts=0, aliases=None, top_classes=[]):
+ # type: (unicode, str, bool, bool, int, Optional[Dict[unicode, unicode]], List[Any]) -> None # NOQA
"""*class_names* is a list of child classes to show bases from.
If *show_builtins* is True, then Python builtins will be shown
@@ -140,7 +140,7 @@ class InheritanceGraph(object):
self.class_names = class_names
classes = self._import_classes(class_names, currmodule)
self.class_info = self._class_info(classes, show_builtins,
- private_bases, parts, aliases)
+ private_bases, parts, aliases, top_classes)
if not self.class_info:
raise InheritanceException('No classes found for '
'inheritance diagram')
@@ -153,13 +153,16 @@ class InheritanceGraph(object):
classes.extend(import_classes(name, currmodule))
return classes
- def _class_info(self, classes, show_builtins, private_bases, parts, aliases):
- # type: (List[Any], bool, bool, int, Optional[Dict[unicode, unicode]]) -> List[Tuple[unicode, unicode, List[unicode], unicode]] # NOQA
+ def _class_info(self, classes, show_builtins, private_bases, parts, aliases, top_classes):
+ # type: (List[Any], bool, bool, int, Optional[Dict[unicode, unicode]], List[Any]) -> List[Tuple[unicode, unicode, List[unicode], unicode]] # NOQA
"""Return name and bases for all classes that are ancestors of
*classes*.
*parts* gives the number of dotted name parts that is removed from the
displayed node names.
+
+ *top_classes* gives the name(s) of the top most ancestor class to traverse
+ to. Multiple names can be specified separated by comma.
"""
all_classes = {}
py_builtins = vars(builtins).values()
@@ -189,6 +192,10 @@ class InheritanceGraph(object):
baselist = [] # type: List[unicode]
all_classes[cls] = (nodename, fullname, baselist, tooltip)
+
+ if fullname in top_classes:
+ return
+
for base in cls.__bases__:
if not show_builtins and base in py_builtins:
continue
@@ -322,6 +329,7 @@ class InheritanceDiagram(Directive):
'parts': directives.nonnegative_int,
'private-bases': directives.flag,
'caption': directives.unchanged,
+ 'top-classes': directives.unchanged_required,
}
def run(self):
@@ -334,6 +342,11 @@ class InheritanceDiagram(Directive):
# Store the original content for use as a hash
node['parts'] = self.options.get('parts', 0)
node['content'] = ', '.join(class_names)
+ node['top-classes'] = []
+ for cls in self.options.get('top-classes', '').split(','):
+ cls = cls.strip()
+ if cls:
+ node['top-classes'].append(cls)
# Create a graph starting with the list of classes
try:
@@ -341,7 +354,8 @@ class InheritanceDiagram(Directive):
class_names, env.ref_context.get('py:module'),
parts=node['parts'],
private_bases='private-bases' in self.options,
- aliases=env.config.inheritance_alias)
+ aliases=env.config.inheritance_alias,
+ top_classes=node['top-classes'])
except InheritanceException as err:
return [node.document.reporter.warning(err.args[0],
line=self.lineno)]
diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py
index 1ee58353c..52683d309 100644
--- a/sphinx/ext/intersphinx.py
+++ b/sphinx/ext/intersphinx.py
@@ -20,7 +20,7 @@
also be specified individually, e.g. if the docs should be buildable
without Internet access.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -304,6 +304,7 @@ def missing_reference(app, env, node, contnode):
in_set = setname
to_try.append((inventories.named_inventory[setname], newtarget))
if domain:
+ node['reftarget'] = newtarget
full_qualified_name = env.get_domain(domain).get_full_qualified_name(node)
if full_qualified_name:
to_try.append((inventories.named_inventory[setname], full_qualified_name))
diff --git a/sphinx/ext/jsmath.py b/sphinx/ext/jsmath.py
index dc57c13c6..a74f0641a 100644
--- a/sphinx/ext/jsmath.py
+++ b/sphinx/ext/jsmath.py
@@ -6,7 +6,7 @@
Set up everything for use of JSMath to display math in HTML
via JavaScript.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -16,6 +16,7 @@ import sphinx
from sphinx.locale import _
from sphinx.application import ExtensionError
from sphinx.ext.mathbase import setup_math as mathbase_setup
+from sphinx.ext.mathbase import get_node_equation_number
def html_visit_math(self, node):
@@ -35,7 +36,8 @@ def html_visit_displaymath(self, node):
if i == 0:
# necessary to e.g. set the id property correctly
if node['number']:
- self.body.append('<span class="eqno">(%s)' % node['number'])
+ number = get_node_equation_number(self, node)
+ self.body.append('<span class="eqno">(%s)' % number)
self.add_permalink_ref(node, _('Permalink to this equation'))
self.body.append('</span>')
self.body.append(self.starttag(node, 'div', CLASS='math'))
diff --git a/sphinx/ext/linkcode.py b/sphinx/ext/linkcode.py
index e74ee8529..a42dab528 100644
--- a/sphinx/ext/linkcode.py
+++ b/sphinx/ext/linkcode.py
@@ -5,7 +5,7 @@
Add external links to module code in Python object descriptions.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/mathbase.py b/sphinx/ext/mathbase.py
index f83ca5da8..f2e15b485 100644
--- a/sphinx/ext/mathbase.py
+++ b/sphinx/ext/mathbase.py
@@ -5,7 +5,7 @@
Set up math support in source files and LaTeX/text output.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -84,6 +84,13 @@ class MathDomain(Domain):
newnode['target'] = target
return newnode
else:
+ if env.config.math_numfig and env.config.numfig:
+ if docname in env.toc_fignumbers:
+ id = 'equation-' + target
+ number = env.toc_fignumbers[docname]['displaymath'].get(id, ())
+ number = '.'.join(map(str, number))
+ else:
+ number = ''
try:
eqref_format = env.config.math_eqref_format or "({number})"
title = nodes.Text(eqref_format.format(number=number))
@@ -126,6 +133,23 @@ class MathDomain(Domain):
return len(targets) + 1
+def get_node_equation_number(writer, node):
+ if writer.builder.config.math_numfig and writer.builder.config.numfig:
+ figtype = 'displaymath'
+ if writer.builder.name == 'singlehtml':
+ key = u"%s/%s" % (writer.docnames[-1], figtype)
+ else:
+ key = figtype
+
+ id = node['ids'][0]
+ number = writer.builder.fignumbers.get(key, {}).get(id, ())
+ number = '.'.join(map(str, number))
+ else:
+ number = node['number']
+
+ return number
+
+
def wrap_displaymath(math, label, numbering):
# type: (unicode, unicode, bool) -> unicode
def is_equation(part):
@@ -341,6 +365,7 @@ def setup_math(app, htmlinlinevisitors, htmldisplayvisitors):
# type: (Sphinx, Tuple[Callable, Any], Tuple[Callable, Any]) -> None
app.add_config_value('math_number_all', False, 'env')
app.add_config_value('math_eqref_format', None, 'env', string_classes)
+ app.add_config_value('math_numfig', True, 'env')
app.add_domain(MathDomain)
app.add_node(math, override=True,
latex=(latex_visit_math, None),
@@ -348,12 +373,12 @@ def setup_math(app, htmlinlinevisitors, htmldisplayvisitors):
man=(man_visit_math, None),
texinfo=(texinfo_visit_math, None),
html=htmlinlinevisitors)
- app.add_node(displaymath,
- latex=(latex_visit_displaymath, None),
- text=(text_visit_displaymath, None),
- man=(man_visit_displaymath, man_depart_displaymath),
- texinfo=(texinfo_visit_displaymath, texinfo_depart_displaymath),
- html=htmldisplayvisitors)
+ app.add_enumerable_node(displaymath, 'displaymath',
+ latex=(latex_visit_displaymath, None),
+ text=(text_visit_displaymath, None),
+ man=(man_visit_displaymath, man_depart_displaymath),
+ texinfo=(texinfo_visit_displaymath, texinfo_depart_displaymath),
+ html=htmldisplayvisitors)
app.add_node(eqref, latex=(latex_visit_eqref, None))
app.add_role('math', math_role)
app.add_role('eq', EqXRefRole(warn_dangling=True))
diff --git a/sphinx/ext/mathjax.py b/sphinx/ext/mathjax.py
index f25f91e74..8698e2801 100644
--- a/sphinx/ext/mathjax.py
+++ b/sphinx/ext/mathjax.py
@@ -7,7 +7,7 @@
Sphinx's HTML writer -- requires the MathJax JavaScript library on your
webserver/computer.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -17,6 +17,7 @@ import sphinx
from sphinx.locale import _
from sphinx.errors import ExtensionError
from sphinx.ext.mathbase import setup_math as mathbase_setup
+from sphinx.ext.mathbase import get_node_equation_number
def html_visit_math(self, node):
@@ -36,7 +37,8 @@ def html_visit_displaymath(self, node):
# necessary to e.g. set the id property correctly
if node['number']:
- self.body.append('<span class="eqno">(%s)' % node['number'])
+ number = get_node_equation_number(self, node)
+ self.body.append('<span class="eqno">(%s)' % number)
self.add_permalink_ref(node, _('Permalink to this equation'))
self.body.append('</span>')
self.body.append(self.builder.config.mathjax_display[0])
diff --git a/sphinx/ext/napoleon/__init__.py b/sphinx/ext/napoleon/__init__.py
index f319a18c4..b65f7f2a1 100644
--- a/sphinx/ext/napoleon/__init__.py
+++ b/sphinx/ext/napoleon/__init__.py
@@ -5,7 +5,7 @@
Support for NumPy and Google style docstrings.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -27,13 +27,12 @@ class Config(object):
Listed below are all the settings used by napoleon and their default
values. These settings can be changed in the Sphinx `conf.py` file. Make
- sure that both "sphinx.ext.autodoc" and "sphinx.ext.napoleon" are
- enabled in `conf.py`::
+ sure that "sphinx.ext.napoleon" is enabled in `conf.py`::
# conf.py
# Add any Sphinx extension module names here, as strings
- extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon']
+ extensions = ['sphinx.ext.napoleon']
# Napoleon settings
napoleon_google_docstring = True
@@ -294,6 +293,7 @@ def setup(app):
_patch_python_domain()
+ app.setup_extension('sphinx.ext.autodoc')
app.connect('autodoc-process-docstring', _process_docstring)
app.connect('autodoc-skip-member', _skip_member)
diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py
index c77598ef1..b349c761f 100644
--- a/sphinx/ext/napoleon/docstring.py
+++ b/sphinx/ext/napoleon/docstring.py
@@ -7,7 +7,7 @@
Classes for docstring parsing and formatting.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -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/ext/napoleon/iterators.py b/sphinx/ext/napoleon/iterators.py
index b03bcf047..b4bba8863 100644
--- a/sphinx/ext/napoleon/iterators.py
+++ b/sphinx/ext/napoleon/iterators.py
@@ -7,7 +7,7 @@
A collection of helpful iterators.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/pngmath.py b/sphinx/ext/pngmath.py
index 85010b799..717d51756 100644
--- a/sphinx/ext/pngmath.py
+++ b/sphinx/ext/pngmath.py
@@ -6,7 +6,7 @@
Render math in HTML via dvipng. This extension has been deprecated; please
use sphinx.ext.imgmath instead.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -30,6 +30,7 @@ from sphinx.util.png import read_png_depth, write_png_depth
from sphinx.util.osutil import ensuredir, ENOENT, cd
from sphinx.util.pycompat import sys_encoding
from sphinx.ext.mathbase import setup_math as mathbase_setup, wrap_displaymath
+from sphinx.ext.mathbase import get_node_equation_number
if False:
# For type annotation
@@ -242,7 +243,8 @@ def html_visit_displaymath(self, node):
self.body.append(self.starttag(node, 'div', CLASS='math'))
self.body.append('<p>')
if node['number']:
- self.body.append('<span class="eqno">(%s)</span>' % node['number'])
+ number = get_node_equation_number(self, node)
+ self.body.append('<span class="eqno">(%s)</span>' % number)
if fname is None:
# something failed -- use text-only as a bad substitute
self.body.append('<span class="math">%s</span></p>\n</div>' %
diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py
index a58422793..e60620b5b 100644
--- a/sphinx/ext/todo.py
+++ b/sphinx/ext/todo.py
@@ -8,7 +8,7 @@
all todos of your project and lists them along with a backlink to the
original location.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -178,7 +178,8 @@ def process_todo_nodes(app, doctree, fromdocname):
todo_entry = todo_info['todo']
# Remove targetref from the (copied) node to avoid emitting a
# duplicate label of the original entry when we walk this node.
- del todo_entry['targetref']
+ if 'targetref' in todo_entry:
+ del todo_entry['targetref']
# (Recursively) resolve references in the todo content
env.resolve_references(todo_entry, todo_info['docname'],
diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py
index ce67081d3..757d7adc0 100644
--- a/sphinx/ext/viewcode.py
+++ b/sphinx/ext/viewcode.py
@@ -5,7 +5,7 @@
Add links to module code in Python object descriptions.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -71,12 +71,12 @@ def doctree_read(app, doctree):
code = analyzer.code.decode(analyzer.encoding)
else:
code = analyzer.code
- if entry is None or entry[0] != code:
+ if entry is False:
+ return
+ elif entry is None or entry[0] != code:
analyzer.find_tags()
entry = code, analyzer.tags, {}, refname
env._viewcode_modules[modname] = entry # type: ignore
- elif entry is False:
- return
_, tags, used, _ = entry
if fullname in tags:
used[fullname] = docname
diff --git a/sphinx/extension.py b/sphinx/extension.py
index 0520bf564..98d35b5af 100644
--- a/sphinx/extension.py
+++ b/sphinx/extension.py
@@ -5,7 +5,7 @@
Utilities for Sphinx extensions.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -38,7 +38,7 @@ class Extension(object):
# The extension supports parallel write or not. The default value
# is ``True``. Sphinx writes parallelly documents even if
# the extension does not tell its status.
- self.parallel_write_safe = kwargs.pop('parallel_read_safe', True)
+ self.parallel_write_safe = kwargs.pop('parallel_write_safe', True)
def verify_required_extensions(app, requirements):
diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py
index eb309d82a..eef24ee95 100644
--- a/sphinx/highlighting.py
+++ b/sphinx/highlighting.py
@@ -5,7 +5,7 @@
Highlight code blocks using Pygments.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/io.py b/sphinx/io.py
index 84e1d7bb3..3c32c167c 100644
--- a/sphinx/io.py
+++ b/sphinx/io.py
@@ -5,30 +5,31 @@
Input/Output files
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
+import re
import codecs
from docutils.io import FileInput, NullOutput
from docutils.core import Publisher
from docutils.readers import standalone
+from docutils.statemachine import StringList, string2lines
from docutils.writers import UnfilteredWriter
-from six import string_types, text_type, iteritems
+from six import text_type
from typing import Any, Union # NOQA
from sphinx.transforms import (
ApplySourceWorkaround, ExtraTranslatableNodes, CitationReferences,
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, SortIds,
AutoNumbering, AutoIndexUpgrader, FilterSystemMessages,
- UnreferencedFootnotesDetector
+ UnreferencedFootnotesDetector, SphinxSmartQuotes, ManpageLink
)
from sphinx.transforms.compact_bullet_list import RefOnlyBulletListTransform
from sphinx.transforms.i18n import (
PreserveTranslatableMessages, Locale, RemoveTranslatableInline,
)
from sphinx.util import logging
-from sphinx.util import import_object, split_docinfo
from sphinx.util.docutils import LoggingReporter
if False:
@@ -42,44 +43,18 @@ if False:
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
+docinfo_re = re.compile(':\\w+:.*?')
+
logger = logging.getLogger(__name__)
class SphinxBaseReader(standalone.Reader):
"""
- Add our source parsers
- """
- def __init__(self, app, parsers={}, *args, **kwargs):
- # type: (Sphinx, Dict[unicode, Parser], Any, Any) -> None
- standalone.Reader.__init__(self, *args, **kwargs)
- self.parser_map = {} # type: Dict[unicode, Parser]
- for suffix, parser_class in parsers.items():
- if isinstance(parser_class, string_types):
- parser_class = import_object(parser_class, 'source parser') # type: ignore
- parser = parser_class()
- if hasattr(parser, 'set_application'):
- parser.set_application(app)
- self.parser_map[suffix] = parser
-
- def read(self, source, parser, settings):
- # type: (Input, Parser, Dict) -> nodes.document
- self.source = source
-
- for suffix in self.parser_map:
- if source.source_path.endswith(suffix):
- self.parser = self.parser_map[suffix]
- break
- else:
- # use special parser for unknown file-extension '*' (if exists)
- self.parser = self.parser_map.get('*')
+ A base class of readers for Sphinx.
- if not self.parser:
- self.parser = parser
- self.settings = settings
- self.input = self.source.read()
- self.parse()
- return self.document
+ This replaces reporter by Sphinx's on generating document.
+ """
def get_transforms(self):
# type: () -> List[Transform]
@@ -87,50 +62,66 @@ class SphinxBaseReader(standalone.Reader):
def new_document(self):
# type: () -> nodes.document
+ """Creates a new document object which having a special reporter object good
+ for logging.
+ """
document = standalone.Reader.new_document(self)
reporter = document.reporter
- document.reporter = LoggingReporter(reporter.source, reporter.report_level,
- reporter.halt_level, reporter.debug_flag,
- reporter.error_handler)
+ document.reporter = LoggingReporter.from_reporter(reporter)
+ document.reporter.set_source(self.source)
return document
class SphinxStandaloneReader(SphinxBaseReader):
"""
- Add our own transforms.
+ A basic document reader for Sphinx.
"""
transforms = [ApplySourceWorkaround, ExtraTranslatableNodes, PreserveTranslatableMessages,
Locale, CitationReferences, DefaultSubstitutions, MoveModuleTargets,
HandleCodeBlocks, AutoNumbering, AutoIndexUpgrader, SortIds,
RemoveTranslatableInline, PreserveTranslatableMessages, FilterSystemMessages,
- RefOnlyBulletListTransform, UnreferencedFootnotesDetector]
+ RefOnlyBulletListTransform, UnreferencedFootnotesDetector, ManpageLink
+ ] # type: List[Transform]
+
+ def __init__(self, app, *args, **kwargs):
+ # type: (Sphinx, Any, Any) -> None
+ self.transforms = self.transforms + app.registry.get_transforms()
+ self.smart_quotes = app.env.settings['smart_quotes']
+ SphinxBaseReader.__init__(self, *args, **kwargs) # type: ignore
+
+ def get_transforms(self):
+ transforms = SphinxBaseReader.get_transforms(self)
+ if self.smart_quotes:
+ transforms.append(SphinxSmartQuotes)
+ return transforms
class SphinxI18nReader(SphinxBaseReader):
"""
- Replacer for document.reporter.get_source_and_line method.
+ A document reader for i18n.
- reST text lines for translation do not have the original source line number.
- This class provides the correct line numbers when reporting.
+ This returns the source line number of original text as current source line number
+ to let users know where the error happened.
+ Because the translated texts are partial and they don't have correct line numbers.
"""
+ lineno = None # type: int
transforms = [ApplySourceWorkaround, ExtraTranslatableNodes, CitationReferences,
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks,
AutoNumbering, SortIds, RemoveTranslatableInline,
FilterSystemMessages, RefOnlyBulletListTransform,
- UnreferencedFootnotesDetector]
-
- def __init__(self, *args, **kwargs):
- # type: (Any, Any) -> None
- SphinxBaseReader.__init__(self, *args, **kwargs)
- self.lineno = None # type: int
+ UnreferencedFootnotesDetector, ManpageLink]
def set_lineno_for_reporter(self, lineno):
# type: (int) -> None
+ """Stores the source line number of original text."""
self.lineno = lineno
def new_document(self):
# type: () -> nodes.document
+ """Creates a new document object which having a special reporter object for
+ translation.
+ """
document = SphinxBaseReader.new_document(self)
reporter = document.reporter
@@ -143,6 +134,8 @@ class SphinxI18nReader(SphinxBaseReader):
class SphinxDummyWriter(UnfilteredWriter):
+ """Dummy writer module used for generating doctree."""
+
supported = ('html',) # needed to keep "meta" nodes
def translate(self):
@@ -155,7 +148,13 @@ def SphinxDummySourceClass(source, *args, **kwargs):
return source
-class SphinxFileInput(FileInput):
+class SphinxBaseFileInput(FileInput):
+ """A base class of SphinxFileInput.
+
+ It supports to replace unknown Unicode characters to '?'. And it also emits
+ Sphinx events ``source-read`` on reading.
+ """
+
def __init__(self, app, env, *args, **kwds):
# type: (Sphinx, BuildEnvironment, Any, Any) -> None
self.app = app
@@ -175,27 +174,16 @@ class SphinxFileInput(FileInput):
def read(self):
# type: () -> unicode
- def get_parser_type(source_path):
- # type: (unicode) -> Tuple[unicode]
- for suffix, parser_class in iteritems(self.app.registry.get_source_parsers()):
- if source_path.endswith(suffix):
- if isinstance(parser_class, string_types):
- parser_class = import_object(parser_class, 'source parser') # type: ignore # NOQA
- return parser_class.supported
- return ('restructuredtext',)
+ """Reads the contents from file.
+ After reading, it emits Sphinx event ``source-read``.
+ """
data = FileInput.read(self)
- if self.app:
- arg = [data]
- self.app.emit('source-read', self.env.docname, arg)
- data = arg[0]
- docinfo, data = split_docinfo(data)
- if 'restructuredtext' in get_parser_type(self.source_path):
- if self.env.config.rst_epilog:
- data = data + '\n' + self.env.config.rst_epilog + '\n'
- if self.env.config.rst_prolog:
- data = self.env.config.rst_prolog + '\n' + data
- return docinfo + data
+
+ # emit source-read event
+ arg = [data]
+ self.app.emit('source-read', self.env.docname, arg)
+ return arg[0]
def warn_and_replace(self, error):
# type: (Any) -> Tuple
@@ -213,14 +201,84 @@ class SphinxFileInput(FileInput):
return (u'?', error.end)
+class SphinxFileInput(SphinxBaseFileInput):
+ """A basic FileInput for Sphinx."""
+ pass
+
+
+class SphinxRSTFileInput(SphinxBaseFileInput):
+ """A reST FileInput for Sphinx.
+
+ This FileInput automatically prepends and appends text by :confval:`rst_prolog` and
+ :confval:`rst_epilog`.
+
+ .. important::
+
+ This FileInput uses an instance of ``StringList`` as a return value of ``read()``
+ method to indicate original source filename and line numbers after prepending and
+ appending.
+ For that reason, ``sphinx.parsers.RSTParser`` should be used with this to parse
+ a content correctly.
+ """
+
+ def prepend_prolog(self, text, prolog):
+ # type: (StringList, unicode) -> None
+ docinfo = self.count_docinfo_lines(text)
+ if docinfo:
+ # insert a blank line after docinfo
+ text.insert(docinfo, '', '<generated>', 0)
+ docinfo += 1
+
+ # insert prolog (after docinfo if exists)
+ for lineno, line in enumerate(prolog.splitlines()):
+ text.insert(docinfo + lineno, line, '<rst_prolog>', lineno)
+
+ text.insert(docinfo + lineno + 1, '', '<generated>', 0)
+
+ def append_epilog(self, text, epilog):
+ # type: (StringList, unicode) -> None
+ # append a blank line and rst_epilog
+ text.append('', '<generated>', 0)
+ for lineno, line in enumerate(epilog.splitlines()):
+ text.append(line, '<rst_epilog>', lineno)
+
+ def read(self):
+ # type: () -> StringList
+ inputstring = SphinxBaseFileInput.read(self)
+ lines = string2lines(inputstring, convert_whitespace=True)
+ content = StringList()
+ for lineno, line in enumerate(lines):
+ content.append(line, self.source_path, lineno)
+
+ if self.env.config.rst_prolog:
+ self.prepend_prolog(content, self.env.config.rst_prolog)
+ if self.env.config.rst_epilog:
+ self.append_epilog(content, self.env.config.rst_epilog)
+
+ return content
+
+ def count_docinfo_lines(self, content):
+ # type: (StringList) -> int
+ if len(content) == 0:
+ return 0
+ else:
+ for lineno, line in enumerate(content.data):
+ if not docinfo_re.match(line):
+ break
+ return lineno
+
+
def read_doc(app, env, filename):
# type: (Sphinx, BuildEnvironment, unicode) -> nodes.document
"""Parse a document and convert to doctree."""
- reader = SphinxStandaloneReader(app, parsers=app.registry.get_source_parsers())
- source = SphinxFileInput(app, env, source=None, source_path=filename,
- encoding=env.config.source_encoding)
+ input_class = app.registry.get_source_input(filename)
+ reader = SphinxStandaloneReader(app)
+ source = input_class(app, env, source=None, source_path=filename,
+ encoding=env.config.source_encoding)
+ parser = app.registry.create_source_parser(app, filename)
pub = Publisher(reader=reader,
+ parser=parser,
writer=SphinxDummyWriter(),
source_class=SphinxDummySourceClass,
destination=NullOutput())
@@ -229,3 +287,14 @@ def read_doc(app, env, filename):
pub.set_source(source, filename)
pub.publish()
return pub.document
+
+
+def setup(app):
+ app.registry.add_source_input('*', SphinxFileInput)
+ app.registry.add_source_input('restructuredtext', SphinxRSTFileInput)
+
+ return {
+ 'version': 'builtin',
+ 'parallel_read_safe': True,
+ 'parallel_write_safe': True,
+ }
diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py
index 41d48ad34..8839e48fa 100644
--- a/sphinx/jinja2glue.py
+++ b/sphinx/jinja2glue.py
@@ -5,7 +5,7 @@
Glue code for the jinja2 templating engine.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/make_mode.py b/sphinx/make_mode.py
index 83c8988b0..0bc1a797d 100644
--- a/sphinx/make_mode.py
+++ b/sphinx/make_mode.py
@@ -11,7 +11,7 @@
This is in its own module so that importing it is fast. It should not
import the main Sphinx modules (like sphinx.applications, sphinx.builders).
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
diff --git a/sphinx/parsers.py b/sphinx/parsers.py
index 33556e487..34822898f 100644
--- a/sphinx/parsers.py
+++ b/sphinx/parsers.py
@@ -5,19 +5,20 @@
A Base class for additional parsers.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import docutils.parsers
import docutils.parsers.rst
+from docutils.parsers.rst import states
+from docutils.statemachine import StringList
from docutils.transforms.universal import SmartQuotes
-from sphinx.transforms import SphinxSmartQuotes
-
if False:
# For type annotation
from typing import Any, Dict, List, Type # NOQA
+ from docutils import nodes # NOQA
from docutils.transforms import Transform # NOQA
from sphinx.application import Sphinx # NOQA
@@ -56,16 +57,37 @@ class Parser(docutils.parsers.Parser):
class RSTParser(docutils.parsers.rst.Parser):
- """A reST parser customized for Sphinx."""
+ """A reST parser for Sphinx."""
def get_transforms(self):
# type: () -> List[Type[Transform]]
- """Sphinx's reST parser replaces a transform class for smart-quotes by own's"""
+ """Sphinx's reST parser replaces a transform class for smart-quotes by own's
+
+ refs: sphinx.io.SphinxStandaloneReader"""
transforms = docutils.parsers.rst.Parser.get_transforms(self)
transforms.remove(SmartQuotes)
- transforms.append(SphinxSmartQuotes)
return transforms
+ def parse(self, inputstring, document):
+ # type: (Any, nodes.document) -> None
+ """Parse text and generate a document tree.
+
+ This accepts StringList as an inputstring parameter.
+ It enables to handle mixed contents (cf. :confval:`rst_prolog`) correctly.
+ """
+ if isinstance(inputstring, StringList):
+ self.setup_parse(inputstring, document)
+ self.statemachine = states.RSTStateMachine(
+ state_classes=self.state_classes,
+ initial_state=self.initial_state,
+ debug=document.reporter.debug_flag)
+ # Give inputstring directly to statemachine.
+ self.statemachine.run(inputstring, document, inliner=self.inliner)
+ self.finish_parse()
+ else:
+ # otherwise, inputstring might be a string. It will be handled by superclass.
+ docutils.parsers.rst.Parser.parse(self, inputstring, document)
+
def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py
index 66544f073..de951a19f 100644
--- a/sphinx/pycode/__init__.py
+++ b/sphinx/pycode/__init__.py
@@ -5,7 +5,7 @@
Utilities parsing and analyzing Python code.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
@@ -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
diff --git a/sphinx/pycode/parser.py b/sphinx/pycode/parser.py
index 7460dcfce..9aed7f7f4 100644
--- a/sphinx/pycode/parser.py
+++ b/sphinx/pycode/parser.py
@@ -5,7 +5,7 @@
Utilities parsing and analyzing Python code.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
diff --git a/sphinx/pygments_styles.py b/sphinx/pygments_styles.py
index a70005d7c..d29d825d5 100644
--- a/sphinx/pygments_styles.py
+++ b/sphinx/pygments_styles.py
@@ -5,7 +5,7 @@
Sphinx theme specific highlighting styles.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index 75c244c8e..5e403b1d8 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -5,7 +5,7 @@
This file has moved to :py:mod:`sphinx.cmd.quickstart`.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/registry.py b/sphinx/registry.py
index 0861575db..e48c12f96 100644
--- a/sphinx/registry.py
+++ b/sphinx/registry.py
@@ -13,26 +13,33 @@ from __future__ import print_function
import traceback
from pkg_resources import iter_entry_points
-from six import itervalues
+from six import iteritems, itervalues, string_types
from sphinx.errors import ExtensionError, SphinxError, VersionRequirementError
from sphinx.extension import Extension
from sphinx.domains import ObjType
from sphinx.domains.std import GenericObject, Target
from sphinx.locale import __
+from sphinx.parsers import Parser as SphinxParser
from sphinx.roles import XRefRole
from sphinx.util import logging
+from sphinx.util import import_object
+from sphinx.util.console import bold # type: ignore
from sphinx.util.docutils import directive_helper
if False:
# For type annotation
- from typing import Any, Callable, Dict, Iterator, List, Type # NOQA
+ from typing import Any, Callable, Dict, Iterator, List, Type, Union # NOQA
from docutils import nodes # NOQA
+ from docutils.io import Input # NOQA
from docutils.parsers import Parser # NOQA
+ from docutils.transform import Transform # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.domains import Domain, Index # NOQA
from sphinx.environment import BuildEnvironment # NOQA
+ from sphinx.ext.autodoc import Documenter # NOQA
+ from sphinx.util.typing import RoleFunction # NOQA
logger = logging.getLogger(__name__)
@@ -45,13 +52,23 @@ EXTENSION_BLACKLIST = {
class SphinxComponentRegistry(object):
def __init__(self):
- self.builders = {} # type: Dict[unicode, Type[Builder]]
- self.domains = {} # type: Dict[unicode, Type[Domain]]
- self.source_parsers = {} # type: Dict[unicode, Parser]
- self.translators = {} # type: Dict[unicode, nodes.NodeVisitor]
+ self.autodoc_attrgettrs = {} # type: Dict[Type, Callable[[Any, unicode, Any], Any]]
+ self.builders = {} # type: Dict[unicode, Type[Builder]]
+ self.documenters = {} # type: Dict[unicode, Type[Documenter]]
+ self.domains = {} # type: Dict[unicode, Type[Domain]]
+ self.domain_directives = {} # type: Dict[unicode, Dict[unicode, Any]]
+ self.domain_indices = {} # type: Dict[unicode, List[Type[Index]]]
+ self.domain_object_types = {} # type: Dict[unicode, Dict[unicode, ObjType]]
+ self.domain_roles = {} # type: Dict[unicode, Dict[unicode, Union[RoleFunction, XRefRole]]] # NOQA
+ self.post_transforms = [] # type: List[Type[Transform]]
+ self.source_parsers = {} # type: Dict[unicode, Parser]
+ self.source_inputs = {} # type: Dict[unicode, Input]
+ self.translators = {} # type: Dict[unicode, nodes.NodeVisitor]
+ self.transforms = [] # type: List[Type[Transform]]
def add_builder(self, builder):
# type: (Type[Builder]) -> None
+ logger.debug('[app] adding builder: %r', builder)
if not hasattr(builder, 'name'):
raise ExtensionError(__('Builder class %s has no "name" attribute') % builder)
if builder.name in self.builders:
@@ -83,6 +100,7 @@ class SphinxComponentRegistry(object):
def add_domain(self, domain):
# type: (Type[Domain]) -> None
+ logger.debug('[app] adding domain: %r', domain)
if domain.name in self.domains:
raise ExtensionError(__('domain %s already registered') % domain.name)
self.domains[domain.name] = domain
@@ -94,10 +112,20 @@ class SphinxComponentRegistry(object):
def create_domains(self, env):
# type: (BuildEnvironment) -> Iterator[Domain]
for DomainClass in itervalues(self.domains):
- yield DomainClass(env)
+ domain = DomainClass(env)
+
+ # transplant components added by extensions
+ domain.directives.update(self.domain_directives.get(domain.name, {}))
+ domain.roles.update(self.domain_roles.get(domain.name, {}))
+ domain.indices.extend(self.domain_indices.get(domain.name, []))
+ for name, objtype in iteritems(self.domain_object_types.get(domain.name, {})):
+ domain.add_object_type(name, objtype)
+
+ yield domain
def override_domain(self, domain):
# type: (Type[Domain]) -> None
+ logger.debug('[app] overriding domain: %r', domain)
if domain.name not in self.domains:
raise ExtensionError(__('domain %s not yet registered') % domain.name)
if not issubclass(domain, self.domains[domain.name]):
@@ -108,27 +136,37 @@ class SphinxComponentRegistry(object):
def add_directive_to_domain(self, domain, name, obj,
has_content=None, argument_spec=None, **option_spec):
# type: (unicode, unicode, Any, bool, Any, Any) -> None
+ logger.debug('[app] adding directive to domain: %r',
+ (domain, name, obj, has_content, argument_spec, option_spec))
if domain not in self.domains:
raise ExtensionError(__('domain %s not yet registered') % domain)
- directive = directive_helper(obj, has_content, argument_spec, **option_spec)
- self.domains[domain].directives[name] = directive
+ directives = self.domain_directives.setdefault(domain, {})
+ directives[name] = directive_helper(obj, has_content, argument_spec, **option_spec)
def add_role_to_domain(self, domain, name, role):
- # type: (unicode, unicode, Any) -> None
+ # type: (unicode, unicode, Union[RoleFunction, XRefRole]) -> None
+ logger.debug('[app] adding role to domain: %r', (domain, name, role))
if domain not in self.domains:
raise ExtensionError(__('domain %s not yet registered') % domain)
- self.domains[domain].roles[name] = role
+ roles = self.domain_roles.setdefault(domain, {})
+ roles[name] = role
def add_index_to_domain(self, domain, index):
# type: (unicode, Type[Index]) -> None
+ logger.debug('[app] adding index to domain: %r', (domain, index))
if domain not in self.domains:
raise ExtensionError(__('domain %s not yet registered') % domain)
- self.domains[domain].indices.append(index)
+ indices = self.domain_indices.setdefault(domain, [])
+ indices.append(index)
def add_object_type(self, directivename, rolename, indextemplate='',
parse_node=None, ref_nodeclass=None, objname='',
doc_field_types=[]):
# type: (unicode, unicode, unicode, Callable, nodes.Node, unicode, List) -> None
+ logger.debug('[app] adding object type: %r',
+ (directivename, rolename, indextemplate, parse_node,
+ ref_nodeclass, objname, doc_field_types))
+
# create a subclass of GenericObject as the new directive
directive = type(directivename, # type: ignore
(GenericObject, object),
@@ -136,36 +174,89 @@ class SphinxComponentRegistry(object):
'parse_node': staticmethod(parse_node),
'doc_field_types': doc_field_types})
- stddomain = self.domains['std']
- stddomain.directives[directivename] = directive
- stddomain.roles[rolename] = XRefRole(innernodeclass=ref_nodeclass)
- stddomain.object_types[directivename] = ObjType(objname or directivename, rolename)
+ self.add_directive_to_domain('std', directivename, directive)
+ self.add_role_to_domain('std', rolename, XRefRole(innernodeclass=ref_nodeclass))
+
+ object_types = self.domain_object_types.setdefault('std', {})
+ object_types[directivename] = ObjType(objname or directivename, rolename)
def add_crossref_type(self, directivename, rolename, indextemplate='',
ref_nodeclass=None, objname=''):
# type: (unicode, unicode, unicode, nodes.Node, unicode) -> None
+ logger.debug('[app] adding crossref type: %r',
+ (directivename, rolename, indextemplate, ref_nodeclass, objname))
+
# create a subclass of Target as the new directive
directive = type(directivename, # type: ignore
(Target, object),
{'indextemplate': indextemplate})
- stddomain = self.domains['std']
- stddomain.directives[directivename] = directive
- stddomain.roles[rolename] = XRefRole(innernodeclass=ref_nodeclass)
- stddomain.object_types[directivename] = ObjType(objname or directivename, rolename)
+ self.add_directive_to_domain('std', directivename, directive)
+ self.add_role_to_domain('std', rolename, XRefRole(innernodeclass=ref_nodeclass))
+
+ object_types = self.domain_object_types.setdefault('std', {})
+ object_types[directivename] = ObjType(objname or directivename, rolename)
def add_source_parser(self, suffix, parser):
- # type: (unicode, Parser) -> None
+ # type: (unicode, Type[Parser]) -> None
+ logger.debug('[app] adding search source_parser: %r, %r', suffix, parser)
if suffix in self.source_parsers:
raise ExtensionError(__('source_parser for %r is already registered') % suffix)
self.source_parsers[suffix] = parser
+ def get_source_parser(self, filename):
+ # type: (unicode) -> Type[Parser]
+ for suffix, parser_class in iteritems(self.source_parsers):
+ if filename.endswith(suffix):
+ break
+ else:
+ # use special parser for unknown file-extension '*' (if exists)
+ parser_class = self.source_parsers.get('*')
+
+ if parser_class is None:
+ raise SphinxError(__('Source parser for %s not registered') % filename)
+ else:
+ if isinstance(parser_class, string_types):
+ parser_class = import_object(parser_class, 'source parser') # type: ignore
+ return parser_class
+
def get_source_parsers(self):
# type: () -> Dict[unicode, Parser]
return self.source_parsers
+ def create_source_parser(self, app, filename):
+ # type: (Sphinx, unicode) -> Parser
+ parser_class = self.get_source_parser(filename)
+ parser = parser_class()
+ if isinstance(parser, SphinxParser):
+ parser.set_application(app)
+ return parser
+
+ def add_source_input(self, filetype, input_class):
+ # type: (unicode, Type[Input]) -> None
+ if filetype in self.source_inputs:
+ raise ExtensionError(__('source_input for %r is already registered') % filetype)
+ self.source_inputs[filetype] = input_class
+
+ def get_source_input(self, filename):
+ # type: (unicode) -> Type[Input]
+ parser = self.get_source_parser(filename)
+ for filetype in parser.supported:
+ if filetype in self.source_inputs:
+ input_class = self.source_inputs[filetype]
+ break
+ else:
+ # use special source_input for unknown file-type '*' (if exists)
+ input_class = self.source_inputs.get('*')
+
+ if input_class is None:
+ raise SphinxError(__('source_input for %s not registered') % filename)
+ else:
+ return input_class
+
def add_translator(self, name, translator):
# type: (unicode, Type[nodes.NodeVisitor]) -> None
+ logger.info(bold(__('Change of translator for the %s builder.') % name))
self.translators[name] = translator
def get_translator_class(self, builder):
@@ -178,6 +269,32 @@ class SphinxComponentRegistry(object):
translator_class = self.get_translator_class(builder)
return translator_class(builder, document)
+ def add_transform(self, transform):
+ # type: (Type[Transform]) -> None
+ logger.debug('[app] adding transform: %r', transform)
+ self.transforms.append(transform)
+
+ def get_transforms(self):
+ # type: () -> List[Type[Transform]]
+ return self.transforms
+
+ def add_post_transform(self, transform):
+ # type: (Type[Transform]) -> None
+ logger.debug('[app] adding post transform: %r', transform)
+ self.post_transforms.append(transform)
+
+ def get_post_transforms(self):
+ # type: () -> List[Type[Transform]]
+ return self.post_transforms
+
+ def add_documenter(self, objtype, documenter):
+ # type: (unicode, Type[Documenter]) -> None
+ self.documenters[objtype] = documenter
+
+ def add_autodoc_attrgetter(self, typ, attrgetter):
+ # type: (Type, Callable[[Any, unicode, Any], Any]) -> None
+ self.autodoc_attrgettrs[typ] = attrgetter
+
def load_extension(self, app, extname):
# type: (Sphinx, unicode) -> None
"""Load a Sphinx extension."""
diff --git a/sphinx/roles.py b/sphinx/roles.py
index 4007b9f88..223c6c21f 100644
--- a/sphinx/roles.py
+++ b/sphinx/roles.py
@@ -5,7 +5,7 @@
Handlers for additional ReST roles.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py
index a6074a863..fc55a2a45 100644
--- a/sphinx/search/__init__.py
+++ b/sphinx/search/__init__.py
@@ -5,7 +5,7 @@
Create a full-text search index for offline search.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
diff --git a/sphinx/search/en.py b/sphinx/search/en.py
index f7ce43350..fe9b7d8da 100644
--- a/sphinx/search/en.py
+++ b/sphinx/search/en.py
@@ -5,7 +5,7 @@
English search language: includes the JS porter stemmer.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/search/ja.py b/sphinx/search/ja.py
index d1d922dd4..0cdc14a11 100644
--- a/sphinx/search/ja.py
+++ b/sphinx/search/ja.py
@@ -5,7 +5,7 @@
Japanese search language: includes routine to split words.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/search/jssplitter.py b/sphinx/search/jssplitter.py
index 56b91c1d0..7166565f1 100644
--- a/sphinx/search/jssplitter.py
+++ b/sphinx/search/jssplitter.py
@@ -7,7 +7,7 @@
DO NOT EDIT. This is generated by utils/jssplitter_generator.py
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/search/zh.py b/sphinx/search/zh.py
index 5ef4b5888..2301e1103 100644
--- a/sphinx/search/zh.py
+++ b/sphinx/search/zh.py
@@ -5,7 +5,7 @@
Chinese search language: includes routine to split words.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/setup_command.py b/sphinx/setup_command.py
index d219a14d9..cd89fe7f7 100644
--- a/sphinx/setup_command.py
+++ b/sphinx/setup_command.py
@@ -8,7 +8,7 @@
:author: Sebastian Wiesner
:contact: basti.wiesner@gmx.net
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
@@ -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/templates/epub2/container.xml b/sphinx/templates/epub2/container.xml
deleted file mode 100644
index 326cf15fa..000000000
--- a/sphinx/templates/epub2/container.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
- <rootfiles>
- <rootfile full-path="content.opf" media-type="application/oebps-package+xml"/>
- </rootfiles>
-</container>
diff --git a/sphinx/templates/epub2/content.opf_t b/sphinx/templates/epub2/content.opf_t
deleted file mode 100644
index 5169d0551..000000000
--- a/sphinx/templates/epub2/content.opf_t
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<package xmlns="http://www.idpf.org/2007/opf" version="2.0"
- unique-identifier="%(uid)s">
- <metadata xmlns:opf="http://www.idpf.org/2007/opf"
- xmlns:dc="http://purl.org/dc/elements/1.1/">
- <dc:language>{{ lang }}</dc:language>
- <dc:title>{{ title }}</dc:title>
- <dc:creator opf:role="aut">{{ author }}</dc:creator>
- <dc:publisher>{{ publisher }}</dc:publisher>
- <dc:rights>{{ copyright }}</dc:rights>
- <dc:identifier id="{{ uid }}" opf:scheme="{{ scheme }}">{{ id }}</dc:identifier>
- <dc:date>{{ date }}</dc:date>
- {%- if cover %}
- <meta name="cover" content="{{ cover }}"/>
- {%- endif %}
- </metadata>
- <manifest>
- <item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml" />
- {%- for item in manifest_items %}
- <item id="{{ item.id }}" href="{{ item.href }}" media-type="{{ item.media_type }}" />
- {%- endfor %}
- </manifest>
- <spine toc="ncx">
- {%- for spine in spines %}
- {%- if spine.linear %}
- <itemref idref="{{ spine.idref }}" />
- {%- else %}
- <itemref idref="{{ spine.idref }}" linear="no" />
- {%- endif %}
- {%- endfor %}
- </spine>
- <guide>
- {%- for guide in guides %}
- <reference type="{{ guide.type }}" title="{{ guide.title }}" href="{{ guide.uri }}" />
- {%- endfor %}
- </guide>
-</package>
diff --git a/sphinx/templates/epub2/mimetype b/sphinx/templates/epub2/mimetype
deleted file mode 100644
index 57ef03f24..000000000
--- a/sphinx/templates/epub2/mimetype
+++ /dev/null
@@ -1 +0,0 @@
-application/epub+zip \ No newline at end of file
diff --git a/sphinx/templates/epub2/toc.ncx_t b/sphinx/templates/epub2/toc.ncx_t
deleted file mode 100644
index 9bb701908..000000000
--- a/sphinx/templates/epub2/toc.ncx_t
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0"?>
-<ncx version="2005-1" xmlns="http://www.daisy.org/z3986/2005/ncx/">
- <head>
- <meta name="dtb:uid" content="{{ uid }}"/>
- <meta name="dtb:depth" content="{{ level }}"/>
- <meta name="dtb:totalPageCount" content="0"/>
- <meta name="dtb:maxPageNumber" content="0"/>
- </head>
- <docTitle>
- <text>{{ title }}</text>
- </docTitle>
- <navMap>
-{{ navpoints }}
- </navMap>
-</ncx>
diff --git a/sphinx/templates/quickstart/Makefile_t b/sphinx/templates/quickstart/Makefile_t
index 4639a982b..2858d9bf7 100644
--- a/sphinx/templates/quickstart/Makefile_t
+++ b/sphinx/templates/quickstart/Makefile_t
@@ -27,7 +27,6 @@ help:
@echo " applehelp to make an Apple Help Book"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
- @echo " epub3 to make an epub3"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@@ -122,12 +121,6 @@ epub:
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-.PHONY: epub3
-epub3:
- $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
- @echo
- @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
-
.PHONY: latex
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
diff --git a/sphinx/templates/quickstart/conf.py_t b/sphinx/templates/quickstart/conf.py_t
index 4e828f330..c42861c28 100644
--- a/sphinx/templates/quickstart/conf.py_t
+++ b/sphinx/templates/quickstart/conf.py_t
@@ -42,7 +42,11 @@ sys.path.insert(0, u'{{ module_path }}')
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
-extensions = [{{ extensions }}]
+extensions = [
+{%- for ext in extensions %}
+ '{{ ext }}',
+{%- endfor %}
+]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['{{ dot }}templates']
@@ -85,9 +89,6 @@ exclude_patterns = [{{ exclude_patterns }}]
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
-# If true, `todo` and `todoList` produce output, else they produce nothing.
-todo_include_todos = {{ ext_todo }}
-
# -- Options for HTML output ----------------------------------------------
@@ -173,8 +174,8 @@ texinfo_documents = [
author, '{{ project_fn }}', 'One line description of project.',
'Miscellaneous'),
]
+{%- if epub %}
-{% if epub %}
# -- Options for Epub output ----------------------------------------------
@@ -195,9 +196,23 @@ epub_copyright = copyright
# A list of files that should not be packed into the epub file.
epub_exclude_files = ['search.html']
-{% endif %}
+{%- endif %}
+{%- if extensions %}
+
+
+# -- Extension configuration ----------------------------------------------
+{%- endif %}
+{%- if 'sphinx.ext.intersphinx' in extensions %}
+
+# -- Options for intersphinx extension ------------------------------------
-{% if ext_intersphinx %}
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'https://docs.python.org/': None}
-{% endif %}
+{%- endif %}
+{%- if 'sphinx.ext.todo' in extensions %}
+
+# -- Options for todo extension -------------------------------------------
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = True
+{%- endif %}
diff --git a/sphinx/templates/quickstart/make.bat_t b/sphinx/templates/quickstart/make.bat_t
index 8438b5f7e..230977488 100644
--- a/sphinx/templates/quickstart/make.bat_t
+++ b/sphinx/templates/quickstart/make.bat_t
@@ -29,7 +29,6 @@ if "%1" == "help" (
echo. qthelp to make HTML files and a qthelp project
echo. devhelp to make HTML files and a Devhelp project
echo. epub to make an epub
- echo. epub3 to make an epub3
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. text to make text files
echo. man to make manual pages
@@ -153,14 +152,6 @@ if "%1" == "epub" (
goto end
)
-if "%1" == "epub3" (
- %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The epub3 file is in %BUILDDIR%/epub3.
- goto end
-)
-
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
if errorlevel 1 exit /b 1
diff --git a/sphinx/testing/__init__.py b/sphinx/testing/__init__.py
index e246be8c0..c551da36f 100644
--- a/sphinx/testing/__init__.py
+++ b/sphinx/testing/__init__.py
@@ -10,6 +10,6 @@
pytest_plugins = 'sphinx.testing.fixtures'
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/testing/fixtures.py b/sphinx/testing/fixtures.py
index 624adc03a..be0037b70 100644
--- a/sphinx/testing/fixtures.py
+++ b/sphinx/testing/fixtures.py
@@ -5,7 +5,7 @@
Sphinx test fixtures for pytest
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
diff --git a/sphinx/testing/path.py b/sphinx/testing/path.py
index 634d61332..30c4b49f3 100644
--- a/sphinx/testing/path.py
+++ b/sphinx/testing/path.py
@@ -3,7 +3,7 @@
sphinx.testing.path
~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os
diff --git a/sphinx/testing/util.py b/sphinx/testing/util.py
index 91ae821ac..fb2e8f1f5 100644
--- a/sphinx/testing/util.py
+++ b/sphinx/testing/util.py
@@ -5,7 +5,7 @@
Sphinx test suite utilities
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os
diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty
index bc524911b..2b41673db 100644
--- a/sphinx/texinputs/sphinx.sty
+++ b/sphinx/texinputs/sphinx.sty
@@ -6,7 +6,7 @@
%
\NeedsTeXFormat{LaTeX2e}[1995/12/01]
-\ProvidesPackage{sphinx}[2017/10/27 v1.7 LaTeX package (Sphinx markup)]
+\ProvidesPackage{sphinx}[2017/12/12 v1.7 LaTeX package (Sphinx markup)]
% provides \ltx@ifundefined
% (many packages load ltxcmds: graphicx does for pdftex and lualatex but
@@ -39,7 +39,7 @@
\@ifclassloaded{memoir}{}{\RequirePackage{fancyhdr}}
% for \text macro and \iffirstchoice@ conditional even if amsmath not loaded
\RequirePackage{amstext}
-\RequirePackage{textcomp}
+\RequirePackage[warn]{textcomp}
\RequirePackage{titlesec}
\@ifpackagelater{titlesec}{2016/03/15}%
{\@ifpackagelater{titlesec}{2016/03/21}%
@@ -165,6 +165,7 @@
% For highlighted code.
\RequirePackage{fancyvrb}
\fvset{fontsize=\small}
+\define@key{FV}{hllines}{\def\sphinx@verbatim@checkifhl##1{\in@{, ##1,}{#1}}}
% For hyperlinked footnotes in tables; also for gathering footnotes from
% topic and warning blocks. Also to allow code-blocks in footnotes.
\RequirePackage{footnotehyper-sphinx}
@@ -182,7 +183,7 @@
% control caption around literal-block
\RequirePackage{capt-of}
\RequirePackage{needspace}
-
+\RequirePackage{remreset}% provides \@removefromreset
% to make pdf with correct encoded bookmarks in Japanese
% this should precede the hyperref package
\ifx\kanjiskip\@undefined
@@ -214,6 +215,17 @@
% stylesheet for highlighting with pygments
\RequirePackage{sphinxhighlight}
+% fix baseline increase from Pygments latex formatter in case of error tokens
+% and keep \fboxsep's scope local via added braces
+\def\PYG@tok@err{%
+ \def\PYG@bc##1{{\setlength{\fboxsep}{-\fboxrule}%
+ \fcolorbox[rgb]{1.00,0.00,0.00}{1,1,1}{\strut ##1}}}%
+}
+\def\PYG@tok@cs{%
+ \def\PYG@tc##1{\textcolor[rgb]{0.25,0.50,0.56}{##1}}%
+ \def\PYG@bc##1{{\setlength{\fboxsep}{0pt}%
+ \colorbox[rgb]{1.00,0.94,0.94}{\strut ##1}}}%
+}%
%% OPTIONS
@@ -235,7 +247,10 @@
\fi
\DeclareStringOption[0]{maxlistdepth}% \newcommand*\spx@opt@maxlistdepth{0}
-
+\DeclareStringOption[-1]{numfigreset}
+\DeclareBoolOption[false]{nonumfigreset}
+\DeclareBoolOption[false]{mathnumfig}
+% \DeclareBoolOption[false]{usespart}% not used
% dimensions, we declare the \dimen registers here.
\newdimen\sphinxverbatimsep
\newdimen\sphinxverbatimborder
@@ -314,6 +329,8 @@
% set the key handler. The "value" ##1 must be acceptable by \definecolor.
\define@key{sphinx}{#1}{\definecolor{sphinx#1}##1}%
}%
+% Default color chosen to be as in minted.sty LaTeX package!
+\sphinxDeclareSphinxColorOption{VerbatimHighlightColor}{{rgb}{0.878,1,1}}
% admonition boxes, "light" style
\sphinxDeclareSphinxColorOption{noteBorderColor}{{rgb}{0,0,0}}
\sphinxDeclareSphinxColorOption{hintBorderColor}{{rgb}{0,0,0}}
@@ -335,6 +352,9 @@
\ProcessKeyvalOptions*
% don't allow use of maxlistdepth via \sphinxsetup.
\DisableKeyvalOption{sphinx}{maxlistdepth}
+\DisableKeyvalOption{sphinx}{numfigreset}
+\DisableKeyvalOption{sphinx}{nonumfigreset}
+\DisableKeyvalOption{sphinx}{mathnumfig}
% user interface: options can be changed midway in a document!
\newcommand\sphinxsetup[1]{\setkeys{sphinx}{#1}}
@@ -370,7 +390,7 @@
\expandafter\let
\csname @list\romannumeral\the\count@\expandafter\endcsname
\csname @list\romannumeral\the\numexpr\count@-\@ne\endcsname
- % work around 2.6--3.2d babel-french issue (fixed in 3.2e; no change needed)
+ % workaround 2.6--3.2d babel-french issue (fixed in 3.2e; no change needed)
\ltx@ifundefined{leftmargin\romannumeral\the\count@}
{\expandafter\let
\csname leftmargin\romannumeral\the\count@\expandafter\endcsname
@@ -643,6 +663,7 @@
{\abovecaptionskip\smallskipamount
\belowcaptionskip\smallskipamount}
+
%% FOOTNOTES
%
% Support large numbered footnotes in minipage
@@ -651,6 +672,111 @@
\def\thempfootnote{\arabic{mpfootnote}}
+%% NUMBERING OF FIGURES, TABLES, AND LITERAL BLOCKS
+\ltx@ifundefined{c@chapter}
+ {\newcounter{literalblock}}%
+ {\newcounter{literalblock}[chapter]%
+ \def\theliteralblock{\ifnum\c@chapter>\z@\arabic{chapter}.\fi
+ \arabic{literalblock}}%
+ }%
+\ifspx@opt@nonumfigreset
+ \ltx@ifundefined{c@chapter}{}{%
+ \@removefromreset{figure}{chapter}%
+ \@removefromreset{table}{chapter}%
+ \@removefromreset{literalblock}{chapter}%
+ \ifspx@opt@mathnumfig
+ \@removefromreset{equation}{chapter}%
+ \fi
+ }%
+ \def\thefigure{\arabic{figure}}%
+ \def\thetable {\arabic{table}}%
+ \def\theliteralblock{\arabic{literalblock}}%
+ \ifspx@opt@mathnumfig
+ \def\theequation{\arabic{equation}}%
+ \fi
+\else
+\let\spx@preAthefigure\@empty
+\let\spx@preBthefigure\@empty
+% \ifspx@opt@usespart % <-- LaTeX writer could pass such a 'usespart' boolean
+% % as sphinx.sty package option
+% If document uses \part, (triggered in Sphinx by latex_toplevel_sectioning)
+% LaTeX core per default does not reset chapter or section
+% counters at each part.
+% But if we modify this, we need to redefine \thechapter, \thesection to
+% include the part number and this will cause problems in table of contents
+% because of too wide numbering. Simplest is to do nothing.
+% \fi
+\ifnum\spx@opt@numfigreset>0
+ \ltx@ifundefined{c@chapter}
+ {}
+ {\g@addto@macro\spx@preAthefigure{\ifnum\c@chapter>\z@\arabic{chapter}.}%
+ \g@addto@macro\spx@preBthefigure{\fi}}%
+\fi
+\ifnum\spx@opt@numfigreset>1
+ \@addtoreset{figure}{section}%
+ \@addtoreset{table}{section}%
+ \@addtoreset{literalblock}{section}%
+ \ifspx@opt@mathnumfig
+ \@addtoreset{equation}{section}%
+ \fi
+ \g@addto@macro\spx@preAthefigure{\ifnum\c@section>\z@\arabic{section}.}%
+ \g@addto@macro\spx@preBthefigure{\fi}%
+\fi
+\ifnum\spx@opt@numfigreset>2
+ \@addtoreset{figure}{subsection}%
+ \@addtoreset{table}{subsection}%
+ \@addtoreset{literalblock}{subsection}%
+ \ifspx@opt@mathnumfig
+ \@addtoreset{equation}{subsection}%
+ \fi
+ \g@addto@macro\spx@preAthefigure{\ifnum\c@subsection>\z@\arabic{subsection}.}%
+ \g@addto@macro\spx@preBthefigure{\fi}%
+\fi
+\ifnum\spx@opt@numfigreset>3
+ \@addtoreset{figure}{subsubsection}%
+ \@addtoreset{table}{subsubsection}%
+ \@addtoreset{literalblock}{subsubsection}%
+ \ifspx@opt@mathnumfig
+ \@addtoreset{equation}{subsubsection}%
+ \fi
+ \g@addto@macro\spx@preAthefigure{\ifnum\c@subsubsection>\z@\arabic{subsubsection}.}%
+ \g@addto@macro\spx@preBthefigure{\fi}%
+\fi
+\ifnum\spx@opt@numfigreset>4
+ \@addtoreset{figure}{paragraph}%
+ \@addtoreset{table}{paragraph}%
+ \@addtoreset{literalblock}{paragraph}%
+ \ifspx@opt@mathnumfig
+ \@addtoreset{equation}{paragraph}%
+ \fi
+ \g@addto@macro\spx@preAthefigure{\ifnum\c@subparagraph>\z@\arabic{subparagraph}.}%
+ \g@addto@macro\spx@preBthefigure{\fi}%
+\fi
+\ifnum\spx@opt@numfigreset>5
+ \@addtoreset{figure}{subparagraph}%
+ \@addtoreset{table}{subparagraph}%
+ \@addtoreset{literalblock}{subparagraph}%
+ \ifspx@opt@mathnumfig
+ \@addtoreset{equation}{subparagraph}%
+ \fi
+ \g@addto@macro\spx@preAthefigure{\ifnum\c@subsubparagraph>\z@\arabic{subsubparagraph}.}%
+ \g@addto@macro\spx@preBthefigure{\fi}%
+\fi
+\expandafter\g@addto@macro
+\expandafter\spx@preAthefigure\expandafter{\spx@preBthefigure}%
+\let\thefigure\spx@preAthefigure
+\let\thetable\spx@preAthefigure
+\let\theliteralblock\spx@preAthefigure
+\g@addto@macro\thefigure{\arabic{figure}}%
+\g@addto@macro\thetable{\arabic{table}}%
+\g@addto@macro\theliteralblock{\arabic{literalblock}}%
+ \ifspx@opt@mathnumfig
+ \let\theequation\spx@preAthefigure
+ \g@addto@macro\theequation{\arabic{equation}}%
+ \fi
+\fi
+
+
%% LITERAL BLOCKS
%
% Based on use of "fancyvrb.sty"'s Verbatim.
@@ -666,15 +792,6 @@
\let\endOriginalVerbatim\endVerbatim
% for captions of literal blocks
-% also define `\theH...` macros for hyperref
-\newcounter{literalblock}
-\ltx@ifundefined{c@chapter}
- {\@addtoreset{literalblock}{section}
- \def\theliteralblock {\ifnum\c@section>\z@ \thesection.\fi\arabic{literalblock}}
- \def\theHliteralblock {\theHsection.\arabic{literalblock}}}
- {\@addtoreset{literalblock}{chapter}
- \def\theliteralblock {\ifnum\c@chapter>\z@ \thechapter.\fi\arabic{literalblock}}
- \def\theHliteralblock {\theHchapter.\arabic{literalblock}}}
% at start of caption title
\newcommand*{\fnum@literalblock}{\literalblockname\nobreakspace\theliteralblock}
% this will be overwritten in document preamble by Babel translation
@@ -851,6 +968,34 @@
% needed to create wrapper environments of fancyvrb's Verbatim
\newcommand*{\sphinxVerbatimEnvironment}{\gdef\FV@EnvironName{sphinxVerbatim}}
\newcommand*{\sphinxverbatimsmallskipamount}{\smallskipamount}
+% serves to implement line highlighting and line wrapping
+\newcommand\sphinxFancyVerbFormatLine[1]{%
+ \expandafter\sphinx@verbatim@checkifhl\expandafter{\the\FV@CodeLineNo}%
+ \ifin@
+ \sphinxVerbatimHighlightLine{#1}%
+ \else
+ \sphinxVerbatimFormatLine{#1}%
+ \fi
+}%
+\newcommand\sphinxVerbatimHighlightLine[1]{%
+ \edef\sphinxrestorefboxsep{\fboxsep\the\fboxsep\relax}%
+ \fboxsep0pt\relax % cf LaTeX bug graphics/4524
+ \colorbox{sphinxVerbatimHighlightColor}%
+ {\sphinxrestorefboxsep\sphinxVerbatimFormatLine{#1}}%
+ % no need to restore \fboxsep here, as this ends up in a \hbox from fancyvrb
+}%
+% \sphinxVerbatimFormatLine will be set locally to one of those two:
+\newcommand\sphinxVerbatimFormatLineWrap[1]{%
+ \hsize\linewidth
+ \vtop{\raggedright\hyphenpenalty\z@\exhyphenpenalty\z@
+ \doublehyphendemerits\z@\finalhyphendemerits\z@
+ \strut #1\strut}%
+}%
+\newcommand\sphinxVerbatimFormatLineNoWrap[1]{\hb@xt@\linewidth{\strut #1\hss}}%
+\g@addto@macro\FV@SetupFont{%
+ \sbox\sphinxcontinuationbox {\spx@opt@verbatimcontinued}%
+ \sbox\sphinxvisiblespacebox {\spx@opt@verbatimvisiblespace}%
+}%
\newenvironment{sphinxVerbatim}{%
% first, let's check if there is a caption
\ifx\sphinxVerbatimTitle\empty
@@ -905,23 +1050,19 @@
% to achieve this without extensive rewrite of fancyvrb.
% - The (not used in sphinx) obeytabs option to Verbatim is
% broken by this change (showtabs and tabspace work).
- \expandafter\def\expandafter\FV@SetupFont\expandafter
- {\FV@SetupFont\sbox\sphinxcontinuationbox {\spx@opt@verbatimcontinued}%
- \sbox\sphinxvisiblespacebox {\spx@opt@verbatimvisiblespace}}%
- \def\FancyVerbFormatLine ##1{\hsize\linewidth
- \vtop{\raggedright\hyphenpenalty\z@\exhyphenpenalty\z@
- \doublehyphendemerits\z@\finalhyphendemerits\z@
- \strut ##1\strut}%
- }%
- \let\FV@Space\spx@verbatim@space
+ \let\sphinxVerbatimFormatLine\sphinxVerbatimFormatLineWrap
+ \let\FV@Space\spx@verbatim@space
% Allow breaks at special characters using \PYG... macros.
- \sphinxbreaksatspecials
+ \sphinxbreaksatspecials
% Breaks at punctuation characters . , ; ? ! and / (needs catcode activation)
- \def\FancyVerbCodes{\sphinxbreaksviaactive}%
- \fi % end of conditional code for wrapping long code lines
- % go around fancyvrb's check of \@currenvir
+ \fvset{codes*=\sphinxbreaksviaactive}%
+ \else % end of conditional code for wrapping long code lines
+ \let\sphinxVerbatimFormatLine\sphinxVerbatimFormatLineNoWrap
+ \fi
+ \let\FancyVerbFormatLine\sphinxFancyVerbFormatLine
+ % workaround to fancyvrb's check of \@currenvir
\let\VerbatimEnvironment\sphinxVerbatimEnvironment
- % go around fancyvrb's check of current list depth
+ % workaround to fancyvrb's check of current list depth
\def\@toodeep {\advance\@listdepth\@ne}%
% The list environment is needed to control perfectly the vertical space.
% Note: \OuterFrameSep used by framed.sty is later set to \topsep hence 0pt.
@@ -1018,7 +1159,7 @@
\sphinxunactivateextras}%
% now for the modified alltt environment
\newenvironment{sphinxalltt}
-{% at start of next line to work around Emacs/AUCTeX issue with this file
+{% at start of next line to workaround Emacs/AUCTeX issue with this file
\begin{alltt}%
\ifspx@opt@parsedliteralwraps
\sbox\sphinxcontinuationbox {\spx@opt@verbatimcontinued}%
@@ -1243,7 +1384,7 @@
\spx@notice@border \dimexpr\csname spx@opt@#1border\endcsname\relax
% start specific environment, passing the heading as argument
\begin{sphinx#1}{#2}}
- % in end part, need to go around a LaTeX's "feature"
+ % workaround some LaTeX "feature" of \end command
{\edef\spx@temp{\noexpand\end{sphinx\spx@noticetype}}\spx@temp}
@@ -1318,8 +1459,8 @@
%
\newenvironment{productionlist}{%
% \def\sphinxoptional##1{{\Large[}##1{\Large]}}
- \def\production##1##2{\\\sphinxcode{##1}&::=&\sphinxcode{##2}}%
- \def\productioncont##1{\\& &\sphinxcode{##1}}%
+ \def\production##1##2{\\\sphinxcode{\sphinxupquote{##1}}&::=&\sphinxcode{\sphinxupquote{##2}}}%
+ \def\productioncont##1{\\& &\sphinxcode{\sphinxupquote{##1}}}%
\parindent=2em
\indent
\setlength{\LTpre}{0pt}%
@@ -1400,15 +1541,13 @@
%% TEXT STYLING
%
-% Some custom font markup commands.
-\protected\def\sphinxstrong#1{{\textbf{#1}}}
% to obtain straight quotes we execute \@noligs as patched by upquote, and
% \scantokens is needed in cases where it would be too late for the macro to
% first set catcodes and then fetch its argument. We also make the contents
% breakable at non-escaped . , ; ? ! / using \sphinxbreaksviaactive.
% the macro must be protected if it ends up used in moving arguments,
% in 'alltt' \@noligs is done already, and the \scantokens must be avoided.
-\protected\def\sphinxcode#1{{\def\@tempa{alltt}%
+\protected\def\sphinxupquote#1{{\def\@tempa{alltt}%
\ifx\@tempa\@currenvir\else
\ifspx@opt@inlineliteralwraps
\sphinxbreaksviaactive\let\sphinxafterbreak\empty
@@ -1419,12 +1558,15 @@
\let\do@noligs\sphinx@do@noligs
\@noligs\endlinechar\m@ne\everyeof{}% (<- in case inside \sphinxhref)
\expandafter\scantokens
- \fi {\texttt{#1}}}}
+ \fi {{#1}}}}% extra brace pair to fix end-space gobbling issue...
\def\sphinx@do@noligs #1{\catcode`#1\active\begingroup\lccode`\~`#1\relax
\lowercase{\endgroup\def~{\leavevmode\kern\z@\char`#1 }}}
\def\sphinx@literal@nolig@list {\do\`\do\<\do\>\do\'\do\-}%
-\protected\def\sphinxbfcode#1{\sphinxcode{\bfseries{}#1}}
+% Some custom font markup commands.
+\protected\def\sphinxstrong#1{\textbf{#1}}
+\protected\def\sphinxcode#1{\texttt{#1}}
+\protected\def\sphinxbfcode#1{\textbf{\sphinxcode{#1}}}
\protected\def\sphinxemail#1{\textsf{#1}}
\protected\def\sphinxtablecontinued#1{\textsf{#1}}
\protected\def\sphinxtitleref#1{\emph{#1}}
@@ -1438,21 +1580,21 @@
% additional customizable styling
% FIXME: convert this to package options ?
-\protected\def\sphinxstyleindexentry {\texttt}
-\protected\def\sphinxstyleindexextra #1{ \emph{(#1)}}
-\protected\def\sphinxstyleindexpageref {, \pageref}
-\protected\def\sphinxstyletopictitle #1{\textbf{#1}\par\medskip}
+\protected\def\sphinxstyleindexentry #1{\texttt{#1}}
+\protected\def\sphinxstyleindexextra #1{ \emph{(#1)}}
+\protected\def\sphinxstyleindexpageref #1{, \pageref{#1}}
+\protected\def\sphinxstyletopictitle #1{\textbf{#1}\par\medskip}
\let\sphinxstylesidebartitle\sphinxstyletopictitle
-\protected\def\sphinxstyleothertitle {\textbf}
+\protected\def\sphinxstyleothertitle #1{\textbf{#1}}
\protected\def\sphinxstylesidebarsubtitle #1{~\\\textbf{#1} \smallskip}
% \text.. commands do not allow multiple paragraphs
\protected\def\sphinxstyletheadfamily {\sffamily}
-\protected\def\sphinxstyleemphasis {\emph}
+\protected\def\sphinxstyleemphasis #1{\emph{#1}}
\protected\def\sphinxstyleliteralemphasis#1{\emph{\sphinxcode{#1}}}
-\protected\def\sphinxstylestrong {\textbf}
-\protected\def\sphinxstyleliteralstrong {\sphinxbfcode}
-\protected\def\sphinxstyleabbreviation {\textsc}
-\protected\def\sphinxstyleliteralintitle {\sphinxcode}
+\protected\def\sphinxstylestrong #1{\textbf{#1}}
+\protected\def\sphinxstyleliteralstrong#1{\sphinxbfcode{#1}}
+\protected\def\sphinxstyleabbreviation #1{\textsc{#1}}
+\protected\def\sphinxstyleliteralintitle#1{\sphinxcode{#1}}
\newcommand*\sphinxstylecodecontinued[1]{\footnotesize(#1)}%
\newcommand*\sphinxstylecodecontinues[1]{\footnotesize(#1)}%
% figure legend comes after caption and may contain arbitrary body elements
diff --git a/sphinx/texinputs/sphinxhowto.cls b/sphinx/texinputs/sphinxhowto.cls
index 90680fdee..11a49a205 100644
--- a/sphinx/texinputs/sphinxhowto.cls
+++ b/sphinx/texinputs/sphinxhowto.cls
@@ -25,6 +25,7 @@
% reset these counters in your preamble.
%
\setcounter{secnumdepth}{2}
+\setcounter{tocdepth}{2}% i.e. section and subsection
% Change the title page to look a bit better, and fit in with the fncychap
% ``Bjarne'' style a bit better.
diff --git a/sphinx/themes/agogo/layout.html b/sphinx/themes/agogo/layout.html
index dcf5b0ed7..8bd476cad 100644
--- a/sphinx/themes/agogo/layout.html
+++ b/sphinx/themes/agogo/layout.html
@@ -5,7 +5,7 @@
Sphinx layout template for the agogo theme, originally written
by Andi Albrecht.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- extends "basic/layout.html" %}
diff --git a/sphinx/themes/agogo/static/agogo.css_t b/sphinx/themes/agogo/static/agogo.css_t
index f9e0d1637..0b5bbe16b 100644
--- a/sphinx/themes/agogo/static/agogo.css_t
+++ b/sphinx/themes/agogo/static/agogo.css_t
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- agogo theme.
*
- * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/basic/defindex.html b/sphinx/themes/basic/defindex.html
index 303f9668b..5c92348b9 100644
--- a/sphinx/themes/basic/defindex.html
+++ b/sphinx/themes/basic/defindex.html
@@ -4,7 +4,7 @@
Default template for the "index" page.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}{{ warn('Now base template defindex.html is deprecated.') }}
{%- extends "layout.html" %}
diff --git a/sphinx/themes/basic/domainindex.html b/sphinx/themes/basic/domainindex.html
index dafbf72dc..58f0fc990 100644
--- a/sphinx/themes/basic/domainindex.html
+++ b/sphinx/themes/basic/domainindex.html
@@ -4,7 +4,7 @@
Template for domain indices (module index, ...).
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- extends "layout.html" %}
diff --git a/sphinx/themes/basic/genindex-single.html b/sphinx/themes/basic/genindex-single.html
index bbdbfd0d0..1f61fa0f5 100644
--- a/sphinx/themes/basic/genindex-single.html
+++ b/sphinx/themes/basic/genindex-single.html
@@ -4,7 +4,7 @@
Template for a "single" page of a split index.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% macro indexentries(firstname, links) %}
diff --git a/sphinx/themes/basic/genindex-split.html b/sphinx/themes/basic/genindex-split.html
index 12887c10e..42a7a4be3 100644
--- a/sphinx/themes/basic/genindex-split.html
+++ b/sphinx/themes/basic/genindex-split.html
@@ -4,7 +4,7 @@
Template for a "split" index overview page.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- extends "layout.html" %}
diff --git a/sphinx/themes/basic/genindex.html b/sphinx/themes/basic/genindex.html
index fd96ba642..c852f25e8 100644
--- a/sphinx/themes/basic/genindex.html
+++ b/sphinx/themes/basic/genindex.html
@@ -4,7 +4,7 @@
Template for an "all-in-one" index.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% macro indexentries(firstname, links) %}
diff --git a/sphinx/themes/basic/globaltoc.html b/sphinx/themes/basic/globaltoc.html
index 3b3a9201a..dc6fea373 100644
--- a/sphinx/themes/basic/globaltoc.html
+++ b/sphinx/themes/basic/globaltoc.html
@@ -4,7 +4,7 @@
Sphinx sidebar template: global table of contents.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
<h3><a href="{{ pathto(master_doc) }}">{{ _('Table Of Contents') }}</a></h3>
diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html
index b337a977e..75c1ca568 100644
--- a/sphinx/themes/basic/layout.html
+++ b/sphinx/themes/basic/layout.html
@@ -4,7 +4,7 @@
Master layout template for Sphinx themes.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- block doctype -%}{%- if html5_doctype %}
diff --git a/sphinx/themes/basic/localtoc.html b/sphinx/themes/basic/localtoc.html
index ca1a73ac0..5d3c7f4fd 100644
--- a/sphinx/themes/basic/localtoc.html
+++ b/sphinx/themes/basic/localtoc.html
@@ -4,7 +4,7 @@
Sphinx sidebar template: local table of contents.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- if display_toc %}
diff --git a/sphinx/themes/basic/page.html b/sphinx/themes/basic/page.html
index e96f667ba..d2f0bd3f3 100644
--- a/sphinx/themes/basic/page.html
+++ b/sphinx/themes/basic/page.html
@@ -4,7 +4,7 @@
Master template for simple pages.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- extends "layout.html" %}
diff --git a/sphinx/themes/basic/relations.html b/sphinx/themes/basic/relations.html
index d7fb6f0a4..7c0f4e711 100644
--- a/sphinx/themes/basic/relations.html
+++ b/sphinx/themes/basic/relations.html
@@ -4,7 +4,7 @@
Sphinx sidebar template: relation links.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- if prev %}
diff --git a/sphinx/themes/basic/search.html b/sphinx/themes/basic/search.html
index ce8fa8924..32432a1e3 100644
--- a/sphinx/themes/basic/search.html
+++ b/sphinx/themes/basic/search.html
@@ -4,7 +4,7 @@
Template for the search page.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- extends "layout.html" %}
diff --git a/sphinx/themes/basic/searchbox.html b/sphinx/themes/basic/searchbox.html
index 17d84a02b..506877410 100644
--- a/sphinx/themes/basic/searchbox.html
+++ b/sphinx/themes/basic/searchbox.html
@@ -4,18 +4,20 @@
Sphinx sidebar template: quick search box.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- if pagename != "search" and builder != "singlehtml" %}
<div id="searchbox" style="display: none" role="search">
<h3>{{ _('Quick search') }}</h3>
+ <div class="searchformwrapper">
<form class="search" action="{{ pathto('search') }}" method="get">
- <div><input type="text" name="q" /></div>
- <div><input type="submit" value="{{ _('Go') }}" /></div>
+ <input type="text" name="q" />
+ <input type="submit" value="{{ _('Go') }}" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
+ </div>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
{%- endif %}
diff --git a/sphinx/themes/basic/searchresults.html b/sphinx/themes/basic/searchresults.html
index e04ec15d6..1371bf93c 100644
--- a/sphinx/themes/basic/searchresults.html
+++ b/sphinx/themes/basic/searchresults.html
@@ -4,7 +4,7 @@
Template for the body of the search results page.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
<h1 id="search-documentation">{{ _('Search') }}</h1>
diff --git a/sphinx/themes/basic/sourcelink.html b/sphinx/themes/basic/sourcelink.html
index 3d4f76ddb..ecde6d3c5 100644
--- a/sphinx/themes/basic/sourcelink.html
+++ b/sphinx/themes/basic/sourcelink.html
@@ -4,7 +4,7 @@
Sphinx sidebar template: "show source" link.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- if show_source and has_source and sourcename %}
diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t
index d16c760cb..efb997d8f 100644
--- a/sphinx/themes/basic/static/basic.css_t
+++ b/sphinx/themes/basic/static/basic.css_t
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- basic theme.
*
- * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -82,9 +82,21 @@ div.sphinxsidebar input {
}
div.sphinxsidebar #searchbox input[type="text"] {
- width: 170px;
+ float: left;
+ width: 80%;
+ padding: 0.25em;
+ box-sizing: border-box;
}
+div.sphinxsidebar #searchbox input[type="submit"] {
+ float: left;
+ width: 20%;
+ border-left: none;
+ padding: 0.25em;
+ box-sizing: border-box;
+}
+
+
img {
border: 0;
max-width: 100%;
diff --git a/sphinx/themes/basic/static/doctools.js_t b/sphinx/themes/basic/static/doctools.js_t
index 326856cfc..b261a44f3 100644
--- a/sphinx/themes/basic/static/doctools.js_t
+++ b/sphinx/themes/basic/static/doctools.js_t
@@ -4,7 +4,7 @@
*
* Sphinx JavaScript utilities for all documentation.
*
- * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/basic/static/searchtools.js_t b/sphinx/themes/basic/static/searchtools.js_t
index 306fdf55f..e707bb1ea 100644
--- a/sphinx/themes/basic/static/searchtools.js_t
+++ b/sphinx/themes/basic/static/searchtools.js_t
@@ -4,7 +4,7 @@
*
* Sphinx JavaScript utilities for the full-text search.
*
- * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js
index a95bc3a66..78e14bb4a 100644
--- a/sphinx/themes/basic/static/websupport.js
+++ b/sphinx/themes/basic/static/websupport.js
@@ -4,7 +4,7 @@
*
* sphinx.websupport utilities for all documentation.
*
- * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/classic/layout.html b/sphinx/themes/classic/layout.html
index 50b6dc9e0..19f3d0279 100644
--- a/sphinx/themes/classic/layout.html
+++ b/sphinx/themes/classic/layout.html
@@ -4,7 +4,7 @@
Sphinx layout template for the classic theme.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- extends "basic/layout.html" %}
diff --git a/sphinx/themes/classic/static/classic.css_t b/sphinx/themes/classic/static/classic.css_t
index 25e1c0261..a84ef8696 100644
--- a/sphinx/themes/classic/static/classic.css_t
+++ b/sphinx/themes/classic/static/classic.css_t
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- classic theme.
*
- * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/classic/static/sidebar.js_t b/sphinx/themes/classic/static/sidebar.js_t
index 494df24f9..ce8361d9b 100644
--- a/sphinx/themes/classic/static/sidebar.js_t
+++ b/sphinx/themes/classic/static/sidebar.js_t
@@ -16,7 +16,7 @@
* Once the browser is closed the cookie is deleted and the position
* reset to the default (expanded).
*
- * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/epub/epub-cover.html b/sphinx/themes/epub/epub-cover.html
index 763be11ff..436c36aa0 100644
--- a/sphinx/themes/epub/epub-cover.html
+++ b/sphinx/themes/epub/epub-cover.html
@@ -4,7 +4,7 @@
Sample template for the html cover page.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- extends "layout.html" %}
diff --git a/sphinx/themes/epub/layout.html b/sphinx/themes/epub/layout.html
index f27e4daa1..84d4bf31c 100644
--- a/sphinx/themes/epub/layout.html
+++ b/sphinx/themes/epub/layout.html
@@ -4,7 +4,7 @@
Sphinx layout template for the epub theme.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- extends "basic/layout.html" %}
diff --git a/sphinx/themes/epub/static/epub.css_t b/sphinx/themes/epub/static/epub.css_t
index f8ef61e7c..0e8808f4a 100644
--- a/sphinx/themes/epub/static/epub.css_t
+++ b/sphinx/themes/epub/static/epub.css_t
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- epub theme.
*
- * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/haiku/layout.html b/sphinx/themes/haiku/layout.html
index a6e42d2d2..c93c52dbd 100644
--- a/sphinx/themes/haiku/layout.html
+++ b/sphinx/themes/haiku/layout.html
@@ -4,7 +4,7 @@
Sphinx layout template for the haiku theme.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- extends "basic/layout.html" %}
diff --git a/sphinx/themes/haiku/static/haiku.css_t b/sphinx/themes/haiku/static/haiku.css_t
index cb4a2fb62..16d49fea4 100644
--- a/sphinx/themes/haiku/static/haiku.css_t
+++ b/sphinx/themes/haiku/static/haiku.css_t
@@ -16,7 +16,7 @@
* Braden Ewing <brewin@gmail.com>
* Humdinger <humdingerb@gmail.com>
*
- * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/nature/static/nature.css_t b/sphinx/themes/nature/static/nature.css_t
index bb0c83bac..ff2b1d5ff 100644
--- a/sphinx/themes/nature/static/nature.css_t
+++ b/sphinx/themes/nature/static/nature.css_t
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- nature theme.
*
- * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -125,14 +125,11 @@ div.sphinxsidebar input {
font-size: 1em;
}
-div.sphinxsidebar input[type=text]{
+div.sphinxsidebar .searchformwrapper {
margin-left: 20px;
+ margin-right: 20px;
}
-div.sphinxsidebar input[type=submit]{
- margin-left: 20px;
-}
-
/* -- body styles ----------------------------------------------------------- */
a {
diff --git a/sphinx/themes/nonav/layout.html b/sphinx/themes/nonav/layout.html
index 4d79446c4..5e79b2a7a 100644
--- a/sphinx/themes/nonav/layout.html
+++ b/sphinx/themes/nonav/layout.html
@@ -4,7 +4,7 @@
Sphinx layout template for the any help system theme.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- extends "basic/layout.html" %}
diff --git a/sphinx/themes/nonav/static/nonav.css b/sphinx/themes/nonav/static/nonav.css
index 554b4b912..b41bd2044 100644
--- a/sphinx/themes/nonav/static/nonav.css
+++ b/sphinx/themes/nonav/static/nonav.css
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- nonav theme.
*
- * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/pyramid/static/epub.css b/sphinx/themes/pyramid/static/epub.css
index a0cffc066..0df0eaeeb 100644
--- a/sphinx/themes/pyramid/static/epub.css
+++ b/sphinx/themes/pyramid/static/epub.css
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- default theme.
*
- * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/pyramid/static/pyramid.css_t b/sphinx/themes/pyramid/static/pyramid.css_t
index ca36ef6ac..792f45452 100644
--- a/sphinx/themes/pyramid/static/pyramid.css_t
+++ b/sphinx/themes/pyramid/static/pyramid.css_t
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- pylons theme.
*
- * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -148,12 +148,9 @@ div.sphinxsidebar input {
font-size: 1em;
}
-div.sphinxsidebar input[type=text]{
- margin-left: 20px;
-}
-
-div.sphinxsidebar input[type=submit]{
+div.sphinxsidebar .searchformwrapper {
margin-left: 20px;
+ margin-right: 20px;
}
/* -- sidebars -------------------------------------------------------------- */
diff --git a/sphinx/themes/scrolls/layout.html b/sphinx/themes/scrolls/layout.html
index 893ae17d3..9ebe3b35d 100644
--- a/sphinx/themes/scrolls/layout.html
+++ b/sphinx/themes/scrolls/layout.html
@@ -5,7 +5,7 @@
Sphinx layout template for the scrolls theme, originally written
by Armin Ronacher.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- extends "basic/layout.html" %}
diff --git a/sphinx/themes/scrolls/static/scrolls.css_t b/sphinx/themes/scrolls/static/scrolls.css_t
index 996a6d22a..3edd869af 100644
--- a/sphinx/themes/scrolls/static/scrolls.css_t
+++ b/sphinx/themes/scrolls/static/scrolls.css_t
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- scrolls theme.
*
- * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/sphinxdoc/layout.html b/sphinx/themes/sphinxdoc/layout.html
index b37567bf8..91349c970 100644
--- a/sphinx/themes/sphinxdoc/layout.html
+++ b/sphinx/themes/sphinxdoc/layout.html
@@ -4,7 +4,7 @@
Sphinx layout template for the sphinxdoc theme.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- extends "basic/layout.html" %}
diff --git a/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t b/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t
index 3b88e888e..2f4275a6c 100644
--- a/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t
+++ b/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t
@@ -5,7 +5,7 @@
* Sphinx stylesheet -- sphinxdoc theme. Originally created by
* Armin Ronacher for Werkzeug.
*
- * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/traditional/static/traditional.css_t b/sphinx/themes/traditional/static/traditional.css_t
index fb0ab54c9..e5fda3bab 100644
--- a/sphinx/themes/traditional/static/traditional.css_t
+++ b/sphinx/themes/traditional/static/traditional.css_t
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- traditional docs.python.org theme.
*
- * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/theming.py b/sphinx/theming.py
index 78c73b63f..33c4c76be 100644
--- a/sphinx/theming.py
+++ b/sphinx/theming.py
@@ -5,7 +5,7 @@
Theming support for HTML builders.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/transforms/__init__.py b/sphinx/transforms/__init__.py
index 0ceced214..ceb8de364 100644
--- a/sphinx/transforms/__init__.py
+++ b/sphinx/transforms/__init__.py
@@ -5,10 +5,12 @@
Docutils transforms used by Sphinx when reading documents.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
+import re
+
from docutils import nodes
from docutils.transforms import Transform, Transformer
from docutils.transforms.parts import ContentsFilter
@@ -346,5 +348,23 @@ class SphinxSmartQuotes(SmartQuotes):
texttype = {True: 'literal', # "literal" text is not changed:
False: 'plain'}
for txtnode in txtnodes:
- smartquotable = not is_smartquotable(txtnode)
- yield (texttype[smartquotable], txtnode.astext())
+ notsmartquotable = not is_smartquotable(txtnode)
+ yield (texttype[notsmartquotable], txtnode.astext())
+
+
+class ManpageLink(SphinxTransform):
+ """Find manpage section numbers and names"""
+ default_priority = 999
+
+ def apply(self):
+ for node in self.document.traverse(addnodes.manpage):
+ manpage = ' '.join([str(x) for x in node.children
+ if isinstance(x, nodes.Text)])
+ pattern = r'^(?P<path>(?P<page>.+)[\(\.](?P<section>[1-9]\w*)?\)?)$' # noqa
+ info = {'path': manpage,
+ 'page': manpage,
+ 'section': ''}
+ r = re.match(pattern, manpage)
+ if r:
+ info = r.groupdict()
+ node.attributes.update(info)
diff --git a/sphinx/transforms/compact_bullet_list.py b/sphinx/transforms/compact_bullet_list.py
index 006ae7161..0121dd12f 100644
--- a/sphinx/transforms/compact_bullet_list.py
+++ b/sphinx/transforms/compact_bullet_list.py
@@ -5,7 +5,7 @@
Docutils transforms used by Sphinx when reading documents.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -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/transforms/i18n.py b/sphinx/transforms/i18n.py
index 4c1fbc2a7..5ae33d86a 100644
--- a/sphinx/transforms/i18n.py
+++ b/sphinx/transforms/i18n.py
@@ -5,7 +5,7 @@
Docutils transforms used by Sphinx when reading documents.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -50,15 +50,12 @@ def publish_msgstr(app, source, source_path, source_line, config, settings):
:rtype: docutils.nodes.document
"""
from sphinx.io import SphinxI18nReader
- reader = SphinxI18nReader(
- app=app,
- parsers=app.registry.get_source_parsers(),
- parser_name='restructuredtext', # default parser
- )
+ reader = SphinxI18nReader()
reader.set_lineno_for_reporter(source_line)
+ parser = app.registry.create_source_parser(app, '')
doc = reader.read(
source=StringInput(source=source, source_path=source_path),
- parser=reader.parser,
+ parser=parser,
settings=settings,
)
try:
diff --git a/sphinx/transforms/post_transforms/__init__.py b/sphinx/transforms/post_transforms/__init__.py
index a3f742e53..b65d929e2 100644
--- a/sphinx/transforms/post_transforms/__init__.py
+++ b/sphinx/transforms/post_transforms/__init__.py
@@ -5,7 +5,7 @@
Docutils transforms used by Sphinx.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/transforms/post_transforms/images.py b/sphinx/transforms/post_transforms/images.py
index 788684e40..d09f57e67 100644
--- a/sphinx/transforms/post_transforms/images.py
+++ b/sphinx/transforms/post_transforms/images.py
@@ -5,7 +5,7 @@
Docutils transforms used by Sphinx.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py
index 03f8ce6a3..938ec71db 100644
--- a/sphinx/util/__init__.py
+++ b/sphinx/util/__init__.py
@@ -5,7 +5,7 @@
Utility functions for Sphinx.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import absolute_import
@@ -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
@@ -564,16 +562,6 @@ def encode_uri(uri):
return urlunsplit(split)
-def split_docinfo(text):
- # type: (unicode) -> Sequence[unicode]
- docinfo_re = re.compile('\\A((?:\\s*:\\w+:.*?\n(?:[ \\t]+.*?\n)*)+)', re.M)
- result = docinfo_re.split(text, 1) # type: ignore
- if len(result) == 1:
- return '', result[0]
- else:
- return result[1:]
-
-
def display_chunk(chunk):
# type: (Any) -> unicode
if isinstance(chunk, (list, tuple)):
diff --git a/sphinx/util/console.py b/sphinx/util/console.py
index 63a619f55..8069dd9c9 100644
--- a/sphinx/util/console.py
+++ b/sphinx/util/console.py
@@ -5,7 +5,7 @@
Format colored console output.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py
index 4bce071c0..2f952d7cc 100644
--- a/sphinx/util/docfields.py
+++ b/sphinx/util/docfields.py
@@ -6,7 +6,7 @@
"Doc fields" are reST field lists in object descriptions that will
be domain-specifically transformed to a more appealing presentation.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import absolute_import
diff --git a/sphinx/util/docstrings.py b/sphinx/util/docstrings.py
index c2ef91a66..bc4b96a56 100644
--- a/sphinx/util/docstrings.py
+++ b/sphinx/util/docstrings.py
@@ -5,7 +5,7 @@
Utilities for docstring processing.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py
index 92e6c8c22..bfaff758f 100644
--- a/sphinx/util/docutils.py
+++ b/sphinx/util/docutils.py
@@ -5,7 +5,7 @@
Utility functions for docutils.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import absolute_import
@@ -18,8 +18,9 @@ from contextlib import contextmanager
import docutils
from docutils.languages import get_language
-from docutils.utils import Reporter
+from docutils.statemachine import StateMachine, ViewList
from docutils.parsers.rst import directives, roles, convert_directive_function
+from docutils.utils import Reporter
from sphinx.errors import ExtensionError
from sphinx.locale import __
@@ -30,9 +31,11 @@ report_re = re.compile('^(.+?:(?:\\d+)?): \\((DEBUG|INFO|WARNING|ERROR|SEVERE)/(
if False:
# For type annotation
- from typing import Any, Callable, Iterator, List, Tuple # NOQA
+ from typing import Any, Callable, Generator, Iterator, List, Tuple # NOQA
from docutils import nodes # NOQA
+ from docutils.statemachine import State # NOQA
from sphinx.environment import BuildEnvironment # NOQA
+ from sphinx.io import SphinxFileInput # NOQA
__version_info__ = tuple(LooseVersion(docutils.__version__).version)
@@ -167,16 +170,34 @@ class WarningStream(object):
class LoggingReporter(Reporter):
+ @classmethod
+ def from_reporter(cls, reporter):
+ # type: (Reporter) -> LoggingReporter
+ """Create an instance of LoggingReporter from other reporter object."""
+ return cls(reporter.source, reporter.report_level, reporter.halt_level,
+ reporter.debug_flag, reporter.error_handler)
+
def __init__(self, source, report_level, halt_level,
debug=False, error_handler='backslashreplace'):
# type: (unicode, int, int, bool, unicode) -> None
stream = WarningStream()
Reporter.__init__(self, source, report_level, halt_level,
stream, debug, error_handler=error_handler)
+ self.source_and_line = None # type: SphinxFileInput
+
+ def set_source(self, source):
+ # type: (SphinxFileInput) -> None
+ self.source_and_line = source
- def set_conditions(self, category, report_level, halt_level, debug=False):
- # type: (unicode, int, int, bool) -> None
- Reporter.set_conditions(self, category, report_level, halt_level, debug=debug)
+ def system_message(self, *args, **kwargs):
+ # type: (Any, Any) -> Any
+ if kwargs.get('line') and isinstance(self.source_and_line, ViewList):
+ # replace source parameter if source is set
+ source, lineno = self.source_and_line.info(kwargs.get('line'))
+ kwargs['source'] = source
+ kwargs['line'] = lineno
+
+ return Reporter.system_message(self, *args, **kwargs)
def is_html5_writer_available():
@@ -196,3 +217,22 @@ def directive_helper(obj, has_content=None, argument_spec=None, **option_spec):
raise ExtensionError(__('when adding directive classes, no '
'additional arguments may be given'))
return obj
+
+
+@contextmanager
+def switch_source_input(state, content):
+ # type: (State, ViewList) -> Generator
+ """Switch current source input of state temporarily."""
+ try:
+ # remember the original ``get_source_and_line()`` method
+ get_source_and_line = state.memo.reporter.get_source_and_line
+
+ # replace it by new one
+ state_machine = StateMachine([], None)
+ state_machine.input_lines = content
+ state.memo.reporter.get_source_and_line = state_machine.get_source_and_line
+
+ yield
+ finally:
+ # restore the method
+ state.memo.reporter.get_source_and_line = get_source_and_line
diff --git a/sphinx/util/fileutil.py b/sphinx/util/fileutil.py
index fe98117d2..3fd570273 100644
--- a/sphinx/util/fileutil.py
+++ b/sphinx/util/fileutil.py
@@ -5,7 +5,7 @@
File utility functions for Sphinx.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import absolute_import
diff --git a/sphinx/util/i18n.py b/sphinx/util/i18n.py
index 09b53b4a0..75a8506fa 100644
--- a/sphinx/util/i18n.py
+++ b/sphinx/util/i18n.py
@@ -5,7 +5,7 @@
Builder superclass for all builders.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import gettext
diff --git a/sphinx/util/images.py b/sphinx/util/images.py
index 1c2b4033a..46187775d 100644
--- a/sphinx/util/images.py
+++ b/sphinx/util/images.py
@@ -5,7 +5,7 @@
Image utility functions for Sphinx.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import absolute_import
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index 2d15c2883..8c10b7aa5 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -5,7 +5,7 @@
Helpers for inspecting Python modules.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import absolute_import
@@ -404,10 +404,18 @@ class Signature(object):
if annotation == Ellipsis:
return '...'
if not isinstance(annotation, type):
- return repr(annotation)
+ qualified_name = repr(annotation)
+ if qualified_name.startswith('typing.'): # for typing.Union
+ return qualified_name.split('.', 1)[1]
+ else:
+ return qualified_name
- qualified_name = (annotation.__module__ + '.' + annotation.__qualname__ # type: ignore
- if annotation else repr(annotation))
+ if not annotation:
+ qualified_name = repr(annotation)
+ elif annotation.__module__ == 'typing':
+ qualified_name = annotation.__qualname__ # type: ignore
+ else:
+ qualified_name = (annotation.__module__ + '.' + annotation.__qualname__) # type: ignore # NOQA
if annotation.__module__ == 'builtins':
return annotation.__qualname__ # type: ignore
@@ -434,7 +442,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 +450,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 +460,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))
diff --git a/sphinx/util/inventory.py b/sphinx/util/inventory.py
index 40c0dc648..837188b5a 100644
--- a/sphinx/util/inventory.py
+++ b/sphinx/util/inventory.py
@@ -5,7 +5,7 @@
Inventory utility functions for Sphinx.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
diff --git a/sphinx/util/jsdump.py b/sphinx/util/jsdump.py
index 73aa2ce03..6776691cf 100644
--- a/sphinx/util/jsdump.py
+++ b/sphinx/util/jsdump.py
@@ -6,7 +6,7 @@
This module implements a simple JavaScript serializer.
Uses the basestring encode function from simplejson by Bob Ippolito.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/jsonimpl.py b/sphinx/util/jsonimpl.py
index 09c04dc6a..fbaa72978 100644
--- a/sphinx/util/jsonimpl.py
+++ b/sphinx/util/jsonimpl.py
@@ -5,7 +5,7 @@
JSON serializer implementation wrapper.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/logging.py b/sphinx/util/logging.py
index 41dc6022f..04bf91830 100644
--- a/sphinx/util/logging.py
+++ b/sphinx/util/logging.py
@@ -5,7 +5,7 @@
Logging utility functions for Sphinx.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import absolute_import
@@ -53,7 +53,7 @@ VERBOSITY_MAP.update({
COLOR_MAP = defaultdict(lambda: 'blue') # type: Dict[int, unicode]
COLOR_MAP.update({
logging.ERROR: 'darkred',
- logging.WARNING: 'darkred',
+ logging.WARNING: 'red',
logging.DEBUG: 'darkgray',
})
@@ -82,6 +82,10 @@ def convert_serializable(records):
r.msg = r.getMessage()
r.args = ()
+ location = getattr(r, 'location', None)
+ if isinstance(location, nodes.Node):
+ r.location = get_node_location(location) # type: ignore
+
class SphinxWarningLogRecord(logging.LogRecord):
"""Log record class supporting location"""
@@ -152,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:
@@ -415,21 +419,26 @@ class WarningLogRecordTranslator(logging.Filter):
else:
record.location = None
elif isinstance(location, nodes.Node):
- (source, line) = get_source_line(location)
- if source and line:
- record.location = "%s:%s" % (source, line)
- elif source:
- record.location = "%s:" % source
- elif line:
- record.location = "<unknown>:%s" % line
- else:
- record.location = None
+ record.location = get_node_location(location)
elif location and ':' not in location:
record.location = '%s' % self.app.env.doc2path(location)
return True
+def get_node_location(node):
+ # type: (nodes.Node) -> str
+ (source, line) = get_source_line(node)
+ if source and line:
+ return "%s:%s" % (source, line)
+ elif source:
+ return "%s:" % source
+ elif line:
+ return "<unknown>:%s" % line
+ else:
+ return None
+
+
class ColorizeFormatter(logging.Formatter):
def format(self, record):
# type: (logging.LogRecord) -> str
diff --git a/sphinx/util/matching.py b/sphinx/util/matching.py
index 401f5f002..bddf84f5c 100644
--- a/sphinx/util/matching.py
+++ b/sphinx/util/matching.py
@@ -5,7 +5,7 @@
Pattern-matching utility functions for Sphinx.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py
index cf57edb07..4ff4937b9 100644
--- a/sphinx/util/nodes.py
+++ b/sphinx/util/nodes.py
@@ -5,7 +5,7 @@
Docutils node-related utility functions for Sphinx.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import absolute_import
diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py
index a8bff11c4..b38e58e5d 100644
--- a/sphinx/util/osutil.py
+++ b/sphinx/util/osutil.py
@@ -5,7 +5,7 @@
Operating system-related utility functions for Sphinx.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
diff --git a/sphinx/util/parallel.py b/sphinx/util/parallel.py
index 9bc3c36e1..6340e4dfc 100644
--- a/sphinx/util/parallel.py
+++ b/sphinx/util/parallel.py
@@ -5,7 +5,7 @@
Parallel building utilities.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/png.py b/sphinx/util/png.py
index cc4447e4e..d22839fbf 100644
--- a/sphinx/util/png.py
+++ b/sphinx/util/png.py
@@ -5,7 +5,7 @@
PNG image manipulation helpers.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py
index 7f7ee4e9b..e1a2bad9a 100644
--- a/sphinx/util/pycompat.py
+++ b/sphinx/util/pycompat.py
@@ -5,7 +5,7 @@
Stuff for Python version compatibility.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/requests.py b/sphinx/util/requests.py
index fb8761481..4bd4c042e 100644
--- a/sphinx/util/requests.py
+++ b/sphinx/util/requests.py
@@ -5,7 +5,7 @@
Simple requests package loader
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/rst.py b/sphinx/util/rst.py
index 6977cda96..5860b0fd5 100644
--- a/sphinx/util/rst.py
+++ b/sphinx/util/rst.py
@@ -5,7 +5,7 @@
reST helper functions.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import absolute_import
diff --git a/sphinx/util/stemmer/__init__.py b/sphinx/util/stemmer/__init__.py
index a41373a81..a10da7370 100644
--- a/sphinx/util/stemmer/__init__.py
+++ b/sphinx/util/stemmer/__init__.py
@@ -5,7 +5,7 @@
Word stemming utilities for Sphinx.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/tags.py b/sphinx/util/tags.py
index 24f64bece..2c4855e91 100644
--- a/sphinx/util/tags.py
+++ b/sphinx/util/tags.py
@@ -3,7 +3,7 @@
sphinx.util.tags
~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/template.py b/sphinx/util/template.py
index 87e81d823..a78871349 100644
--- a/sphinx/util/template.py
+++ b/sphinx/util/template.py
@@ -5,7 +5,7 @@
Templates utility functions for Sphinx.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/texescape.py b/sphinx/util/texescape.py
index 07f5390c4..8d37e0f60 100644
--- a/sphinx/util/texescape.py
+++ b/sphinx/util/texescape.py
@@ -5,7 +5,7 @@
TeX escaping helper.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py
index d30cc230a..793504b77 100644
--- a/sphinx/util/typing.py
+++ b/sphinx/util/typing.py
@@ -5,7 +5,7 @@
The composit types for Sphinx.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/websupport.py b/sphinx/util/websupport.py
index 4d91cb77c..59133b9e1 100644
--- a/sphinx/util/websupport.py
+++ b/sphinx/util/websupport.py
@@ -3,7 +3,7 @@
sphinx.util.websupport
~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/versioning.py b/sphinx/versioning.py
index 1e7c452bd..953ef4f6b 100644
--- a/sphinx/versioning.py
+++ b/sphinx/versioning.py
@@ -6,7 +6,7 @@
Implements the low-level algorithms Sphinx uses for the versioning of
doctrees.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from uuid import uuid4
diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py
index 528343f8c..51d906fa6 100644
--- a/sphinx/websupport/__init__.py
+++ b/sphinx/websupport/__init__.py
@@ -5,7 +5,7 @@
Base Module for web support functions.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/websupport/errors.py b/sphinx/websupport/errors.py
index 587d7e7e7..7456659ec 100644
--- a/sphinx/websupport/errors.py
+++ b/sphinx/websupport/errors.py
@@ -5,7 +5,7 @@
Contains Error classes for the web support package.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/websupport/search/__init__.py b/sphinx/websupport/search/__init__.py
index 0f90e009b..e1e871ba0 100644
--- a/sphinx/websupport/search/__init__.py
+++ b/sphinx/websupport/search/__init__.py
@@ -5,7 +5,7 @@
Server side search support for the web support package.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/websupport/search/nullsearch.py b/sphinx/websupport/search/nullsearch.py
index afae1ca57..422b398c9 100644
--- a/sphinx/websupport/search/nullsearch.py
+++ b/sphinx/websupport/search/nullsearch.py
@@ -5,7 +5,7 @@
The default search adapter, does nothing.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/websupport/search/whooshsearch.py b/sphinx/websupport/search/whooshsearch.py
index f007c3cdc..94cce8ed7 100644
--- a/sphinx/websupport/search/whooshsearch.py
+++ b/sphinx/websupport/search/whooshsearch.py
@@ -5,7 +5,7 @@
Whoosh search adapter.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/websupport/search/xapiansearch.py b/sphinx/websupport/search/xapiansearch.py
index 23be038e5..4df4769e2 100644
--- a/sphinx/websupport/search/xapiansearch.py
+++ b/sphinx/websupport/search/xapiansearch.py
@@ -5,7 +5,7 @@
Xapian search adapter.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/websupport/storage/__init__.py b/sphinx/websupport/storage/__init__.py
index adfdec4a5..727e86da4 100644
--- a/sphinx/websupport/storage/__init__.py
+++ b/sphinx/websupport/storage/__init__.py
@@ -5,7 +5,7 @@
Storage for the websupport package.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/websupport/storage/differ.py b/sphinx/websupport/storage/differ.py
index 449d038da..1358d8645 100644
--- a/sphinx/websupport/storage/differ.py
+++ b/sphinx/websupport/storage/differ.py
@@ -5,7 +5,7 @@
A differ for creating an HTML representations of proposal diffs
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/websupport/storage/sqlalchemy_db.py b/sphinx/websupport/storage/sqlalchemy_db.py
index a2dfc35b9..e1c86dd9d 100644
--- a/sphinx/websupport/storage/sqlalchemy_db.py
+++ b/sphinx/websupport/storage/sqlalchemy_db.py
@@ -6,7 +6,7 @@
SQLAlchemy table and mapper definitions used by the
:class:`sphinx.websupport.storage.sqlalchemystorage.SQLAlchemyStorage`.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py
index dc5e9400b..b018ea0a3 100644
--- a/sphinx/websupport/storage/sqlalchemystorage.py
+++ b/sphinx/websupport/storage/sqlalchemystorage.py
@@ -5,7 +5,7 @@
An SQLAlchemy storage backend.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/writers/__init__.py b/sphinx/writers/__init__.py
index 6b157a83a..79eacbbfb 100644
--- a/sphinx/writers/__init__.py
+++ b/sphinx/writers/__init__.py
@@ -5,6 +5,6 @@
Custom docutils writers.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py
index b3419b70a..84e7bfbc9 100644
--- a/sphinx/writers/html.py
+++ b/sphinx/writers/html.py
@@ -5,7 +5,7 @@
docutils writers handling Sphinx' custom nodes.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -79,6 +79,7 @@ class HTMLTranslator(BaseTranslator):
self.highlightopts = builder.config.highlight_options
self.highlightlinenothreshold = sys.maxsize
self.docnames = [builder.current_docname] # for singlehtml builder
+ self.manpages_url = builder.config.manpages_url
self.protect_literal_text = 0
self.permalink_text = builder.config.html_add_permalinks
# support backwards-compatible setting to a bool
@@ -816,9 +817,14 @@ class HTMLTranslator(BaseTranslator):
def visit_manpage(self, node):
# type: (nodes.Node) -> None
self.visit_literal_emphasis(node)
+ if self.manpages_url:
+ node['refuri'] = self.manpages_url.format(**node.attributes)
+ self.visit_reference(node)
def depart_manpage(self, node):
# type: (nodes.Node) -> None
+ if self.manpages_url:
+ self.depart_reference(node)
self.depart_literal_emphasis(node)
# overwritten to add even/odd classes
diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py
index 1efd060f2..50bf2ea8c 100644
--- a/sphinx/writers/html5.py
+++ b/sphinx/writers/html5.py
@@ -5,7 +5,7 @@
Experimental docutils writers for HTML5 handling Sphinx' custom nodes.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -49,6 +49,7 @@ class HTML5Translator(BaseTranslator):
self.highlightopts = builder.config.highlight_options
self.highlightlinenothreshold = sys.maxsize
self.docnames = [builder.current_docname] # for singlehtml builder
+ self.manpages_url = builder.config.manpages_url
self.protect_literal_text = 0
self.permalink_text = builder.config.html_add_permalinks
# support backwards-compatible setting to a bool
@@ -758,9 +759,14 @@ class HTML5Translator(BaseTranslator):
def visit_manpage(self, node):
# type: (nodes.Node) -> None
self.visit_literal_emphasis(node)
+ if self.manpages_url:
+ node['refuri'] = self.manpages_url.format(**dict(node))
+ self.visit_reference(node)
def depart_manpage(self, node):
# type: (nodes.Node) -> None
+ if self.manpages_url:
+ self.depart_reference(node)
self.depart_literal_emphasis(node)
# overwritten to add even/odd classes
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index e48905757..6c86e6174 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -8,7 +8,7 @@
Much of this code is adapted from Dave Kuhlman's "docpy" writer from his
docutils sandbox.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -48,7 +48,8 @@ BEGIN_DOC = r'''
URI_SCHEMES = ('mailto:', 'http:', 'https:', 'ftp:')
-SECNUMDEPTH = 3
+LATEXSECTIONNAMES = ["part", "chapter", "section", "subsection",
+ "subsubsection", "paragraph", "subparagraph"]
DEFAULT_SETTINGS = {
'latex_engine': 'pdflatex',
@@ -501,9 +502,9 @@ def rstdim_to_latexdim(width_str):
class LaTeXTranslator(nodes.NodeVisitor):
- sectionnames = ["part", "chapter", "section", "subsection",
- "subsubsection", "paragraph", "subparagraph"]
+ secnumdepth = 2 # legacy sphinxhowto.cls uses this, whereas article.cls
+ # default is originally 3. For book/report, 2 is already LaTeX default.
ignore_missing_images = False
# sphinx specific document classes
@@ -532,16 +533,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.compact_list = 0
self.first_param = 0
- # determine top section level
- if builder.config.latex_toplevel_sectioning:
- self.top_sectionlevel = \
- self.sectionnames.index(builder.config.latex_toplevel_sectioning)
- else:
- if document.settings.docclass == 'howto':
- self.top_sectionlevel = 2
- else:
- self.top_sectionlevel = 1
-
# sort out some elements
self.elements = DEFAULT_SETTINGS.copy()
self.elements.update(ADDITIONAL_SETTINGS.get(builder.config.latex_engine, {}))
@@ -558,20 +549,64 @@ class LaTeXTranslator(nodes.NodeVisitor):
'author': document.settings.author, # treat as a raw LaTeX code
'indexname': _('Index'),
})
- if not self.elements['releasename']:
+ if not self.elements['releasename'] and self.elements['release']:
self.elements.update({
'releasename': _('Release'),
})
+
+ # we assume LaTeX class provides \chapter command except in case
+ # of non-Japanese 'howto' case
+ self.sectionnames = LATEXSECTIONNAMES[:]
if document.settings.docclass == 'howto':
docclass = builder.config.latex_docclass.get('howto', 'article')
+ if docclass[0] == 'j': # Japanese class...
+ pass
+ else:
+ self.sectionnames.remove('chapter')
else:
docclass = builder.config.latex_docclass.get('manual', 'report')
self.elements['docclass'] = docclass
+
+ # determine top section level
+ self.top_sectionlevel = 1
+ if builder.config.latex_toplevel_sectioning:
+ try:
+ self.top_sectionlevel = \
+ self.sectionnames.index(builder.config.latex_toplevel_sectioning)
+ except ValueError:
+ logger.warning('unknown %r toplevel_sectioning for class %r' %
+ (builder.config.latex_toplevel_sectioning, docclass))
+
if builder.config.today:
self.elements['date'] = builder.config.today
else:
self.elements['date'] = format_date(builder.config.today_fmt or _('%b %d, %Y'), # type: ignore # NOQA
language=builder.config.language)
+
+ if builder.config.numfig:
+ self.numfig_secnum_depth = builder.config.numfig_secnum_depth
+ if self.numfig_secnum_depth > 0: # default is 1
+ # numfig_secnum_depth as passed to sphinx.sty indices same names as in
+ # LATEXSECTIONNAMES but with -1 for part, 0 for chapter, 1 for section...
+ if len(self.sectionnames) < len(LATEXSECTIONNAMES) and \
+ self.top_sectionlevel > 0:
+ self.numfig_secnum_depth += self.top_sectionlevel
+ else:
+ self.numfig_secnum_depth += self.top_sectionlevel - 1
+ # this (minus one) will serve as minimum to LaTeX's secnumdepth
+ self.numfig_secnum_depth = min(self.numfig_secnum_depth,
+ len(LATEXSECTIONNAMES) - 1)
+ # if passed key value is < 1 LaTeX will act as if 0; see sphinx.sty
+ self.elements['sphinxpkgoptions'] += \
+ (',numfigreset=%s' % self.numfig_secnum_depth)
+ else:
+ self.elements['sphinxpkgoptions'] += ',nonumfigreset'
+ try:
+ if builder.config.math_numfig:
+ self.elements['sphinxpkgoptions'] += ',mathnumfig'
+ except AttributeError:
+ pass
+
if builder.config.latex_logo:
# no need for \\noindent here, used in flushright
self.elements['logo'] = '\\sphinxincludegraphics{%s}\\par' % \
@@ -628,23 +663,32 @@ class LaTeXTranslator(nodes.NodeVisitor):
return '\\usepackage{%s}' % (packagename,)
usepackages = (declare_package(*p) for p in builder.usepackages)
self.elements['usepackages'] += "\n".join(usepackages)
+
+ minsecnumdepth = self.secnumdepth # 2 from legacy sphinx manual/howto
if document.get('tocdepth'):
- # redece tocdepth if `part` or `chapter` is used for top_sectionlevel
+ # reduce tocdepth if `part` or `chapter` is used for top_sectionlevel
# tocdepth = -1: show only parts
# tocdepth = 0: show parts and chapters
# tocdepth = 1: show parts, chapters and sections
# tocdepth = 2: show parts, chapters, sections and subsections
# ...
tocdepth = document['tocdepth'] + self.top_sectionlevel - 2
- maxdepth = len(self.sectionnames) - self.top_sectionlevel
- if tocdepth > maxdepth:
+ if len(self.sectionnames) < len(LATEXSECTIONNAMES) and \
+ self.top_sectionlevel > 0:
+ tocdepth += 1 # because top_sectionlevel is shifted by -1
+ if tocdepth > len(LATEXSECTIONNAMES) - 2: # default is 5 <-> subparagraph
logger.warning('too large :maxdepth:, ignored.')
- tocdepth = maxdepth
+ tocdepth = len(LATEXSECTIONNAMES) - 2
self.elements['tocdepth'] = '\\setcounter{tocdepth}{%d}' % tocdepth
- if tocdepth >= SECNUMDEPTH:
- # Increase secnumdepth if tocdepth is depther than default SECNUMDEPTH
- self.elements['secnumdepth'] = '\\setcounter{secnumdepth}{%d}' % tocdepth
+ minsecnumdepth = max(minsecnumdepth, tocdepth)
+
+ if builder.config.numfig and (builder.config.numfig_secnum_depth > 0):
+ minsecnumdepth = max(minsecnumdepth, self.numfig_secnum_depth - 1)
+
+ if minsecnumdepth > self.secnumdepth:
+ self.elements['secnumdepth'] = '\\setcounter{secnumdepth}{%d}' %\
+ minsecnumdepth
if getattr(document.settings, 'contentsname', None):
self.elements['contentsname'] = \
@@ -1172,12 +1216,12 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_desc_addname(self, node):
# type: (nodes.Node) -> None
- self.body.append(r'\sphinxcode{')
+ self.body.append(r'\sphinxcode{\sphinxupquote{')
self.literal_whitespace += 1
def depart_desc_addname(self, node):
# type: (nodes.Node) -> None
- self.body.append('}')
+ self.body.append('}}')
self.literal_whitespace -= 1
def visit_desc_type(self, node):
@@ -1198,13 +1242,13 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_desc_name(self, node):
# type: (nodes.Node) -> None
- self.body.append(r'\sphinxbfcode{')
+ self.body.append(r'\sphinxbfcode{\sphinxupquote{')
self.no_contractions += 1
self.literal_whitespace += 1
def depart_desc_name(self, node):
# type: (nodes.Node) -> None
- self.body.append('}')
+ self.body.append('}}')
self.literal_whitespace -= 1
self.no_contractions -= 1
@@ -1243,11 +1287,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_desc_annotation(self, node):
# type: (nodes.Node) -> None
- self.body.append(r'\sphinxbfcode{')
+ self.body.append(r'\sphinxbfcode{\sphinxupquote{')
def depart_desc_annotation(self, node):
# type: (nodes.Node) -> None
- self.body.append('}')
+ self.body.append('}}')
def visit_desc_content(self, node):
# type: (nodes.Node) -> None
@@ -2133,12 +2177,12 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_literal_emphasis(self, node):
# type: (nodes.Node) -> None
- self.body.append(r'\sphinxstyleliteralemphasis{')
+ self.body.append(r'\sphinxstyleliteralemphasis{\sphinxupquote{')
self.no_contractions += 1
def depart_literal_emphasis(self, node):
# type: (nodes.Node) -> None
- self.body.append('}')
+ self.body.append('}}')
self.no_contractions -= 1
def visit_strong(self, node):
@@ -2151,12 +2195,12 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_literal_strong(self, node):
# type: (nodes.Node) -> None
- self.body.append(r'\sphinxstyleliteralstrong{')
+ self.body.append(r'\sphinxstyleliteralstrong{\sphinxupquote{')
self.no_contractions += 1
def depart_literal_strong(self, node):
# type: (nodes.Node) -> None
- self.body.append('}')
+ self.body.append('}}')
self.no_contractions -= 1
def visit_abbreviation(self, node):
@@ -2215,14 +2259,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
# type: (nodes.Node) -> None
self.no_contractions += 1
if self.in_title:
- self.body.append(r'\sphinxstyleliteralintitle{')
+ self.body.append(r'\sphinxstyleliteralintitle{\sphinxupquote{')
else:
- self.body.append(r'\sphinxcode{')
+ self.body.append(r'\sphinxcode{\sphinxupquote{')
def depart_literal(self, node):
# type: (nodes.Node) -> None
self.no_contractions -= 1
- self.body.append('}')
+ self.body.append('}}')
def visit_footnote_reference(self, node):
# type: (nodes.Node) -> None
@@ -2268,6 +2312,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
lang = self.hlsettingstack[-1][0]
linenos = code.count('\n') >= self.hlsettingstack[-1][1] - 1
highlight_args = node.get('highlight_args', {})
+ hllines = '\\fvset{hllines={, %s,}}%%' %\
+ str(highlight_args.get('hl_lines', []))[1:-1]
if 'language' in node:
# code-block directives
lang = node['language']
@@ -2306,7 +2352,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
hlcode += '\\end{sphinxVerbatimintable}'
else:
hlcode += '\\end{sphinxVerbatim}'
- self.body.append('\n' + hlcode + '\n')
+ self.body.append('\n' + hllines + '\n' + hlcode + '\n')
raise nodes.SkipNode
def depart_literal_block(self, node):
diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py
index 71c2aac0b..1d645ce5f 100644
--- a/sphinx/writers/manpage.py
+++ b/sphinx/writers/manpage.py
@@ -5,7 +5,7 @@
Manual page writer, extended for Sphinx custom nodes.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py
index d7e08510e..b73557f86 100644
--- a/sphinx/writers/texinfo.py
+++ b/sphinx/writers/texinfo.py
@@ -5,7 +5,7 @@
Custom docutils writer for Texinfo.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py
index d2b2f9045..b6e3f4cec 100644
--- a/sphinx/writers/text.py
+++ b/sphinx/writers/text.py
@@ -5,7 +5,7 @@
Custom docutils writer for plain text.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os
diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py
index 1e7f4babd..a962faf4d 100644
--- a/sphinx/writers/websupport.py
+++ b/sphinx/writers/websupport.py
@@ -5,7 +5,7 @@
sphinx.websupport writer that adds comment-related annotations.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/writers/xml.py b/sphinx/writers/xml.py
index 9cb64216a..f94fe847c 100644
--- a/sphinx/writers/xml.py
+++ b/sphinx/writers/xml.py
@@ -5,7 +5,7 @@
Docutils-native XML and pseudo-XML writers.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/conftest.py b/tests/conftest.py
index 28dbd6ed4..9fb06edab 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -3,18 +3,51 @@
pytest config for sphinx/tests
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os
+import shutil
+import sys
+import warnings
import pytest
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']
+
@pytest.fixture(scope='session')
def rootdir():
return path(os.path.dirname(__file__) or '.').abspath() / 'roots'
+
+
+def pytest_report_header(config):
+ return 'Running Sphinx test suite (with Python %s)...' % (
+ sys.version.split()[0])
+
+
+def _initialize_test_directory(session):
+ testroot = os.path.join(str(session.config.rootdir), 'tests')
+ tempdir = os.path.abspath(os.getenv('SPHINX_TEST_TEMPDIR',
+ os.path.join(testroot, 'build')))
+ os.environ['SPHINX_TEST_TEMPDIR'] = tempdir
+
+ print('Temporary files will be placed in %s.' % tempdir)
+
+ if os.path.exists(tempdir):
+ shutil.rmtree(tempdir)
+
+ os.makedirs(tempdir)
+
+
+def pytest_sessionstart(session):
+ _initialize_test_directory(session)
diff --git a/tests/py35/test_autodoc_py35.py b/tests/py35/test_autodoc_py35.py
index 481374948..439ebd67a 100644
--- a/tests/py35/test_autodoc_py35.py
+++ b/tests/py35/test_autodoc_py35.py
@@ -6,7 +6,7 @@
Test the autodoc extension. This tests mainly the Documenters; the auto
directives are tested in a test source file translated by test_build.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -21,6 +21,7 @@ from docutils.statemachine import ViewList
from sphinx.ext.autodoc import AutoDirective, add_documenter, \
ModuleLevelDocumenter, FunctionDocumenter, cut_lines, between, ALL
+from sphinx.util import logging
app = None
@@ -30,7 +31,7 @@ def setup_module(rootdir, sphinx_test_tempdir):
global app
srcdir = sphinx_test_tempdir / 'autodoc-root'
if not srcdir.exists():
- (rootdir/'test-root').copytree(srcdir)
+ (rootdir / 'test-root').copytree(srcdir)
app = SphinxTestApp(srcdir=srcdir)
app.builder.env.app = app
app.builder.env.temp_data['docname'] = 'dummy'
@@ -47,7 +48,7 @@ directive = options = None
@pytest.fixture
def setup_test():
global options, directive
- global processed_docstrings, processed_signatures, _warnings
+ global processed_docstrings, processed_signatures
options = Struct(
inherited_members = False,
@@ -70,24 +71,17 @@ def setup_test():
env = app.builder.env,
genopt = options,
result = ViewList(),
- warn = warnfunc,
filename_set = set(),
)
processed_docstrings = []
processed_signatures = []
- _warnings = []
-_warnings = []
processed_docstrings = []
processed_signatures = []
-def warnfunc(msg):
- _warnings.append(msg)
-
-
def process_docstring(app, what, name, obj, options, lines):
processed_docstrings.append((what, name))
if name == 'bar':
@@ -111,20 +105,21 @@ def skip_member(app, what, name, obj, skip, options):
@pytest.mark.usefixtures('setup_test')
def test_generate():
+ logging.setup(app, app._status, app._warning)
+
def assert_warns(warn_str, objtype, name, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
assert len(directive.result) == 0, directive.result
- assert len(_warnings) == 1, _warnings
- assert warn_str in _warnings[0], _warnings
- del _warnings[:]
+ assert warn_str in app._warning.getvalue()
+ app._warning.truncate(0)
def assert_works(objtype, name, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
assert directive.result
# print '\n'.join(directive.result)
- assert len(_warnings) == 0, _warnings
+ assert app._warning.getvalue() == ''
del directive.result[:]
def assert_processes(items, objtype, name, **kw):
@@ -134,18 +129,18 @@ def test_generate():
assert set(processed_docstrings) | set(processed_signatures) == set(items)
def assert_result_contains(item, objtype, name, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
# print '\n'.join(directive.result)
- assert len(_warnings) == 0, _warnings
+ assert app._warning.getvalue() == ''
assert item in directive.result
del directive.result[:]
def assert_order(items, objtype, name, member_order, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.options.member_order = member_order
inst.generate(**kw)
- assert len(_warnings) == 0, _warnings
+ assert app._warning.getvalue() == ''
items = list(reversed(items))
lineiter = iter(directive.result)
# for line in directive.result:
diff --git a/tests/roots/test-apidoc-toc/mypackage/__init__.py b/tests/roots/test-apidoc-toc/mypackage/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/roots/test-apidoc-toc/mypackage/__init__.py
diff --git a/tests/roots/test-apidoc-toc/mypackage/main.py b/tests/roots/test-apidoc-toc/mypackage/main.py
new file mode 100755
index 000000000..5d3da04b9
--- /dev/null
+++ b/tests/roots/test-apidoc-toc/mypackage/main.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+import os
+
+import mod_resource
+
+import mod_something
+
+
+if __name__ == "__main__":
+ print("Hello, world! -> something returns: {}".format(mod_something.something()))
+
+ res_path = \
+ os.path.join(os.path.dirname(mod_resource.__file__), 'resource.txt')
+ with open(res_path) as f:
+ text = f.read()
+ print("From mod_resource:resource.txt -> {}".format(text))
diff --git a/tests/roots/test-apidoc-toc/mypackage/no_init/foo.py b/tests/roots/test-apidoc-toc/mypackage/no_init/foo.py
new file mode 100644
index 000000000..ce059b276
--- /dev/null
+++ b/tests/roots/test-apidoc-toc/mypackage/no_init/foo.py
@@ -0,0 +1 @@
+MESSAGE="There's no __init__.py in this folder, hence we should be left out"
diff --git a/tests/roots/test-apidoc-toc/mypackage/resource/__init__.py b/tests/roots/test-apidoc-toc/mypackage/resource/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/roots/test-apidoc-toc/mypackage/resource/__init__.py
diff --git a/tests/roots/test-apidoc-toc/mypackage/resource/resource.txt b/tests/roots/test-apidoc-toc/mypackage/resource/resource.txt
new file mode 100644
index 000000000..5b64c924d
--- /dev/null
+++ b/tests/roots/test-apidoc-toc/mypackage/resource/resource.txt
@@ -0,0 +1 @@
+This is a text resource to be included in this otherwise empty module. No python contents here. \ No newline at end of file
diff --git a/tests/roots/test-apidoc-toc/mypackage/something/__init__.py b/tests/roots/test-apidoc-toc/mypackage/something/__init__.py
new file mode 100644
index 000000000..259184ba3
--- /dev/null
+++ b/tests/roots/test-apidoc-toc/mypackage/something/__init__.py
@@ -0,0 +1 @@
+"Subpackage Something" \ No newline at end of file
diff --git a/tests/roots/test-root/bom.po b/tests/roots/test-builder-gettext-dont-rebuild-mo/bom.po
index c6025eb1e..c6025eb1e 100644
--- a/tests/roots/test-root/bom.po
+++ b/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-directive-code/emphasize.rst b/tests/roots/test-directive-code/emphasize.rst
new file mode 100644
index 000000000..95db574ce
--- /dev/null
+++ b/tests/roots/test-directive-code/emphasize.rst
@@ -0,0 +1,7 @@
+Literal Includes with Highlighted Lines
+=======================================
+
+.. literalinclude:: target.py
+ :language: python
+ :emphasize-lines: 5-6, 13-15, 24-
+
diff --git a/tests/roots/test-domain-cpp/index.rst b/tests/roots/test-domain-cpp/index.rst
index 618e51037..2df5ec848 100644
--- a/tests/roots/test-domain-cpp/index.rst
+++ b/tests/roots/test-domain-cpp/index.rst
@@ -28,14 +28,20 @@ directives
An unscoped enum.
+ .. cpp:enumerator:: A
+
.. cpp:enum-class:: MyScopedEnum
A scoped enum.
+ .. cpp:enumerator:: B
+
.. cpp:enum-struct:: protected MyScopedVisibilityEnum : std::underlying_type<MySpecificEnum>::type
A scoped enum with non-default visibility, and with a specified underlying type.
+ .. cpp:enumerator:: B
+
.. cpp:function:: void paren_1(int, float)
.. cpp:function:: void paren_2(int, float)
diff --git a/tests/roots/test-ext-autodoc/target/__init__.py b/tests/roots/test-ext-autodoc/target/__init__.py
new file mode 100644
index 000000000..bd00bf183
--- /dev/null
+++ b/tests/roots/test-ext-autodoc/target/__init__.py
@@ -0,0 +1,225 @@
+# -*- coding: utf-8 -*-
+
+import enum
+from six import StringIO, add_metaclass
+from sphinx.ext.autodoc import add_documenter # NOQA
+
+
+__all__ = ['Class']
+
+#: documentation for the integer
+integer = 1
+
+
+def raises(exc, func, *args, **kwds):
+ """Raise AssertionError if ``func(*args, **kwds)`` does not raise *exc*."""
+ pass
+
+
+class CustomEx(Exception):
+ """My custom exception."""
+
+ def f(self):
+ """Exception method."""
+
+
+class CustomDataDescriptor(object):
+ """Descriptor class docstring."""
+
+ def __init__(self, doc):
+ self.__doc__ = doc
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self
+ return 42
+
+ def meth(self):
+ """Function."""
+ return "The Answer"
+
+
+class CustomDataDescriptorMeta(type):
+ """Descriptor metaclass docstring."""
+
+
+@add_metaclass(CustomDataDescriptorMeta)
+class CustomDataDescriptor2(CustomDataDescriptor):
+ """Descriptor class with custom metaclass docstring."""
+
+
+def _funky_classmethod(name, b, c, d, docstring=None):
+ """Generates a classmethod for a class from a template by filling out
+ some arguments."""
+ def template(cls, a, b, c, d=4, e=5, f=6):
+ return a, b, c, d, e, f
+ from functools import partial
+ function = partial(template, b=b, c=c, d=d)
+ function.__name__ = name
+ function.__doc__ = docstring
+ return classmethod(function)
+
+
+class Base(object):
+ def inheritedmeth(self):
+ """Inherited function."""
+
+
+class Derived(Base):
+ def inheritedmeth(self):
+ # no docstring here
+ pass
+
+
+class Class(Base):
+ """Class to document."""
+
+ descr = CustomDataDescriptor("Descriptor instance docstring.")
+
+ def meth(self):
+ """Function."""
+
+ def undocmeth(self):
+ pass
+
+ def skipmeth(self):
+ """Method that should be skipped."""
+
+ def excludemeth(self):
+ """Method that should be excluded."""
+
+ # should not be documented
+ skipattr = 'foo'
+
+ #: should be documented -- süß
+ attr = 'bar'
+
+ @property
+ def prop(self):
+ """Property."""
+
+ docattr = 'baz'
+ """should likewise be documented -- süß"""
+
+ udocattr = 'quux'
+ u"""should be documented as well - süß"""
+
+ # initialized to any class imported from another module
+ mdocattr = StringIO()
+ """should be documented as well - süß"""
+
+ roger = _funky_classmethod("roger", 2, 3, 4)
+
+ moore = _funky_classmethod("moore", 9, 8, 7,
+ docstring="moore(a, e, f) -> happiness")
+
+ def __init__(self, arg):
+ self.inst_attr_inline = None #: an inline documented instance attr
+ #: a documented instance attribute
+ self.inst_attr_comment = None
+ self.inst_attr_string = None
+ """a documented instance attribute"""
+ self._private_inst_attr = None #: a private instance attribute
+
+ def __special1__(self):
+ """documented special method"""
+
+ def __special2__(self):
+ # undocumented special method
+ pass
+
+
+class CustomDict(dict):
+ """Docstring."""
+
+
+def function(foo, *args, **kwds):
+ """
+ Return spam.
+ """
+ pass
+
+
+class Outer(object):
+ """Foo"""
+
+ class Inner(object):
+ """Foo"""
+
+ def meth(self):
+ """Foo"""
+
+ # should be documented as an alias
+ factory = dict
+
+
+class DocstringSig(object):
+ def meth(self):
+ """meth(FOO, BAR=1) -> BAZ
+First line of docstring
+
+ rest of docstring
+ """
+
+ def meth2(self):
+ """First line, no signature
+ Second line followed by indentation::
+
+ indented line
+ """
+
+ @property
+ def prop1(self):
+ """DocstringSig.prop1(self)
+ First line of docstring
+ """
+ return 123
+
+ @property
+ def prop2(self):
+ """First line of docstring
+ Second line of docstring
+ """
+ return 456
+
+
+class StrRepr(str):
+ def __repr__(self):
+ return self
+
+
+class AttCls(object):
+ a1 = StrRepr('hello\nworld')
+ a2 = None
+
+
+class InstAttCls(object):
+ """Class with documented class and instance attributes."""
+
+ #: Doc comment for class attribute InstAttCls.ca1.
+ #: It can have multiple lines.
+ ca1 = 'a'
+
+ ca2 = 'b' #: Doc comment for InstAttCls.ca2. One line only.
+
+ ca3 = 'c'
+ """Docstring for class attribute InstAttCls.ca3."""
+
+ def __init__(self):
+ #: Doc comment for instance attribute InstAttCls.ia1
+ self.ia1 = 'd'
+
+ self.ia2 = 'e'
+ """Docstring for instance attribute InstAttCls.ia2."""
+
+
+class EnumCls(enum.Enum):
+ """
+ this is enum class
+ """
+
+ #: doc for val1
+ val1 = 12
+ val2 = 23 #: doc for val2
+ val3 = 34
+ """doc for val3"""
diff --git a/tests/roots/test-ext-math/index.rst b/tests/roots/test-ext-math/index.rst
index 9d16824f6..4237b73ff 100644
--- a/tests/roots/test-ext-math/index.rst
+++ b/tests/roots/test-ext-math/index.rst
@@ -2,8 +2,10 @@ Test Math
=========
.. toctree::
+ :numbered: 1
math
+ page
.. math:: a^2+b^2=c^2
diff --git a/tests/roots/test-ext-math/page.rst b/tests/roots/test-ext-math/page.rst
new file mode 100644
index 000000000..ef8040910
--- /dev/null
+++ b/tests/roots/test-ext-math/page.rst
@@ -0,0 +1,9 @@
+Test multiple pages
+===================
+
+.. math::
+ :label: bar
+
+ a = b + 1
+
+Referencing equations :eq:`foo` and :eq:`bar`.
diff --git a/tests/roots/test-ext-todo/index.rst b/tests/roots/test-ext-todo/index.rst
index 6b95f73fd..781473d6a 100644
--- a/tests/roots/test-ext-todo/index.rst
+++ b/tests/roots/test-ext-todo/index.rst
@@ -7,3 +7,5 @@ test for sphinx.ext.todo
bar
.. todolist::
+
+.. todolist::
diff --git a/tests/roots/test-extensions/conf.py b/tests/roots/test-extensions/conf.py
new file mode 100644
index 000000000..9a3cbc844
--- /dev/null
+++ b/tests/roots/test-extensions/conf.py
@@ -0,0 +1,4 @@
+import os
+import sys
+
+sys.path.insert(0, os.path.abspath('.'))
diff --git a/tests/roots/test-extensions/read_parallel.py b/tests/roots/test-extensions/read_parallel.py
new file mode 100644
index 000000000..a3e052f95
--- /dev/null
+++ b/tests/roots/test-extensions/read_parallel.py
@@ -0,0 +1,4 @@
+def setup(app):
+ return {
+ 'parallel_read_safe': True
+ }
diff --git a/tests/roots/test-extensions/read_serial.py b/tests/roots/test-extensions/read_serial.py
new file mode 100644
index 000000000..c55570a5c
--- /dev/null
+++ b/tests/roots/test-extensions/read_serial.py
@@ -0,0 +1,4 @@
+def setup(app):
+ return {
+ 'parallel_read_safe': False
+ }
diff --git a/tests/roots/test-extensions/write_parallel.py b/tests/roots/test-extensions/write_parallel.py
new file mode 100644
index 000000000..ebc48ef9b
--- /dev/null
+++ b/tests/roots/test-extensions/write_parallel.py
@@ -0,0 +1,4 @@
+def setup(app):
+ return {
+ 'parallel_write_safe': True,
+ }
diff --git a/tests/roots/test-extensions/write_serial.py b/tests/roots/test-extensions/write_serial.py
new file mode 100644
index 000000000..75494ce77
--- /dev/null
+++ b/tests/roots/test-extensions/write_serial.py
@@ -0,0 +1,4 @@
+def setup(app):
+ return {
+ 'parallel_write_safe': False
+ }
diff --git a/tests/roots/test-inheritance/basic_diagram.rst b/tests/roots/test-inheritance/basic_diagram.rst
new file mode 100644
index 000000000..4c3838e65
--- /dev/null
+++ b/tests/roots/test-inheritance/basic_diagram.rst
@@ -0,0 +1,5 @@
+Basic Diagram
+==============
+
+.. inheritance-diagram::
+ dummy.test
diff --git a/tests/roots/test-inheritance/conf.py b/tests/roots/test-inheritance/conf.py
new file mode 100644
index 000000000..f1ddb4ad6
--- /dev/null
+++ b/tests/roots/test-inheritance/conf.py
@@ -0,0 +1,6 @@
+import sys, os
+
+sys.path.insert(0, os.path.abspath('.'))
+
+extensions = ['sphinx.ext.inheritance_diagram']
+source_suffix = '.rst'
diff --git a/tests/roots/test-inheritance/contents.rst b/tests/roots/test-inheritance/contents.rst
new file mode 100644
index 000000000..db4fbacb8
--- /dev/null
+++ b/tests/roots/test-inheritance/contents.rst
@@ -0,0 +1,4 @@
+.. toctree::
+ :glob:
+
+ *
diff --git a/tests/roots/test-inheritance/diagram_module_w_2_top_classes.rst b/tests/roots/test-inheritance/diagram_module_w_2_top_classes.rst
new file mode 100644
index 000000000..cc4365e9c
--- /dev/null
+++ b/tests/roots/test-inheritance/diagram_module_w_2_top_classes.rst
@@ -0,0 +1,6 @@
+Diagram using module with 2 top classes
+=======================================
+
+.. inheritance-diagram::
+ dummy.test
+ :top-classes: dummy.test.B, dummy.test.C
diff --git a/tests/roots/test-inheritance/diagram_w_1_top_class.rst b/tests/roots/test-inheritance/diagram_w_1_top_class.rst
new file mode 100644
index 000000000..97da82557
--- /dev/null
+++ b/tests/roots/test-inheritance/diagram_w_1_top_class.rst
@@ -0,0 +1,7 @@
+Diagram using 1 top class
+=========================
+
+.. inheritance-diagram::
+ dummy.test
+ :top-classes: dummy.test.B
+
diff --git a/tests/roots/test-inheritance/diagram_w_2_top_classes.rst b/tests/roots/test-inheritance/diagram_w_2_top_classes.rst
new file mode 100644
index 000000000..8a6ae5865
--- /dev/null
+++ b/tests/roots/test-inheritance/diagram_w_2_top_classes.rst
@@ -0,0 +1,9 @@
+Diagram using 2 top classes
+===========================
+
+.. inheritance-diagram::
+ dummy.test.F
+ dummy.test.D
+ dummy.test.E
+ :top-classes: dummy.test.B, dummy.test.C
+
diff --git a/tests/roots/test-inheritance/diagram_w_parts.rst b/tests/roots/test-inheritance/diagram_w_parts.rst
new file mode 100644
index 000000000..65a831802
--- /dev/null
+++ b/tests/roots/test-inheritance/diagram_w_parts.rst
@@ -0,0 +1,7 @@
+Diagram using the parts option
+==============================
+
+.. inheritance-diagram::
+ dummy.test
+ :parts: 1
+
diff --git a/tests/roots/test-inheritance/dummy/__init__.py b/tests/roots/test-inheritance/dummy/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/roots/test-inheritance/dummy/__init__.py
diff --git a/tests/roots/test-inheritance/dummy/test.py b/tests/roots/test-inheritance/dummy/test.py
new file mode 100644
index 000000000..cafa07886
--- /dev/null
+++ b/tests/roots/test-inheritance/dummy/test.py
@@ -0,0 +1,30 @@
+"""
+
+ Test with a class diagram like this::
+
+ A
+ / \
+ B C
+ / \ / \
+ E D F
+
+"""
+
+class A(object):
+ pass
+
+class B(A):
+ pass
+
+class C(A):
+ pass
+
+class D(B, C):
+ pass
+
+class E(B):
+ pass
+
+class F(C):
+ pass
+
diff --git a/tests/roots/test-latex-numfig/conf.py b/tests/roots/test-latex-numfig/conf.py
new file mode 100644
index 000000000..506186b26
--- /dev/null
+++ b/tests/roots/test-latex-numfig/conf.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+
+master_doc = 'index'
+
+extensions = ['sphinx.ext.imgmath'] # for math_numfig
+
+latex_documents = [
+ ('indexmanual', 'SphinxManual.tex', 'Test numfig manual',
+ 'Sphinx', 'manual'),
+ ('indexhowto', 'SphinxHowTo.tex', 'Test numfig howto',
+ 'Sphinx', 'howto'),
+]
diff --git a/tests/roots/test-latex-numfig/index.rst b/tests/roots/test-latex-numfig/index.rst
new file mode 100644
index 000000000..6b8b9688c
--- /dev/null
+++ b/tests/roots/test-latex-numfig/index.rst
@@ -0,0 +1,9 @@
+=================
+test-latex-numfig
+=================
+
+.. toctree::
+ :numbered:
+
+ indexmanual
+ indexhowto
diff --git a/tests/roots/test-latex-numfig/indexhowto.rst b/tests/roots/test-latex-numfig/indexhowto.rst
new file mode 100644
index 000000000..4749f1ecd
--- /dev/null
+++ b/tests/roots/test-latex-numfig/indexhowto.rst
@@ -0,0 +1,10 @@
+=======================
+test-latex-numfig-howto
+=======================
+
+This is a part
+==============
+
+This is a section
+-----------------
+
diff --git a/tests/roots/test-latex-numfig/indexmanual.rst b/tests/roots/test-latex-numfig/indexmanual.rst
new file mode 100644
index 000000000..8bab4fbfd
--- /dev/null
+++ b/tests/roots/test-latex-numfig/indexmanual.rst
@@ -0,0 +1,13 @@
+========================
+test-latex-numfig-manual
+========================
+
+First part
+==========
+
+This is chapter
+---------------
+
+This is section
+~~~~~~~~~~~~~~~
+
diff --git a/tests/roots/test-latex-table/expects/longtable_having_verbatim.tex b/tests/roots/test-latex-table/expects/longtable_having_verbatim.tex
index 097449cd9..e1628a9bd 100644
--- a/tests/roots/test-latex-table/expects/longtable_having_verbatim.tex
+++ b/tests/roots/test-latex-table/expects/longtable_having_verbatim.tex
@@ -27,6 +27,7 @@ header2
\endlastfoot
+\fvset{hllines={, ,}}%
\begin{sphinxVerbatimintable}[commandchars=\\\{\}]
\PYG{n}{hello} \PYG{n}{world}
\end{sphinxVerbatimintable}
diff --git a/tests/roots/test-latex-table/expects/table_having_verbatim.tex b/tests/roots/test-latex-table/expects/table_having_verbatim.tex
index 2e2b1dc9a..40d2f424c 100644
--- a/tests/roots/test-latex-table/expects/table_having_verbatim.tex
+++ b/tests/roots/test-latex-table/expects/table_having_verbatim.tex
@@ -10,6 +10,7 @@ header1
header2
\\
\hline
+\fvset{hllines={, ,}}%
\begin{sphinxVerbatimintable}[commandchars=\\\{\}]
\PYG{n}{hello} \PYG{n}{world}
\end{sphinxVerbatimintable}
diff --git a/tests/roots/test-manpage_url/conf.py b/tests/roots/test-manpage_url/conf.py
new file mode 100644
index 000000000..c46e40773
--- /dev/null
+++ b/tests/roots/test-manpage_url/conf.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+
+master_doc = 'index'
+html_theme = 'classic'
+exclude_patterns = ['_build']
diff --git a/tests/roots/test-manpage_url/index.rst b/tests/roots/test-manpage_url/index.rst
new file mode 100644
index 000000000..50d3b042e
--- /dev/null
+++ b/tests/roots/test-manpage_url/index.rst
@@ -0,0 +1,3 @@
+ * :manpage:`man(1)`
+ * :manpage:`ls.1`
+ * :manpage:`sphinx`
diff --git a/tests/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/autodoc.txt b/tests/roots/test-root/autodoc.txt
index aa0dffba1..3c83ebf6e 100644
--- a/tests/roots/test-root/autodoc.txt
+++ b/tests/roots/test-root/autodoc.txt
@@ -5,7 +5,7 @@ Just testing a few autodoc possibilities...
.. automodule:: util
-.. automodule:: test_autodoc
+.. automodule:: autodoc_target
:members:
.. autofunction:: function
@@ -34,7 +34,7 @@ Just testing a few autodoc possibilities...
.. autoclass:: MarkupError
-.. currentmodule:: test_autodoc
+.. currentmodule:: autodoc_target
.. autoclass:: InstAttCls
:members:
diff --git a/tests/roots/test-root/autodoc_target.py b/tests/roots/test-root/autodoc_target.py
new file mode 100644
index 000000000..bd00bf183
--- /dev/null
+++ b/tests/roots/test-root/autodoc_target.py
@@ -0,0 +1,225 @@
+# -*- coding: utf-8 -*-
+
+import enum
+from six import StringIO, add_metaclass
+from sphinx.ext.autodoc import add_documenter # NOQA
+
+
+__all__ = ['Class']
+
+#: documentation for the integer
+integer = 1
+
+
+def raises(exc, func, *args, **kwds):
+ """Raise AssertionError if ``func(*args, **kwds)`` does not raise *exc*."""
+ pass
+
+
+class CustomEx(Exception):
+ """My custom exception."""
+
+ def f(self):
+ """Exception method."""
+
+
+class CustomDataDescriptor(object):
+ """Descriptor class docstring."""
+
+ def __init__(self, doc):
+ self.__doc__ = doc
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self
+ return 42
+
+ def meth(self):
+ """Function."""
+ return "The Answer"
+
+
+class CustomDataDescriptorMeta(type):
+ """Descriptor metaclass docstring."""
+
+
+@add_metaclass(CustomDataDescriptorMeta)
+class CustomDataDescriptor2(CustomDataDescriptor):
+ """Descriptor class with custom metaclass docstring."""
+
+
+def _funky_classmethod(name, b, c, d, docstring=None):
+ """Generates a classmethod for a class from a template by filling out
+ some arguments."""
+ def template(cls, a, b, c, d=4, e=5, f=6):
+ return a, b, c, d, e, f
+ from functools import partial
+ function = partial(template, b=b, c=c, d=d)
+ function.__name__ = name
+ function.__doc__ = docstring
+ return classmethod(function)
+
+
+class Base(object):
+ def inheritedmeth(self):
+ """Inherited function."""
+
+
+class Derived(Base):
+ def inheritedmeth(self):
+ # no docstring here
+ pass
+
+
+class Class(Base):
+ """Class to document."""
+
+ descr = CustomDataDescriptor("Descriptor instance docstring.")
+
+ def meth(self):
+ """Function."""
+
+ def undocmeth(self):
+ pass
+
+ def skipmeth(self):
+ """Method that should be skipped."""
+
+ def excludemeth(self):
+ """Method that should be excluded."""
+
+ # should not be documented
+ skipattr = 'foo'
+
+ #: should be documented -- süß
+ attr = 'bar'
+
+ @property
+ def prop(self):
+ """Property."""
+
+ docattr = 'baz'
+ """should likewise be documented -- süß"""
+
+ udocattr = 'quux'
+ u"""should be documented as well - süß"""
+
+ # initialized to any class imported from another module
+ mdocattr = StringIO()
+ """should be documented as well - süß"""
+
+ roger = _funky_classmethod("roger", 2, 3, 4)
+
+ moore = _funky_classmethod("moore", 9, 8, 7,
+ docstring="moore(a, e, f) -> happiness")
+
+ def __init__(self, arg):
+ self.inst_attr_inline = None #: an inline documented instance attr
+ #: a documented instance attribute
+ self.inst_attr_comment = None
+ self.inst_attr_string = None
+ """a documented instance attribute"""
+ self._private_inst_attr = None #: a private instance attribute
+
+ def __special1__(self):
+ """documented special method"""
+
+ def __special2__(self):
+ # undocumented special method
+ pass
+
+
+class CustomDict(dict):
+ """Docstring."""
+
+
+def function(foo, *args, **kwds):
+ """
+ Return spam.
+ """
+ pass
+
+
+class Outer(object):
+ """Foo"""
+
+ class Inner(object):
+ """Foo"""
+
+ def meth(self):
+ """Foo"""
+
+ # should be documented as an alias
+ factory = dict
+
+
+class DocstringSig(object):
+ def meth(self):
+ """meth(FOO, BAR=1) -> BAZ
+First line of docstring
+
+ rest of docstring
+ """
+
+ def meth2(self):
+ """First line, no signature
+ Second line followed by indentation::
+
+ indented line
+ """
+
+ @property
+ def prop1(self):
+ """DocstringSig.prop1(self)
+ First line of docstring
+ """
+ return 123
+
+ @property
+ def prop2(self):
+ """First line of docstring
+ Second line of docstring
+ """
+ return 456
+
+
+class StrRepr(str):
+ def __repr__(self):
+ return self
+
+
+class AttCls(object):
+ a1 = StrRepr('hello\nworld')
+ a2 = None
+
+
+class InstAttCls(object):
+ """Class with documented class and instance attributes."""
+
+ #: Doc comment for class attribute InstAttCls.ca1.
+ #: It can have multiple lines.
+ ca1 = 'a'
+
+ ca2 = 'b' #: Doc comment for InstAttCls.ca2. One line only.
+
+ ca3 = 'c'
+ """Docstring for class attribute InstAttCls.ca3."""
+
+ def __init__(self):
+ #: Doc comment for instance attribute InstAttCls.ia1
+ self.ia1 = 'd'
+
+ self.ia2 = 'e'
+ """Docstring for instance attribute InstAttCls.ia2."""
+
+
+class EnumCls(enum.Enum):
+ """
+ this is enum class
+ """
+
+ #: doc for val1
+ val1 = 12
+ val2 = 23 #: doc for val2
+ val3 = 34
+ """doc for val3"""
diff --git a/tests/roots/test-root/conf.py b/tests/roots/test-root/conf.py
index 9e984635d..0753fe19c 100644
--- a/tests/roots/test-root/conf.py
+++ b/tests/roots/test-root/conf.py
@@ -29,16 +29,11 @@ numfig = True
rst_epilog = '.. |subst| replace:: global substitution'
-html_theme = 'testtheme'
-html_theme_path = ['.']
-html_theme_options = {'testopt': 'testoverride'}
html_sidebars = {'**': ['localtoc.html', 'relations.html', 'sourcelink.html',
'customsb.html', 'searchbox.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/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/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/roots/test-smartquotes/conf.py b/tests/roots/test-smartquotes/conf.py
new file mode 100644
index 000000000..31e7a6ed4
--- /dev/null
+++ b/tests/roots/test-smartquotes/conf.py
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+
+master_doc = 'index'
+
+latex_documents = [
+ (master_doc, 'test.tex', 'The basic Sphinx documentation for testing', 'Sphinx', 'report')
+]
diff --git a/tests/roots/test-smartquotes/index.rst b/tests/roots/test-smartquotes/index.rst
new file mode 100644
index 000000000..be0d9b89c
--- /dev/null
+++ b/tests/roots/test-smartquotes/index.rst
@@ -0,0 +1,4 @@
+test-smartquotes
+================
+
+-- "Sphinx" is a tool that makes it easy ...
diff --git a/tests/roots/test-root/testtheme/layout.html b/tests/roots/test-theming/test_theme/staticfiles/layout.html
index 81372be0f..81372be0f 100644
--- a/tests/roots/test-root/testtheme/layout.html
+++ b/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
index fda6cd29e..fda6cd29e 100644
--- a/tests/roots/test-root/testtheme/static/staticimg.png
+++ b/tests/roots/test-theming/test_theme/staticfiles/static/staticimg.png
Binary files differ
diff --git a/tests/roots/test-root/testtheme/static/statictmpl.html_t b/tests/roots/test-theming/test_theme/staticfiles/static/statictmpl.html_t
index 4ab292b42..4ab292b42 100644
--- a/tests/roots/test-root/testtheme/static/statictmpl.html_t
+++ b/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
index a87767379..a87767379 100644
--- a/tests/roots/test-root/testtheme/theme.conf
+++ b/tests/roots/test-theming/test_theme/staticfiles/theme.conf
diff --git a/tests/roots/test-root/ziptheme.zip b/tests/roots/test-theming/ziptheme.zip
index 8a246ed98..8a246ed98 100644
--- a/tests/roots/test-root/ziptheme.zip
+++ b/tests/roots/test-theming/ziptheme.zip
Binary files differ
diff --git a/tests/run.py b/tests/run.py
deleted file mode 100755
index a8439ba02..000000000
--- a/tests/run.py
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
- Sphinx unit test driver
- ~~~~~~~~~~~~~~~~~~~~~~~
-
- This script runs the Sphinx unit test suite.
-
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-from __future__ import print_function
-
-import os
-import sys
-import warnings
-import traceback
-import shutil
-
-testroot = os.path.dirname(__file__) or '.'
-sys.path.insert(0, os.path.abspath(os.path.join(testroot, os.path.pardir)))
-
-# filter warnings of test dependencies
-warnings.filterwarnings('ignore', category=DeprecationWarning, module='site') # virtualenv
-warnings.filterwarnings('ignore', category=ImportWarning, module='backports')
-warnings.filterwarnings('ignore', category=ImportWarning, module='pkgutil')
-warnings.filterwarnings('ignore', category=ImportWarning, module='pytest_cov')
-warnings.filterwarnings('ignore', category=PendingDeprecationWarning, module=r'_pytest\..*')
-
-# check dependencies before testing
-print('Checking dependencies...')
-for modname in ('pytest', 'mock', 'six', 'docutils', 'jinja2', 'pygments',
- 'snowballstemmer', 'babel', 'html5lib'):
- try:
- __import__(modname)
- except ImportError as err:
- if modname == 'mock' and sys.version_info[0] == 3:
- continue
- traceback.print_exc()
- print('The %r package is needed to run the Sphinx test suite.' % modname)
- sys.exit(1)
-
-# find a temp dir for testing and clean it up now
-os.environ['SPHINX_TEST_TEMPDIR'] = \
- os.path.abspath(os.path.join(testroot, 'build')) \
- if 'SPHINX_TEST_TEMPDIR' not in os.environ \
- else os.path.abspath(os.environ['SPHINX_TEST_TEMPDIR'])
-
-tempdir = os.environ['SPHINX_TEST_TEMPDIR']
-print('Temporary files will be placed in %s.' % tempdir)
-if os.path.exists(tempdir):
- shutil.rmtree(tempdir)
-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))
diff --git a/tests/test_api_translator.py b/tests/test_api_translator.py
index 35b24732b..4e4230ba3 100644
--- a/tests/test_api_translator.py
+++ b/tests/test_api_translator.py
@@ -5,7 +5,7 @@
Test the Sphinx API for translator.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_application.py b/tests/test_application.py
index 785a78878..12b6bbe60 100644
--- a/tests/test_application.py
+++ b/tests/test_application.py
@@ -5,13 +5,14 @@
Test the Sphinx class.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from docutils import nodes
from sphinx.application import ExtensionError
from sphinx.domains import Domain
+from sphinx.util import logging
from sphinx.testing.util import strip_escseq
import pytest
@@ -86,3 +87,38 @@ def test_add_source_parser(app, status, warning):
assert set(app.registry.get_source_parsers().keys()) == set(['*', '.md', '.test'])
assert app.registry.get_source_parsers()['.md'].__name__ == 'DummyMarkdownParser'
assert app.registry.get_source_parsers()['.test'].__name__ == 'TestSourceParser'
+
+
+@pytest.mark.sphinx(testroot='extensions')
+def test_add_is_parallel_allowed(app, status, warning):
+ logging.setup(app, status, warning)
+
+ assert app.is_parallel_allowed('read') is True
+ assert app.is_parallel_allowed('write') is True
+ assert warning.getvalue() == ''
+
+ app.setup_extension('read_parallel')
+ assert app.is_parallel_allowed('read') is True
+ assert app.is_parallel_allowed('write') is True
+ assert warning.getvalue() == ''
+ app.extensions.pop('read_parallel')
+
+ app.setup_extension('write_parallel')
+ assert app.is_parallel_allowed('read') is False
+ assert app.is_parallel_allowed('write') is True
+ assert 'the write_parallel extension does not declare' in warning.getvalue()
+ app.extensions.pop('write_parallel')
+ warning.truncate(0) # reset warnings
+
+ app.setup_extension('read_serial')
+ assert app.is_parallel_allowed('read') is False
+ assert app.is_parallel_allowed('write') is True
+ assert warning.getvalue() == ''
+ app.extensions.pop('read_serial')
+
+ app.setup_extension('write_serial')
+ assert app.is_parallel_allowed('read') is False
+ assert app.is_parallel_allowed('write') is False
+ assert 'the write_serial extension does not declare' in warning.getvalue()
+ app.extensions.pop('write_serial')
+ warning.truncate(0) # reset warnings
diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py
index 11958800a..d9c94bc0d 100644
--- a/tests/test_autodoc.py
+++ b/tests/test_autodoc.py
@@ -6,39 +6,44 @@
Test the autodoc extension. This tests mainly the Documenters; the auto
directives are tested in a test source file translated by test_build.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
+import sys
from six import PY3
from sphinx.testing.util import SphinxTestApp, Struct # NOQA
import pytest
-import enum
-from six import StringIO, add_metaclass
from docutils.statemachine import ViewList
from sphinx.ext.autodoc import AutoDirective, add_documenter, \
ModuleLevelDocumenter, FunctionDocumenter, cut_lines, between, ALL
+from sphinx.util import logging
app = None
@pytest.fixture(scope='module', autouse=True)
def setup_module(rootdir, sphinx_test_tempdir):
- global app
- srcdir = sphinx_test_tempdir / 'autodoc-root'
- if not srcdir.exists():
- (rootdir/'test-root').copytree(srcdir)
- app = SphinxTestApp(srcdir=srcdir)
- app.builder.env.app = app
- app.builder.env.temp_data['docname'] = 'dummy'
- app.connect('autodoc-process-docstring', process_docstring)
- app.connect('autodoc-process-signature', process_signature)
- app.connect('autodoc-skip-member', skip_member)
- yield
- app.cleanup()
+ try:
+ global app
+ srcdir = sphinx_test_tempdir / 'autodoc-root'
+ if not srcdir.exists():
+ (rootdir / 'test-root').copytree(srcdir)
+ testroot = rootdir / 'test-ext-autodoc'
+ sys.path.append(testroot)
+ app = SphinxTestApp(srcdir=srcdir)
+ app.builder.env.app = app
+ app.builder.env.temp_data['docname'] = 'dummy'
+ app.connect('autodoc-process-docstring', process_docstring)
+ app.connect('autodoc-process-signature', process_signature)
+ app.connect('autodoc-skip-member', skip_member)
+ yield
+ finally:
+ app.cleanup()
+ sys.path.remove(testroot)
directive = options = None
@@ -47,7 +52,7 @@ directive = options = None
@pytest.fixture
def setup_test():
global options, directive
- global processed_docstrings, processed_signatures, _warnings
+ global processed_docstrings, processed_signatures
options = Struct(
inherited_members = False,
@@ -64,34 +69,31 @@ def setup_test():
members = [],
member_order = 'alphabetic',
exclude_members = set(),
+ ignore_module_all = False,
)
directive = Struct(
env = app.builder.env,
genopt = options,
result = ViewList(),
- warn = warnfunc,
filename_set = set(),
)
processed_docstrings = []
processed_signatures = []
- _warnings = []
+
+ app._status.truncate(0)
+ app._warning.truncate(0)
yield
AutoDirective._special_attrgetters.clear()
-_warnings = []
processed_docstrings = []
processed_signatures = []
-def warnfunc(msg):
- _warnings.append(msg)
-
-
def process_docstring(app, what, name, obj, options, lines):
processed_docstrings.append((what, name))
if name == 'bar':
@@ -115,8 +117,10 @@ def skip_member(app, what, name, obj, skip, options):
@pytest.mark.usefixtures('setup_test')
def test_parse_name():
+ logging.setup(app, app._status, app._warning)
+
def verify(objtype, name, result):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
assert inst.parse_name()
assert (inst.modname, inst.objpath, inst.args, inst.retann) == result
@@ -124,8 +128,7 @@ def test_parse_name():
verify('module', 'test_autodoc', ('test_autodoc', [], None, None))
verify('module', 'test.test_autodoc', ('test.test_autodoc', [], None, None))
verify('module', 'test(arg)', ('test', [], 'arg', None))
- assert 'signature arguments' in _warnings[0]
- del _warnings[:]
+ assert 'signature arguments' in app._warning.getvalue()
# for functions/classes
verify('function', 'test_autodoc.raises',
@@ -159,7 +162,7 @@ def test_parse_name():
@pytest.mark.usefixtures('setup_test')
def test_format_signature():
def formatsig(objtype, name, obj, args, retann):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.fullname = name
inst.doc_as_attr = False # for class objtype
inst.object = obj
@@ -241,7 +244,6 @@ def test_format_signature():
# test exception handling (exception is caught and args is '')
directive.env.config.autodoc_docstring_signature = False
assert formatsig('function', 'int', int, None, None) == ''
- del _warnings[:]
# test processing by event handler
assert formatsig('method', 'bar', H.foo1, None, None) == '42'
@@ -265,7 +267,7 @@ def test_format_signature():
@pytest.mark.usefixtures('setup_test')
def test_get_doc():
def getdocl(objtype, obj, encoding=None):
- inst = AutoDirective._registry[objtype](directive, 'tmp')
+ inst = app.registry.documenters[objtype](directive, 'tmp')
inst.object = obj
inst.objpath = [obj.__name__]
inst.doc_as_attr = False
@@ -420,7 +422,7 @@ def test_get_doc():
# class has __init__ method without docstring and
# __new__ method with docstring
# class docstring: depends on config value which one is taken
- class I:
+ class I: # NOQA
"""Class docstring"""
def __new__(cls):
"""New docstring"""
@@ -431,6 +433,8 @@ def test_get_doc():
directive.env.config.autoclass_content = 'both'
assert getdocl('class', I) == ['Class docstring', '', 'New docstring']
+ from target import Base, Derived
+
# NOTE: inspect.getdoc seems not to work with locally defined classes
directive.env.config.autodoc_inherit_docstrings = False
assert getdocl('method', Base.inheritedmeth) == ['Inherited function.']
@@ -442,7 +446,7 @@ def test_get_doc():
@pytest.mark.usefixtures('setup_test')
def test_docstring_processing():
def process(objtype, name, obj):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.object = obj
inst.fullname = name
return list(inst.process_doc(inst.get_doc()))
@@ -499,7 +503,7 @@ def test_docstring_property_processing():
def genarate_docstring(objtype, name, **kw):
del processed_docstrings[:]
del processed_signatures[:]
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
results = list(directive.result)
docstrings = inst.get_doc()[0]
@@ -508,24 +512,24 @@ def test_docstring_property_processing():
directive.env.config.autodoc_docstring_signature = False
results, docstrings = \
- genarate_docstring('attribute', 'test_autodoc.DocstringSig.prop1')
+ genarate_docstring('attribute', 'target.DocstringSig.prop1')
assert '.. py:attribute:: DocstringSig.prop1' in results
assert 'First line of docstring' in docstrings
assert 'DocstringSig.prop1(self)' in docstrings
results, docstrings = \
- genarate_docstring('attribute', 'test_autodoc.DocstringSig.prop2')
+ genarate_docstring('attribute', 'target.DocstringSig.prop2')
assert '.. py:attribute:: DocstringSig.prop2' in results
assert 'First line of docstring' in docstrings
assert 'Second line of docstring' in docstrings
directive.env.config.autodoc_docstring_signature = True
results, docstrings = \
- genarate_docstring('attribute', 'test_autodoc.DocstringSig.prop1')
+ genarate_docstring('attribute', 'target.DocstringSig.prop1')
assert '.. py:attribute:: DocstringSig.prop1' in results
assert 'First line of docstring' in docstrings
assert 'DocstringSig.prop1(self)' not in docstrings
results, docstrings = \
- genarate_docstring('attribute', 'test_autodoc.DocstringSig.prop2')
+ genarate_docstring('attribute', 'target.DocstringSig.prop2')
assert '.. py:attribute:: DocstringSig.prop2' in results
assert 'First line of docstring' in docstrings
assert 'Second line of docstring' in docstrings
@@ -533,6 +537,8 @@ def test_docstring_property_processing():
@pytest.mark.usefixtures('setup_test')
def test_new_documenter():
+ logging.setup(app, app._status, app._warning)
+
class MyDocumenter(ModuleLevelDocumenter):
objtype = 'integer'
directivetype = 'data'
@@ -548,19 +554,22 @@ def test_new_documenter():
add_documenter(MyDocumenter)
def assert_result_contains(item, objtype, name, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ app._warning.truncate(0)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
# print '\n'.join(directive.result)
- assert len(_warnings) == 0, _warnings
+ assert app._warning.getvalue() == ''
assert item in directive.result
del directive.result[:]
options.members = ['integer']
- assert_result_contains('.. py:data:: integer', 'module', 'test_autodoc')
+ assert_result_contains('.. py:data:: integer', 'module', 'target')
@pytest.mark.usefixtures('setup_test')
def test_attrgetter_using():
+ from target import Class
+
def assert_getter_works(objtype, name, obj, attrs=[], **kw):
getattr_spy = []
@@ -572,7 +581,7 @@ def test_attrgetter_using():
AutoDirective._special_attrgetters[type] = special_getattr
del getattr_spy[:]
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
hooked_members = [s[1] for s in getattr_spy]
@@ -585,28 +594,30 @@ def test_attrgetter_using():
options.members = ALL
options.inherited_members = False
- assert_getter_works('class', 'test_autodoc.Class', Class, ['meth'])
+ assert_getter_works('class', 'target.Class', Class, ['meth'])
options.inherited_members = True
- assert_getter_works('class', 'test_autodoc.Class', Class, ['meth', 'inheritedmeth'])
+ assert_getter_works('class', 'target.Class', Class, ['meth', 'inheritedmeth'])
@pytest.mark.usefixtures('setup_test')
def test_generate():
+ logging.setup(app, app._status, app._warning)
+
def assert_warns(warn_str, objtype, name, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
assert len(directive.result) == 0, directive.result
- assert len(_warnings) == 1, _warnings
- assert warn_str in _warnings[0], _warnings
- del _warnings[:]
+
+ assert warn_str in app._warning.getvalue()
+ app._warning.truncate(0)
def assert_works(objtype, name, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
assert directive.result
# print '\n'.join(directive.result)
- assert len(_warnings) == 0, _warnings
+ assert app._warning.getvalue() == ''
del directive.result[:]
def assert_processes(items, objtype, name, **kw):
@@ -616,18 +627,18 @@ def test_generate():
assert set(processed_docstrings) | set(processed_signatures) == set(items)
def assert_result_contains(item, objtype, name, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
# print '\n'.join(directive.result)
- assert len(_warnings) == 0, _warnings
+ assert app._warning.getvalue() == ''
assert item in directive.result
del directive.result[:]
def assert_order(items, objtype, name, member_order, **kw):
- inst = AutoDirective._registry[objtype](directive, name)
+ inst = app.registry.documenters[objtype](directive, name)
inst.options.member_order = member_order
inst.generate(**kw)
- assert len(_warnings) == 0, _warnings
+ assert app._warning.getvalue() == ''
items = list(reversed(items))
lineiter = iter(directive.result)
# for line in directive.result:
@@ -655,11 +666,11 @@ def test_generate():
assert_warns("failed to import function 'foobar' from module 'util'",
'function', 'util.foobar', more_content=None)
# method missing
- assert_warns("failed to import method 'Class.foobar' from module 'test_autodoc';",
- 'method', 'test_autodoc.Class.foobar', more_content=None)
+ assert_warns("failed to import method 'Class.foobar' from module 'target';",
+ 'method', 'target.Class.foobar', more_content=None)
# test auto and given content mixing
- directive.env.ref_context['py:module'] = 'test_autodoc'
+ directive.env.ref_context['py:module'] = 'target'
assert_result_contains(' Function.', 'method', 'Class.meth')
add_content = ViewList()
add_content.append('Content.', '', 0)
@@ -674,72 +685,77 @@ def test_generate():
assert len(directive.result) == 0
# assert that exceptions can be documented
- assert_works('exception', 'test_autodoc.CustomEx', all_members=True)
- assert_works('exception', 'test_autodoc.CustomEx')
+ assert_works('exception', 'target.CustomEx', all_members=True)
+ assert_works('exception', 'target.CustomEx')
# test diverse inclusion settings for members
- should = [('class', 'test_autodoc.Class')]
+ should = [('class', 'target.Class')]
assert_processes(should, 'class', 'Class')
- should.extend([('method', 'test_autodoc.Class.meth')])
+ should.extend([('method', 'target.Class.meth')])
options.members = ['meth']
options.exclude_members = set(['excludemeth'])
assert_processes(should, 'class', 'Class')
- should.extend([('attribute', 'test_autodoc.Class.prop'),
- ('attribute', 'test_autodoc.Class.descr'),
- ('attribute', 'test_autodoc.Class.attr'),
- ('attribute', 'test_autodoc.Class.docattr'),
- ('attribute', 'test_autodoc.Class.udocattr'),
- ('attribute', 'test_autodoc.Class.mdocattr'),
- ('attribute', 'test_autodoc.Class.inst_attr_comment'),
- ('attribute', 'test_autodoc.Class.inst_attr_inline'),
- ('attribute', 'test_autodoc.Class.inst_attr_string'),
- ('method', 'test_autodoc.Class.moore'),
+ should.extend([('attribute', 'target.Class.prop'),
+ ('attribute', 'target.Class.descr'),
+ ('attribute', 'target.Class.attr'),
+ ('attribute', 'target.Class.docattr'),
+ ('attribute', 'target.Class.udocattr'),
+ ('attribute', 'target.Class.mdocattr'),
+ ('attribute', 'target.Class.inst_attr_comment'),
+ ('attribute', 'target.Class.inst_attr_inline'),
+ ('attribute', 'target.Class.inst_attr_string'),
+ ('method', 'target.Class.moore'),
])
options.members = ALL
assert_processes(should, 'class', 'Class')
options.undoc_members = True
- should.extend((('attribute', 'test_autodoc.Class.skipattr'),
- ('method', 'test_autodoc.Class.undocmeth'),
- ('method', 'test_autodoc.Class.roger')))
+ should.extend((('attribute', 'target.Class.skipattr'),
+ ('method', 'target.Class.undocmeth'),
+ ('method', 'target.Class.roger')))
assert_processes(should, 'class', 'Class')
options.inherited_members = True
- should.append(('method', 'test_autodoc.Class.inheritedmeth'))
+ should.append(('method', 'target.Class.inheritedmeth'))
assert_processes(should, 'class', 'Class')
# test special members
options.special_members = ['__special1__']
- should.append(('method', 'test_autodoc.Class.__special1__'))
+ should.append(('method', 'target.Class.__special1__'))
assert_processes(should, 'class', 'Class')
options.special_members = ALL
- should.append(('method', 'test_autodoc.Class.__special2__'))
+ should.append(('method', 'target.Class.__special2__'))
assert_processes(should, 'class', 'Class')
options.special_members = False
options.members = []
# test module flags
- assert_result_contains('.. py:module:: test_autodoc',
- 'module', 'test_autodoc')
+ assert_result_contains('.. py:module:: target',
+ 'module', 'target')
options.synopsis = 'Synopsis'
- assert_result_contains(' :synopsis: Synopsis', 'module', 'test_autodoc')
+ assert_result_contains(' :synopsis: Synopsis', 'module', 'target')
options.deprecated = True
- assert_result_contains(' :deprecated:', 'module', 'test_autodoc')
+ assert_result_contains(' :deprecated:', 'module', 'target')
options.platform = 'Platform'
- assert_result_contains(' :platform: Platform', 'module', 'test_autodoc')
+ assert_result_contains(' :platform: Platform', 'module', 'target')
# test if __all__ is respected for modules
options.members = ALL
- assert_result_contains('.. py:class:: Class(arg)', 'module', 'test_autodoc')
+ assert_result_contains('.. py:class:: Class(arg)', 'module', 'target')
try:
assert_result_contains('.. py:exception:: CustomEx',
- 'module', 'test_autodoc')
+ 'module', 'target')
except AssertionError:
pass
else:
assert False, 'documented CustomEx which is not in __all__'
+ # test ignore-module-all
+ options.ignore_module_all = True
+ assert_result_contains('.. py:class:: Class(arg)', 'module', 'target')
+ assert_result_contains('.. py:exception:: CustomEx', 'module', 'target')
+
# test noindex flag
options.members = []
options.noindex = True
- assert_result_contains(' :noindex:', 'module', 'test_autodoc')
+ assert_result_contains(' :noindex:', 'module', 'target')
assert_result_contains(' :noindex:', 'class', 'Base')
# okay, now let's get serious about mixing Python and C signature stuff
@@ -747,14 +763,14 @@ def test_generate():
all_members=True)
# test inner class handling
- assert_processes([('class', 'test_autodoc.Outer'),
- ('class', 'test_autodoc.Outer.Inner'),
- ('method', 'test_autodoc.Outer.Inner.meth')],
+ assert_processes([('class', 'target.Outer'),
+ ('class', 'target.Outer.Inner'),
+ ('method', 'target.Outer.Inner.meth')],
'class', 'Outer', all_members=True)
# test descriptor docstrings
assert_result_contains(' Descriptor instance docstring.',
- 'attribute', 'test_autodoc.Class.descr')
+ 'attribute', 'target.Class.descr')
# test generation for C modules (which have no source file)
directive.env.ref_context['py:module'] = 'time'
@@ -762,7 +778,7 @@ def test_generate():
assert_processes([('function', 'time.asctime')], 'function', 'asctime')
# test autodoc_member_order == 'source'
- directive.env.ref_context['py:module'] = 'test_autodoc'
+ directive.env.ref_context['py:module'] = 'target'
options.private_members = True
if PY3:
roger_line = ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)'
@@ -788,7 +804,7 @@ def test_generate():
del directive.env.ref_context['py:module']
# test attribute initialized to class instance from other module
- directive.env.temp_data['autodoc:class'] = 'test_autodoc.Class'
+ directive.env.temp_data['autodoc:class'] = 'target.Class'
assert_result_contains(u' should be documented as well - s\xfc\xdf',
'attribute', 'mdocattr')
del directive.env.temp_data['autodoc:class']
@@ -796,25 +812,25 @@ def test_generate():
# test autodoc_docstring_signature
assert_result_contains(
'.. py:method:: DocstringSig.meth(FOO, BAR=1) -> BAZ', 'method',
- 'test_autodoc.DocstringSig.meth')
+ 'target.DocstringSig.meth')
assert_result_contains(
- ' rest of docstring', 'method', 'test_autodoc.DocstringSig.meth')
+ ' rest of docstring', 'method', 'target.DocstringSig.meth')
assert_result_contains(
'.. py:method:: DocstringSig.meth2()', 'method',
- 'test_autodoc.DocstringSig.meth2')
+ 'target.DocstringSig.meth2')
assert_result_contains(
' indented line', 'method',
- 'test_autodoc.DocstringSig.meth2')
+ 'target.DocstringSig.meth2')
assert_result_contains(
'.. py:classmethod:: Class.moore(a, e, f) -> happiness', 'method',
- 'test_autodoc.Class.moore')
+ 'target.Class.moore')
# test new attribute documenter behavior
- directive.env.ref_context['py:module'] = 'test_autodoc'
+ directive.env.ref_context['py:module'] = 'target'
options.undoc_members = True
- assert_processes([('class', 'test_autodoc.AttCls'),
- ('attribute', 'test_autodoc.AttCls.a1'),
- ('attribute', 'test_autodoc.AttCls.a2'),
+ assert_processes([('class', 'target.AttCls'),
+ ('attribute', 'target.AttCls.a1'),
+ ('attribute', 'target.AttCls.a2'),
], 'class', 'AttCls')
assert_result_contains(
' :annotation: = hello world', 'attribute', 'AttCls.a1')
@@ -824,40 +840,40 @@ def test_generate():
# test explicit members with instance attributes
del directive.env.temp_data['autodoc:class']
del directive.env.temp_data['autodoc:module']
- directive.env.ref_context['py:module'] = 'test_autodoc'
+ directive.env.ref_context['py:module'] = 'target'
options.inherited_members = False
options.undoc_members = False
options.members = ALL
assert_processes([
- ('class', 'test_autodoc.InstAttCls'),
- ('attribute', 'test_autodoc.InstAttCls.ca1'),
- ('attribute', 'test_autodoc.InstAttCls.ca2'),
- ('attribute', 'test_autodoc.InstAttCls.ca3'),
- ('attribute', 'test_autodoc.InstAttCls.ia1'),
- ('attribute', 'test_autodoc.InstAttCls.ia2'),
+ ('class', 'target.InstAttCls'),
+ ('attribute', 'target.InstAttCls.ca1'),
+ ('attribute', 'target.InstAttCls.ca2'),
+ ('attribute', 'target.InstAttCls.ca3'),
+ ('attribute', 'target.InstAttCls.ia1'),
+ ('attribute', 'target.InstAttCls.ia2'),
], 'class', 'InstAttCls')
del directive.env.temp_data['autodoc:class']
del directive.env.temp_data['autodoc:module']
options.members = ['ca1', 'ia1']
assert_processes([
- ('class', 'test_autodoc.InstAttCls'),
- ('attribute', 'test_autodoc.InstAttCls.ca1'),
- ('attribute', 'test_autodoc.InstAttCls.ia1'),
+ ('class', 'target.InstAttCls'),
+ ('attribute', 'target.InstAttCls.ca1'),
+ ('attribute', 'target.InstAttCls.ia1'),
], 'class', 'InstAttCls')
del directive.env.temp_data['autodoc:class']
del directive.env.temp_data['autodoc:module']
del directive.env.ref_context['py:module']
# test members with enum attributes
- directive.env.ref_context['py:module'] = 'test_autodoc'
+ directive.env.ref_context['py:module'] = 'target'
options.inherited_members = False
options.undoc_members = False
options.members = ALL
assert_processes([
- ('class', 'test_autodoc.EnumCls'),
- ('attribute', 'test_autodoc.EnumCls.val1'),
- ('attribute', 'test_autodoc.EnumCls.val2'),
- ('attribute', 'test_autodoc.EnumCls.val3'),
+ ('class', 'target.EnumCls'),
+ ('attribute', 'target.EnumCls.val1'),
+ ('attribute', 'target.EnumCls.val2'),
+ ('attribute', 'target.EnumCls.val3'),
], 'class', 'EnumCls')
assert_result_contains(
' :annotation: = 12', 'attribute', 'EnumCls.val1')
@@ -871,11 +887,11 @@ def test_generate():
# test descriptor class documentation
options.members = ['CustomDataDescriptor', 'CustomDataDescriptor2']
assert_result_contains('.. py:class:: CustomDataDescriptor(doc)',
- 'module', 'test_autodoc')
+ 'module', 'target')
assert_result_contains(' .. py:method:: CustomDataDescriptor.meth()',
- 'module', 'test_autodoc')
+ 'module', 'target')
assert_result_contains('.. py:class:: CustomDataDescriptor2(doc)',
- 'module', 'test_autodoc')
+ 'module', 'target')
# test mocked module imports
options.members = ['TestAutodoc']
@@ -887,274 +903,3 @@ def test_generate():
options.members = ['decoratedFunction']
assert_result_contains('.. py:function:: decoratedFunction()',
'module', 'autodoc_missing_imports')
-
-
-# --- generate fodder ------------
-__all__ = ['Class']
-
-#: documentation for the integer
-integer = 1
-
-
-def raises(exc, func, *args, **kwds):
- """Raise AssertionError if ``func(*args, **kwds)`` does not raise *exc*."""
- pass
-
-
-class CustomEx(Exception):
- """My custom exception."""
-
- def f(self):
- """Exception method."""
-
-
-class CustomDataDescriptor(object):
- """Descriptor class docstring."""
-
- def __init__(self, doc):
- self.__doc__ = doc
-
- def __get__(self, obj, type=None):
- if obj is None:
- return self
- return 42
-
- def meth(self):
- """Function."""
- return "The Answer"
-
-
-class CustomDataDescriptorMeta(type):
- """Descriptor metaclass docstring."""
-
-
-@add_metaclass(CustomDataDescriptorMeta)
-class CustomDataDescriptor2(CustomDataDescriptor):
- """Descriptor class with custom metaclass docstring."""
-
-
-def _funky_classmethod(name, b, c, d, docstring=None):
- """Generates a classmethod for a class from a template by filling out
- some arguments."""
- def template(cls, a, b, c, d=4, e=5, f=6):
- return a, b, c, d, e, f
- from functools import partial
- function = partial(template, b=b, c=c, d=d)
- function.__name__ = name
- function.__doc__ = docstring
- return classmethod(function)
-
-
-class Base(object):
- def inheritedmeth(self):
- """Inherited function."""
-
-
-class Derived(Base):
- def inheritedmeth(self):
- # no docstring here
- pass
-
-
-class Class(Base):
- """Class to document."""
-
- descr = CustomDataDescriptor("Descriptor instance docstring.")
-
- def meth(self):
- """Function."""
-
- def undocmeth(self):
- pass
-
- def skipmeth(self):
- """Method that should be skipped."""
-
- def excludemeth(self):
- """Method that should be excluded."""
-
- # should not be documented
- skipattr = 'foo'
-
- #: should be documented -- süß
- attr = 'bar'
-
- @property
- def prop(self):
- """Property."""
-
- docattr = 'baz'
- """should likewise be documented -- süß"""
-
- udocattr = 'quux'
- u"""should be documented as well - süß"""
-
- # initialized to any class imported from another module
- mdocattr = StringIO()
- """should be documented as well - süß"""
-
- roger = _funky_classmethod("roger", 2, 3, 4)
-
- moore = _funky_classmethod("moore", 9, 8, 7,
- docstring="moore(a, e, f) -> happiness")
-
- def __init__(self, arg):
- self.inst_attr_inline = None #: an inline documented instance attr
- #: a documented instance attribute
- self.inst_attr_comment = None
- self.inst_attr_string = None
- """a documented instance attribute"""
- self._private_inst_attr = None #: a private instance attribute
-
- def __special1__(self):
- """documented special method"""
-
- def __special2__(self):
- # undocumented special method
- pass
-
-
-class CustomDict(dict):
- """Docstring."""
-
-
-def function(foo, *args, **kwds):
- """
- Return spam.
- """
- pass
-
-
-class Outer(object):
- """Foo"""
-
- class Inner(object):
- """Foo"""
-
- def meth(self):
- """Foo"""
-
- # should be documented as an alias
- factory = dict
-
-
-class DocstringSig(object):
- def meth(self):
- """meth(FOO, BAR=1) -> BAZ
-First line of docstring
-
- rest of docstring
- """
-
- def meth2(self):
- """First line, no signature
- Second line followed by indentation::
-
- indented line
- """
-
- @property
- def prop1(self):
- """DocstringSig.prop1(self)
- First line of docstring
- """
- return 123
-
- @property
- def prop2(self):
- """First line of docstring
- Second line of docstring
- """
- return 456
-
-
-class StrRepr(str):
- def __repr__(self):
- return self
-
-
-class AttCls(object):
- a1 = StrRepr('hello\nworld')
- a2 = None
-
-
-class InstAttCls(object):
- """Class with documented class and instance attributes."""
-
- #: Doc comment for class attribute InstAttCls.ca1.
- #: It can have multiple lines.
- ca1 = 'a'
-
- ca2 = 'b' #: Doc comment for InstAttCls.ca2. One line only.
-
- ca3 = 'c'
- """Docstring for class attribute InstAttCls.ca3."""
-
- def __init__(self):
- #: Doc comment for instance attribute InstAttCls.ia1
- self.ia1 = 'd'
-
- self.ia2 = 'e'
- """Docstring for instance attribute InstAttCls.ia2."""
-
-
-class EnumCls(enum.Enum):
- """
- this is enum class
- """
-
- #: doc for val1
- val1 = 12
- 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')
diff --git a/tests/test_build.py b/tests/test_build.py
index 185b5cda4..df0458aa3 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -5,7 +5,7 @@
Test all builders.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -34,8 +34,8 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir):
basedir = sphinx_test_tempdir / request.node.originalname
# Windows with versions prior to 3.2 (I think) doesn't support unicode on system path
# so we force a non-unicode path in that case
- if sys.platform == "win32" and \
- not (sys.version_info.major >= 3 and sys.version_info.minor >= 2):
+ if (sys.platform == "win32" and
+ not (sys.version_info.major >= 3 and sys.version_info.minor >= 2)):
return basedir / 'all'
try:
srcdir = basedir / test_name
@@ -59,13 +59,14 @@ 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', 'epub', 'applehelp', 'changes', 'xml',
- 'pseudoxml', 'man', 'linkcheck',
+ 'dirhtml', 'singlehtml', 'pickle', 'json', 'text', 'htmlhelp', 'qthelp',
+ 'epub', 'applehelp', 'changes', 'xml', 'pseudoxml', 'linkcheck',
],
)
@mock.patch('sphinx.builders.linkcheck.requests.head',
diff --git a/tests/test_build_applehelp.py b/tests/test_build_applehelp.py
index 4418cb265..31d4ca4df 100644
--- a/tests/test_build_applehelp.py
+++ b/tests/test_build_applehelp.py
@@ -7,7 +7,7 @@
test the HTML itself; that's already handled by
:file:`test_build_html.py`.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_build_epub.py b/tests/test_build_epub.py
index 397547734..e5d86b0ed 100644
--- a/tests/test_build_epub.py
+++ b/tests/test_build_epub.py
@@ -245,5 +245,3 @@ def test_epub_writing_mode(app):
# vertical / writing-mode (CSS)
css = (app.outdir / '_static' / 'epub.css').text()
assert 'writing-mode: vertical-rl;' in css
-
-
diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py
index f256140fe..c14013f9a 100644
--- a/tests/test_build_gettext.py
+++ b/tests/test_build_gettext.py
@@ -5,7 +5,7 @@
Test the build process with gettext builder with the test root.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index dc06491e8..153ff5165 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -5,7 +5,7 @@
Test the HTML builder and check output against XPath.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -15,7 +15,6 @@ from itertools import cycle, chain
from six import PY3
-from sphinx import __display_version__
from sphinx.util.inventory import InventoryFile
from sphinx.testing.util import remove_unicode_literals, strip_escseq
import xml.etree.cElementTree as ElementTree
@@ -126,24 +125,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 +137,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']", ''),
@@ -209,8 +181,8 @@ def test_static_output(app):
r'-| |-'),
],
'autodoc.html': [
- (".//dt[@id='test_autodoc.Class']", ''),
- (".//dt[@id='test_autodoc.function']/em", r'\*\*kwds'),
+ (".//dt[@id='autodoc_target.Class']", ''),
+ (".//dt[@id='autodoc_target.function']/em", r'\*\*kwds'),
(".//dd/p", r'Return spam\.'),
],
'extapi.html': [
@@ -377,7 +349,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 +381,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 <Tests>'),
- ],
'genindex.html': [
# index entries
(".//a/strong", "Main"),
@@ -1145,16 +1113,28 @@ 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_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()
- content_otherext = (app.outdir / 'otherext.html').text()
- content_images = (app.outdir / 'images.html').text()
+ assert (app.outdir / '_sources' / 'index.rst.txt').exists()
+
+
+@pytest.mark.sphinx('html', testroot='basic', confoverrides={'html_sourcelink_suffix': '.rst'})
+def test_html_sourcelink_suffix_same(app):
+ app.builder.build_all()
+ assert (app.outdir / '_sources' / 'index.rst').exists()
- assert '<a href="_sources/otherext.foo"' in content_otherext
- assert '<a href="_sources/images.txt"' in content_images
- assert (app.outdir / '_sources' / 'otherext.foo').exists()
- assert (app.outdir / '_sources' / 'images.txt').exists()
+
+@pytest.mark.sphinx('html', testroot='basic', confoverrides={'html_sourcelink_suffix': ''})
+def test_html_sourcelink_suffix_empty(app):
+ app.builder.build_all()
+ assert (app.outdir / '_sources' / 'index.rst').exists()
@pytest.mark.sphinx('html', testroot='html_entity')
@@ -1263,3 +1243,16 @@ def test_html_sidebar(app, status, warning):
assert '<h3>Related Topics</h3>' not in result
assert '<h3>This Page</h3>' not in result
assert '<h3>Quick search</h3>' not in result
+
+
+@pytest.mark.parametrize('fname,expect', flat_dict({
+ 'index.html': [(".//em/a[@href='https://example.com/man.1']", "", True),
+ (".//em/a[@href='https://example.com/ls.1']", "", True),
+ (".//em/a[@href='https://example.com/sphinx.']", "", True)]
+ }))
+@pytest.mark.sphinx('html', testroot='manpage_url', confoverrides={
+ 'manpages_url': 'https://example.com/{page}.{section}'})
+@pytest.mark.test_params(shared_result='test_build_html_manpage_url')
+def test_html_manpage(app, cached_etree_parse, fname, expect):
+ app.build()
+ check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
diff --git a/tests/test_build_html5.py b/tests/test_build_html5.py
index 771994ca6..4ac70be51 100644
--- a/tests/test_build_html5.py
+++ b/tests/test_build_html5.py
@@ -10,7 +10,7 @@
https://github.com/sphinx-doc/sphinx/pull/2805/files
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -90,8 +90,8 @@ def cached_etree_parse():
r'-| |-'),
],
'autodoc.html': [
- (".//dt[@id='test_autodoc.Class']", ''),
- (".//dt[@id='test_autodoc.function']/em", r'\*\*kwds'),
+ (".//dt[@id='autodoc_target.Class']", ''),
+ (".//dt[@id='autodoc_target.function']/em", r'\*\*kwds'),
(".//dd/p", r'Return spam\.'),
],
'extapi.html': [
@@ -251,7 +251,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'),
@@ -284,9 +283,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 <Tests>'),
- ],
'genindex.html': [
# index entries
(".//a/strong", "Main"),
diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py
index b78bcf637..e7b61ad0c 100644
--- a/tests/test_build_latex.py
+++ b/tests/test_build_latex.py
@@ -5,7 +5,7 @@
Test the build process with LaTeX builder with the test root.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
@@ -165,13 +165,15 @@ def test_latex_warnings(app, status, warning):
@pytest.mark.sphinx('latex', testroot='basic')
-def test_latex_title(app, status, warning):
+def test_latex_basic(app, status, warning):
app.builder.build_all()
result = (app.outdir / 'test.tex').text(encoding='utf8')
print(result)
print(status.getvalue())
print(warning.getvalue())
- assert '\\title{The basic Sphinx documentation for testing}' in result
+ assert r'\title{The basic Sphinx documentation for testing}' in result
+ assert r'\release{}' in result
+ assert r'\renewcommand{\releasename}{}' in result
@pytest.mark.sphinx('latex', testroot='latex-title')
@@ -184,6 +186,18 @@ def test_latex_title_after_admonitions(app, status, warning):
assert '\\title{test-latex-title}' in result
+@pytest.mark.sphinx('latex', testroot='basic',
+ confoverrides={'release': '1.0'})
+def test_latex_release(app, status, warning):
+ app.builder.build_all()
+ result = (app.outdir / 'test.tex').text(encoding='utf8')
+ print(result)
+ print(status.getvalue())
+ print(warning.getvalue())
+ assert r'\release{1.0}' in result
+ assert r'\renewcommand{\releasename}{Release}' in result
+
+
@pytest.mark.sphinx('latex', testroot='numfig',
confoverrides={'numfig': True})
def test_numref(app, status, warning):
@@ -335,6 +349,56 @@ def test_numref_with_language_ja(app, status, warning):
'\\nameref{\\detokenize{foo:foo}}}') in result
+@pytest.mark.sphinx('latex', testroot='latex-numfig')
+def test_latex_obey_numfig_is_false(app, status, warning):
+ app.builder.build_all()
+
+ result = (app.outdir / 'SphinxManual.tex').text(encoding='utf8')
+ assert '\\usepackage{sphinx}' in result
+
+ result = (app.outdir / 'SphinxHowTo.tex').text(encoding='utf8')
+ assert '\\usepackage{sphinx}' in result
+
+
+@pytest.mark.sphinx(
+ 'latex', testroot='latex-numfig',
+ confoverrides={'numfig': True, 'numfig_secnum_depth': 0})
+def test_latex_obey_numfig_secnum_depth_is_zero(app, status, warning):
+ app.builder.build_all()
+
+ result = (app.outdir / 'SphinxManual.tex').text(encoding='utf8')
+ assert '\\usepackage[,nonumfigreset,mathnumfig]{sphinx}' in result
+
+ result = (app.outdir / 'SphinxHowTo.tex').text(encoding='utf8')
+ assert '\\usepackage[,nonumfigreset,mathnumfig]{sphinx}' in result
+
+
+@pytest.mark.sphinx(
+ 'latex', testroot='latex-numfig',
+ confoverrides={'numfig': True, 'numfig_secnum_depth': 2})
+def test_latex_obey_numfig_secnum_depth_is_two(app, status, warning):
+ app.builder.build_all()
+
+ result = (app.outdir / 'SphinxManual.tex').text(encoding='utf8')
+ assert '\\usepackage[,numfigreset=2,mathnumfig]{sphinx}' in result
+
+ result = (app.outdir / 'SphinxHowTo.tex').text(encoding='utf8')
+ assert '\\usepackage[,numfigreset=3,mathnumfig]{sphinx}' in result
+
+
+@pytest.mark.sphinx(
+ 'latex', testroot='latex-numfig',
+ confoverrides={'numfig': True, 'math_numfig': False})
+def test_latex_obey_numfig_but_math_numfig_false(app, status, warning):
+ app.builder.build_all()
+
+ result = (app.outdir / 'SphinxManual.tex').text(encoding='utf8')
+ assert '\\usepackage[,numfigreset=1]{sphinx}' in result
+
+ result = (app.outdir / 'SphinxHowTo.tex').text(encoding='utf8')
+ assert '\\usepackage[,numfigreset=2]{sphinx}' in result
+
+
@pytest.mark.sphinx('latex')
def test_latex_add_latex_package(app, status, warning):
app.add_latex_package('foo')
@@ -712,19 +776,16 @@ def test_latex_logo_if_not_found(app, status, warning):
assert isinstance(exc, SphinxError)
-@pytest.mark.sphinx('latex', testroot='toctree-maxdepth',
- confoverrides={'latex_documents': [
- ('index', 'SphinxTests.tex', 'Sphinx Tests Documentation',
- 'Georg Brandl', 'manual'),
- ]})
+@pytest.mark.sphinx('latex', testroot='toctree-maxdepth')
def test_toctree_maxdepth_manual(app, status, warning):
app.builder.build_all()
- result = (app.outdir / 'SphinxTests.tex').text(encoding='utf8')
+ result = (app.outdir / 'Python.tex').text(encoding='utf8')
print(result)
print(status.getvalue())
print(warning.getvalue())
assert '\\setcounter{tocdepth}{1}' in result
assert '\\setcounter{secnumdepth}' not in result
+ assert '\\chapter{Foo}' in result
@pytest.mark.sphinx(
@@ -741,6 +802,7 @@ def test_toctree_maxdepth_howto(app, status, warning):
print(warning.getvalue())
assert '\\setcounter{tocdepth}{2}' in result
assert '\\setcounter{secnumdepth}' not in result
+ assert '\\section{Foo}' in result
@pytest.mark.sphinx(
@@ -754,6 +816,7 @@ def test_toctree_not_found(app, status, warning):
print(warning.getvalue())
assert '\\setcounter{tocdepth}' not in result
assert '\\setcounter{secnumdepth}' not in result
+ assert '\\chapter{Foo A}' in result
@pytest.mark.sphinx(
@@ -804,6 +867,26 @@ def test_latex_toplevel_sectioning_is_part(app, status, warning):
print(status.getvalue())
print(warning.getvalue())
assert '\\part{Foo}' in result
+ assert '\\chapter{Foo A}' in result
+ assert '\\chapter{Foo B}' in result
+
+
+@pytest.mark.sphinx(
+ 'latex', testroot='toctree-maxdepth',
+ confoverrides={'latex_toplevel_sectioning': 'part',
+ 'latex_documents': [
+ ('index', 'Python.tex', 'Sphinx Tests Documentation',
+ 'Georg Brandl', 'howto')
+ ]})
+def test_latex_toplevel_sectioning_is_part_with_howto(app, status, warning):
+ app.builder.build_all()
+ result = (app.outdir / 'Python.tex').text(encoding='utf8')
+ print(result)
+ print(status.getvalue())
+ print(warning.getvalue())
+ assert '\\part{Foo}' in result
+ assert '\\section{Foo A}' in result
+ assert '\\section{Foo B}' in result
@pytest.mark.sphinx(
@@ -820,6 +903,22 @@ def test_latex_toplevel_sectioning_is_chapter(app, status, warning):
@pytest.mark.sphinx(
'latex', testroot='toctree-maxdepth',
+ confoverrides={'latex_toplevel_sectioning': 'chapter',
+ 'latex_documents': [
+ ('index', 'Python.tex', 'Sphinx Tests Documentation',
+ 'Georg Brandl', 'howto')
+ ]})
+def test_latex_toplevel_sectioning_is_chapter_with_howto(app, status, warning):
+ app.builder.build_all()
+ result = (app.outdir / 'Python.tex').text(encoding='utf8')
+ print(result)
+ print(status.getvalue())
+ print(warning.getvalue())
+ assert '\\section{Foo}' in result
+
+
+@pytest.mark.sphinx(
+ 'latex', testroot='toctree-maxdepth',
confoverrides={'latex_toplevel_sectioning': 'section'})
def test_latex_toplevel_sectioning_is_section(app, status, warning):
app.builder.build_all()
diff --git a/tests/test_build_linkcheck.py b/tests/test_build_linkcheck.py
index cc3d6e24f..839a15628 100644
--- a/tests/test_build_linkcheck.py
+++ b/tests/test_build_linkcheck.py
@@ -5,7 +5,7 @@
Test the build process with manpage builder with the test root.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
diff --git a/tests/test_build_manpage.py b/tests/test_build_manpage.py
index 953332c73..3448d6eeb 100644
--- a/tests/test_build_manpage.py
+++ b/tests/test_build_manpage.py
@@ -5,7 +5,7 @@
Test the build process with manpage builder with the test root.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
diff --git a/tests/test_build_qthelp.py b/tests/test_build_qthelp.py
index 3e4815fbe..de676e6e0 100644
--- a/tests/test_build_qthelp.py
+++ b/tests/test_build_qthelp.py
@@ -7,7 +7,7 @@
test the HTML itself; that's already handled by
:file:`test_build_html.py`.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py
index c4238c659..114f194fe 100644
--- a/tests/test_build_texinfo.py
+++ b/tests/test_build_texinfo.py
@@ -5,7 +5,7 @@
Test the build process with Texinfo builder with the test root.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
diff --git a/tests/test_build_text.py b/tests/test_build_text.py
index 81e354ecd..9bfbe1206 100644
--- a/tests/test_build_text.py
+++ b/tests/test_build_text.py
@@ -5,7 +5,7 @@
Test the build process with Text builder with the test root.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import pytest
diff --git a/tests/test_catalogs.py b/tests/test_catalogs.py
index b3e17a0a1..4bfbb18a3 100644
--- a/tests/test_catalogs.py
+++ b/tests/test_catalogs.py
@@ -5,7 +5,7 @@
Test the base build process.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import shutil
diff --git a/tests/test_config.py b/tests/test_config.py
index 578f6e55c..3f38c7ab8 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -6,7 +6,7 @@
Test the sphinx.config.Config class and its handling in the
Application class.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from six import PY3, iteritems
diff --git a/tests/test_correct_year.py b/tests/test_correct_year.py
index a8058f08c..e7501bb6a 100644
--- a/tests/test_correct_year.py
+++ b/tests/test_correct_year.py
@@ -5,7 +5,7 @@
Test copyright year adjustment
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import pytest
diff --git a/tests/test_directive_code.py b/tests/test_directive_code.py
index 548a5d72c..f62f44f13 100644
--- a/tests/test_directive_code.py
+++ b/tests/test_directive_code.py
@@ -5,7 +5,7 @@
Test the code-block directive.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -349,6 +349,14 @@ def test_code_block_namedlink_latex(app, status, warning):
assert link2 in latex
+@pytest.mark.sphinx('latex', testroot='directive-code')
+def test_code_block_emphasize_latex(app, status, warning):
+ app.builder.build(['emphasize'])
+ latex = (app.outdir / 'Python.tex').text(encoding='utf-8').replace('\r\n', '\n')
+ includes = '\\fvset{hllines={, 5, 6, 13, 14, 15, 24, 25, 26, 27,}}%\n'
+ assert includes in latex
+
+
@pytest.mark.sphinx('xml', testroot='directive-code')
def test_literal_include(app, status, warning):
app.builder.build(['index'])
diff --git a/tests/test_directive_only.py b/tests/test_directive_only.py
index d8017f469..010eae384 100644
--- a/tests/test_directive_only.py
+++ b/tests/test_directive_only.py
@@ -5,7 +5,7 @@
Test the only directive with the test root.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_docutilsconf.py b/tests/test_docutilsconf.py
index fd5cf7a61..91bf8fc95 100644
--- a/tests/test_docutilsconf.py
+++ b/tests/test_docutilsconf.py
@@ -5,7 +5,7 @@
Test docutils.conf support for several writers.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -72,7 +72,7 @@ def test_texinfo(app, status, warning):
@pytest.mark.sphinx('html', testroot='docutilsconf',
docutilsconf='[general]\nsource_link=true\n')
-@pytest.mark.skip(sys.platform == "win32" and \
+@pytest.mark.skip(sys.platform == "win32" and
not (sys.version_info.major >= 3 and sys.version_info.minor >= 2),
reason="Python < 3.2 on Win32 doesn't handle non-ASCII paths right")
def test_docutils_source_link_with_nonascii_file(app, status, warning):
diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py
index 8c838fcf8..3561e76ce 100644
--- a/tests/test_domain_cpp.py
+++ b/tests/test_domain_cpp.py
@@ -5,7 +5,7 @@
Tests the C++ Domain
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -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
@@ -100,13 +101,13 @@ def test_fundamental_types():
if t == "std::nullptr_t":
id = "NSt9nullptr_tE"
return "1f%s" % id
- check("function", "void f(%s arg)" % t, {1: makeIdV1(), 2:makeIdV2()})
+ check("function", "void f(%s arg)" % t, {1: makeIdV1(), 2: makeIdV2()})
def test_expressions():
def exprCheck(expr, id):
ids = 'IE1CIA%s_1aE'
- check('class', 'template<> C<a[%s]>' % expr, {2:ids % expr, 3:ids % id})
+ check('class', 'template<> C<a[%s]>' % expr, {2: ids % expr, 3: ids % id})
# primary
exprCheck('nullptr', 'LDnE')
exprCheck('true', 'L1E')
@@ -117,9 +118,9 @@ def test_expressions():
for i in ints:
for u in unsignedSuffix:
for l in longSuffix:
- expr = i + u + l;
+ expr = i + u + l
exprCheck(expr, 'L' + expr + 'E')
- expr = i + l + u;
+ expr = i + l + u
exprCheck(expr, 'L' + expr + 'E')
for suffix in ['', 'f', 'F', 'l', 'L']:
expr = '5.0' + suffix
@@ -199,55 +200,64 @@ def test_expressions():
# a < expression that starts with something that could be a template
exprCheck('A < 42', 'lt1AL42E')
check('function', 'template<> void f(A<B, 2> &v)',
- {2:"IE1fR1AI1BX2EE", 3:"IE1fR1AI1BXL2EEE"})
+ {2: "IE1fR1AI1BX2EE", 3: "IE1fR1AI1BXL2EEE"})
exprCheck('A<1>::value', 'N1AIXL1EEE5valueE')
- check('class', "template<int T = 42> A", {2:"I_iE1A"})
- check('enumerator', 'A = std::numeric_limits<unsigned long>::max()', {2:"1A"})
+ check('class', "template<int T = 42> A", {2: "I_iE1A"})
+ check('enumerator', 'A = std::numeric_limits<unsigned long>::max()', {2: "1A"})
+
+ exprCheck('operator()()', 'clclE')
+ exprCheck('operator()<int>()', 'clclIiEE')
def test_type_definitions():
- check("type", "public bool b", {1:"b", 2:"1b"}, "bool b")
- check("type", "bool A::b", {1:"A::b", 2:"N1A1bE"})
- check("type", "bool *b", {1:"b", 2:"1b"})
- check("type", "bool *const b", {1:"b", 2:"1b"})
- check("type", "bool *volatile const b", {1:"b", 2:"1b"})
- check("type", "bool *volatile const b", {1:"b", 2:"1b"})
- check("type", "bool *volatile const *b", {1:"b", 2:"1b"})
- check("type", "bool &b", {1:"b", 2:"1b"})
- check("type", "bool b[]", {1:"b", 2:"1b"})
- check("type", "std::pair<int, int> coord", {1:"coord", 2:"5coord"})
- check("type", "long long int foo", {1:"foo", 2:"3foo"})
+ check("type", "public bool b", {1: "b", 2: "1b"}, "bool b")
+ check("type", "bool A::b", {1: "A::b", 2: "N1A1bE"})
+ check("type", "bool *b", {1: "b", 2: "1b"})
+ check("type", "bool *const b", {1: "b", 2: "1b"})
+ check("type", "bool *volatile const b", {1: "b", 2: "1b"})
+ check("type", "bool *volatile const b", {1: "b", 2: "1b"})
+ check("type", "bool *volatile const *b", {1: "b", 2: "1b"})
+ check("type", "bool &b", {1: "b", 2: "1b"})
+ check("type", "bool b[]", {1: "b", 2: "1b"})
+ check("type", "std::pair<int, int> coord", {1: "coord", 2: "5coord"})
+ check("type", "long long int foo", {1: "foo", 2: "3foo"})
check("type", 'std::vector<std::pair<std::string, long long>> module::blah',
- {1:"module::blah", 2:"N6module4blahE"})
- check("type", "std::function<void()> F", {1:"F", 2:"1F"})
- check("type", "std::function<R(A1, A2)> F", {1:"F", 2:"1F"})
- check("type", "std::function<R(A1, A2, A3)> F", {1:"F", 2:"1F"})
- check("type", "std::function<R(A1, A2, A3, As...)> F", {1:"F", 2:"1F"})
+ {1: "module::blah", 2: "N6module4blahE"})
+ check("type", "std::function<void()> F", {1: "F", 2: "1F"})
+ check("type", "std::function<R(A1, A2)> F", {1: "F", 2: "1F"})
+ check("type", "std::function<R(A1, A2, A3)> F", {1: "F", 2: "1F"})
+ check("type", "std::function<R(A1, A2, A3, As...)> F", {1: "F", 2: "1F"})
check("type", "MyContainer::const_iterator",
- {1:"MyContainer::const_iterator", 2:"N11MyContainer14const_iteratorE"})
+ {1: "MyContainer::const_iterator", 2: "N11MyContainer14const_iteratorE"})
check("type",
"public MyContainer::const_iterator",
- {1:"MyContainer::const_iterator", 2:"N11MyContainer14const_iteratorE"},
+ {1: "MyContainer::const_iterator", 2: "N11MyContainer14const_iteratorE"},
output="MyContainer::const_iterator")
# test decl specs on right
- check("type", "bool const b", {1:"b", 2:"1b"})
+ check("type", "bool const b", {1: "b", 2: "1b"})
# test name in global scope
- check("type", "bool ::B::b", {1:"B::b", 2:"N1B1bE"})
+ check("type", "bool ::B::b", {1: "B::b", 2: "N1B1bE"})
- check('type', 'A = B', {2:'1A'})
- check('type', 'A = decltype(b)', {2:'1A'})
+ check('type', 'A = B', {2: '1A'})
+ check('type', 'A = decltype(b)', {2: '1A'})
# from breathe#267 (named function parameters for function pointers
check('type', 'void (*gpio_callback_t)(struct device *port, uint32_t pin)',
- {1:'gpio_callback_t', 2:'15gpio_callback_t'})
- check('type', 'void (*f)(std::function<void(int i)> g)', {1:'f', 2:'1f'})
+ {1: 'gpio_callback_t', 2: '15gpio_callback_t'})
+ check('type', 'void (*f)(std::function<void(int i)> g)', {1: 'f', 2: '1f'})
+
+ check('type', 'T = A::template B<int>::template C<double>', {2: '1T'})
+
+ check('type', 'T = Q<A::operator()>', {2: '1T'})
+ check('type', 'T = Q<A::operator()<int>>', {2: '1T'})
+ check('type', 'T = Q<A::operator bool>', {2: '1T'})
def test_concept_definitions():
check('concept', 'template<typename Param> A::B::Concept',
- {2:'I0EN1A1B7ConceptE'})
+ {2: 'I0EN1A1B7ConceptE'})
check('concept', 'template<typename A, typename B, typename ...C> Foo',
- {2:'I00DpE3Foo'})
+ {2: 'I00DpE3Foo'})
with pytest.raises(DefinitionError):
parse('concept', 'Foo')
with pytest.raises(DefinitionError):
@@ -256,265 +266,270 @@ def test_concept_definitions():
def test_member_definitions():
check('member', ' const std::string & name = 42',
- {1:"name__ssCR", 2:"4name"}, output='const std::string &name = 42')
- check('member', ' const std::string & name', {1:"name__ssCR", 2:"4name"},
+ {1: "name__ssCR", 2: "4name"}, output='const std::string &name = 42')
+ check('member', ' const std::string & name', {1: "name__ssCR", 2: "4name"},
output='const std::string &name')
check('member', ' const std::string & name [ n ]',
- {1:"name__ssCRA", 2:"4name"}, output='const std::string &name[n]')
+ {1: "name__ssCRA", 2: "4name"}, output='const std::string &name[n]')
check('member', 'const std::vector< unsigned int, long> &name',
- {1:"name__std::vector:unsigned-i.l:CR", 2:"4name"},
+ {1: "name__std::vector:unsigned-i.l:CR", 2: "4name"},
output='const std::vector<unsigned int, long> &name')
- check('member', 'module::myclass foo[n]', {1:"foo__module::myclassA", 2:"3foo"})
- check('member', 'int *const p', {1:'p__iPC', 2:'1p'})
- check('member', 'extern int myInt', {1:'myInt__i', 2:'5myInt'})
- check('member', 'thread_local int myInt', {1:'myInt__i', 2:'5myInt'})
- check('member', 'extern thread_local int myInt', {1:'myInt__i', 2:'5myInt'})
- check('member', 'thread_local extern int myInt', {1:'myInt__i', 2:'5myInt'},
+ check('member', 'module::myclass foo[n]', {1: "foo__module::myclassA", 2: "3foo"})
+ check('member', 'int *const p', {1: 'p__iPC', 2: '1p'})
+ check('member', 'extern int myInt', {1: 'myInt__i', 2: '5myInt'})
+ check('member', 'thread_local int myInt', {1: 'myInt__i', 2: '5myInt'})
+ check('member', 'extern thread_local int myInt', {1: 'myInt__i', 2: '5myInt'})
+ check('member', 'thread_local extern int myInt', {1: 'myInt__i', 2: '5myInt'},
'extern thread_local int myInt')
def test_function_definitions():
- check('function', 'operator bool() const', {1:"castto-b-operatorC", 2:"NKcvbEv"})
+ check('function', 'operator bool() const', {1: "castto-b-operatorC", 2: "NKcvbEv"})
check('function', 'A::operator bool() const',
- {1:"A::castto-b-operatorC", 2:"NK1AcvbEv"})
+ {1: "A::castto-b-operatorC", 2: "NK1AcvbEv"})
check('function', 'A::operator bool() volatile const &',
- {1:"A::castto-b-operatorVCR", 2:"NVKR1AcvbEv"})
+ {1: "A::castto-b-operatorVCR", 2: "NVKR1AcvbEv"})
check('function', 'A::operator bool() volatile const &&',
- {1:"A::castto-b-operatorVCO", 2:"NVKO1AcvbEv"})
+ {1: "A::castto-b-operatorVCO", 2: "NVKO1AcvbEv"})
check('function', 'bool namespaced::theclass::method(arg1, arg2)',
- {1:"namespaced::theclass::method__arg1.arg2",
- 2:"N10namespaced8theclass6methodE4arg14arg2"})
+ {1: "namespaced::theclass::method__arg1.arg2",
+ 2: "N10namespaced8theclass6methodE4arg14arg2"})
x = 'std::vector<std::pair<std::string, int>> &module::test(register int ' \
'foo, bar, std::string baz = "foobar, blah, bleh") const = 0'
- check('function', x, {1:"module::test__i.bar.ssC",
- 2:"NK6module4testEi3barNSt6stringE"})
+ check('function', x, {1: "module::test__i.bar.ssC",
+ 2: "NK6module4testEi3barNSt6stringE"})
check('function', 'void f(std::pair<A, B>)',
- {1:"f__std::pair:A.B:", 2:"1fNSt4pairI1A1BEE"})
+ {1: "f__std::pair:A.B:", 2: "1fNSt4pairI1A1BEE"})
check('function', 'explicit module::myclass::foo::foo()',
- {1:"module::myclass::foo::foo", 2:"N6module7myclass3foo3fooEv"})
+ {1: "module::myclass::foo::foo", 2: "N6module7myclass3foo3fooEv"})
check('function', 'module::myclass::foo::~foo()',
- {1:"module::myclass::foo::~foo", 2:"N6module7myclass3fooD0Ev"})
+ {1: "module::myclass::foo::~foo", 2: "N6module7myclass3fooD0Ev"})
check('function', 'int printf(const char *fmt, ...)',
- {1:"printf__cCP.z", 2:"6printfPKcz"})
+ {1: "printf__cCP.z", 2: "6printfPKcz"})
check('function', 'int foo(const unsigned int j)',
- {1:"foo__unsigned-iC", 2:"3fooKj"})
+ {1: "foo__unsigned-iC", 2: "3fooKj"})
check('function', 'int foo(const int *const ptr)',
- {1:"foo__iCPC", 2:"3fooPCKi"})
+ {1: "foo__iCPC", 2: "3fooPCKi"})
check('function', 'module::myclass::operator std::vector<std::string>()',
- {1:"module::myclass::castto-std::vector:ss:-operator",
- 2:"N6module7myclasscvNSt6vectorINSt6stringEEEEv"})
+ {1: "module::myclass::castto-std::vector:ss:-operator",
+ 2: "N6module7myclasscvNSt6vectorINSt6stringEEEEv"})
check('function',
'void operator()(const boost::array<VertexID, 2> &v) const',
- {1:"call-operator__boost::array:VertexID.2:CRC",
- 2:"NKclERKN5boost5arrayI8VertexIDX2EEE",
- 3:"NKclERKN5boost5arrayI8VertexIDXL2EEEE"})
+ {1: "call-operator__boost::array:VertexID.2:CRC",
+ 2: "NKclERKN5boost5arrayI8VertexIDX2EEE",
+ 3: "NKclERKN5boost5arrayI8VertexIDXL2EEEE"})
check('function',
'void operator()(const boost::array<VertexID, 2, "foo, bar"> &v) const',
- {1:'call-operator__boost::array:VertexID.2."foo,--bar":CRC',
- 2:'NKclERKN5boost5arrayI8VertexIDX2EX"foo, bar"EEE',
- 3:'NKclERKN5boost5arrayI8VertexIDXL2EEXLA9_KcEEEE'})
+ {1: 'call-operator__boost::array:VertexID.2."foo,--bar":CRC',
+ 2: 'NKclERKN5boost5arrayI8VertexIDX2EX"foo, bar"EEE',
+ 3: 'NKclERKN5boost5arrayI8VertexIDXL2EEXLA9_KcEEEE'})
check('function', 'MyClass::MyClass(MyClass::MyClass&&)',
- {1:"MyClass::MyClass__MyClass::MyClassRR",
- 2:"N7MyClass7MyClassERRN7MyClass7MyClassE"})
- check('function', 'constexpr int get_value()', {1:"get_valueCE", 2:"9get_valuev"})
+ {1: "MyClass::MyClass__MyClass::MyClassRR",
+ 2: "N7MyClass7MyClassERRN7MyClass7MyClassE"})
+ check('function', 'constexpr int get_value()', {1: "get_valueCE", 2: "9get_valuev"})
check('function', 'static constexpr int get_value()',
- {1:"get_valueCE", 2:"9get_valuev"})
+ {1: "get_valueCE", 2: "9get_valuev"})
check('function', 'int get_value() const noexcept',
- {1:"get_valueC", 2:"NK9get_valueEv"})
+ {1: "get_valueC", 2: "NK9get_valueEv"})
check('function', 'int get_value() const noexcept = delete',
- {1:"get_valueC", 2:"NK9get_valueEv"})
+ {1: "get_valueC", 2: "NK9get_valueEv"})
check('function', 'int get_value() volatile const',
- {1:"get_valueVC", 2:"NVK9get_valueEv"})
+ {1: "get_valueVC", 2: "NVK9get_valueEv"})
check('function', 'MyClass::MyClass(MyClass::MyClass&&) = default',
- {1:"MyClass::MyClass__MyClass::MyClassRR",
- 2:"N7MyClass7MyClassERRN7MyClass7MyClassE"})
+ {1: "MyClass::MyClass__MyClass::MyClassRR",
+ 2: "N7MyClass7MyClassERRN7MyClass7MyClassE"})
check('function', 'virtual MyClass::a_virtual_function() const override',
- {1:"MyClass::a_virtual_functionC", 2:"NK7MyClass18a_virtual_functionEv"})
- check('function', 'A B() override', {1:"B", 2:"1Bv"})
- check('function', 'A B() final', {1:"B", 2:"1Bv"})
- check('function', 'A B() final override', {1:"B", 2:"1Bv"})
- check('function', 'A B() override final', {1:"B", 2:"1Bv"},
+ {1: "MyClass::a_virtual_functionC", 2: "NK7MyClass18a_virtual_functionEv"})
+ check('function', 'A B() override', {1: "B", 2: "1Bv"})
+ check('function', 'A B() final', {1: "B", 2: "1Bv"})
+ check('function', 'A B() final override', {1: "B", 2: "1Bv"})
+ check('function', 'A B() override final', {1: "B", 2: "1Bv"},
output='A B() final override')
check('function', 'MyClass::a_member_function() volatile',
- {1:"MyClass::a_member_functionV", 2:"NV7MyClass17a_member_functionEv"})
+ {1: "MyClass::a_member_functionV", 2: "NV7MyClass17a_member_functionEv"})
check('function', 'MyClass::a_member_function() volatile const',
- {1:"MyClass::a_member_functionVC", 2:"NVK7MyClass17a_member_functionEv"})
+ {1: "MyClass::a_member_functionVC", 2: "NVK7MyClass17a_member_functionEv"})
check('function', 'MyClass::a_member_function() &&',
- {1:"MyClass::a_member_functionO", 2:"NO7MyClass17a_member_functionEv"})
+ {1: "MyClass::a_member_functionO", 2: "NO7MyClass17a_member_functionEv"})
check('function', 'MyClass::a_member_function() &',
- {1:"MyClass::a_member_functionR", 2:"NR7MyClass17a_member_functionEv"})
+ {1: "MyClass::a_member_functionR", 2: "NR7MyClass17a_member_functionEv"})
check('function', 'MyClass::a_member_function() const &',
- {1:"MyClass::a_member_functionCR", 2:"NKR7MyClass17a_member_functionEv"})
+ {1: "MyClass::a_member_functionCR", 2: "NKR7MyClass17a_member_functionEv"})
check('function', 'int main(int argc, char *argv[])',
- {1:"main__i.cPA", 2:"4mainiA_Pc"})
+ {1: "main__i.cPA", 2: "4mainiA_Pc"})
check('function', 'MyClass &MyClass::operator++()',
- {1:"MyClass::inc-operator", 2:"N7MyClassppEv"})
+ {1: "MyClass::inc-operator", 2: "N7MyClassppEv"})
check('function', 'MyClass::pointer MyClass::operator->()',
- {1:"MyClass::pointer-operator", 2:"N7MyClassptEv"})
+ {1: "MyClass::pointer-operator", 2: "N7MyClassptEv"})
x = 'std::vector<std::pair<std::string, int>> &module::test(register int ' \
'foo, bar[n], std::string baz = "foobar, blah, bleh") const = 0'
- check('function', x, {1:"module::test__i.barA.ssC",
- 2:"NK6module4testEiAn_3barNSt6stringE",
- 3:"NK6module4testEiA1n_3barNSt6stringE"})
+ check('function', x, {1: "module::test__i.barA.ssC",
+ 2: "NK6module4testEiAn_3barNSt6stringE",
+ 3: "NK6module4testEiA1n_3barNSt6stringE"})
check('function',
'int foo(Foo f = Foo(double(), std::make_pair(int(2), double(3.4))))',
- {1:"foo__Foo", 2:"3foo3Foo"})
- check('function', 'int foo(A a = x(a))', {1:"foo__A", 2:"3foo1A"})
+ {1: "foo__Foo", 2: "3foo3Foo"})
+ check('function', 'int foo(A a = x(a))', {1: "foo__A", 2: "3foo1A"})
with pytest.raises(DefinitionError):
parse('function', 'int foo(B b=x(a)')
with pytest.raises(DefinitionError):
parse('function', 'int foo)C c=x(a))')
with pytest.raises(DefinitionError):
parse('function', 'int foo(D d=x(a')
- check('function', 'int foo(const A&... a)', {1:"foo__ACRDp", 2:"3fooDpRK1A"})
- check('function', 'virtual void f()', {1:"f", 2:"1fv"})
+ check('function', 'int foo(const A&... a)', {1: "foo__ACRDp", 2: "3fooDpRK1A"})
+ check('function', 'virtual void f()', {1: "f", 2: "1fv"})
# test for ::nestedName, from issue 1738
check("function", "result(int val, ::std::error_category const &cat)",
- {1:"result__i.std::error_categoryCR", 2:"6resultiRNSt14error_categoryE"})
- check("function", "int *f()", {1:"f", 2:"1fv"})
+ {1: "result__i.std::error_categoryCR", 2: "6resultiRNSt14error_categoryE"})
+ check("function", "int *f()", {1: "f", 2: "1fv"})
# tests derived from issue #1753 (skip to keep sanity)
- check("function", "f(int (&array)[10])", {2:"1fRA10_i", 3:"1fRAL10E_i"})
- check("function", "void f(int (&array)[10])", {2:"1fRA10_i", 3:"1fRAL10E_i"})
- check("function", "void f(float *q(double))", {2:"1fFPfdE"})
- check("function", "void f(float *(*q)(double))", {2:"1fPFPfdE"})
- check("function", "void f(float (*q)(double))", {2:"1fPFfdE"})
- check("function", "int (*f(double d))(float)", {1:"f__double", 2:"1fd"})
- check("function", "int (*f(bool b))[5]", {1:"f__b", 2:"1fb"})
+ check("function", "f(int (&array)[10])", {2: "1fRA10_i", 3: "1fRAL10E_i"})
+ check("function", "void f(int (&array)[10])", {2: "1fRA10_i", 3: "1fRAL10E_i"})
+ check("function", "void f(float *q(double))", {2: "1fFPfdE"})
+ check("function", "void f(float *(*q)(double))", {2: "1fPFPfdE"})
+ check("function", "void f(float (*q)(double))", {2: "1fPFfdE"})
+ check("function", "int (*f(double d))(float)", {1: "f__double", 2: "1fd"})
+ check("function", "int (*f(bool b))[5]", {1: "f__b", 2: "1fb"})
check("function", "int (*A::f(double d) const)(float)",
- {1:"A::f__doubleC", 2:"NK1A1fEd"})
+ {1: "A::f__doubleC", 2: "NK1A1fEd"})
check("function", "void f(std::shared_ptr<int(double)> ptr)",
- {2:"1fNSt10shared_ptrIFidEEE"})
- check("function", "void f(int *const p)", {1:"f__iPC", 2:"1fPCi"})
- check("function", "void f(int *volatile const p)", {1:"f__iPVC", 2:"1fPVCi"})
+ {2: "1fNSt10shared_ptrIFidEEE"})
+ check("function", "void f(int *const p)", {1: "f__iPC", 2: "1fPCi"})
+ check("function", "void f(int *volatile const p)", {1: "f__iPVC", 2: "1fPVCi"})
- check('function', 'extern int f()', {1:'f', 2:'1fv'})
+ check('function', 'extern int f()', {1: 'f', 2: '1fv'})
- check('function', 'decltype(auto) f()', {1: 'f', 2:"1fv"})
+ check('function', 'decltype(auto) f()', {1: 'f', 2: "1fv"})
# TODO: make tests for functions in a template, e.g., Test<int&&()>
# such that the id generation for function type types is correct.
check('function', 'friend std::ostream &f(std::ostream&, int)',
- {1:'f__osR.i', 2:'1fRNSt7ostreamEi'})
+ {1: 'f__osR.i', 2: '1fRNSt7ostreamEi'})
# from breathe#223
- check('function', 'void f(struct E e)', {1:'f__E', 2:'1f1E'})
- check('function', 'void f(class E e)', {1:'f__E', 2:'1f1E'})
- check('function', 'void f(typename E e)', {1:'f__E', 2:'1f1E'})
- check('function', 'void f(enum E e)', {1:'f__E', 2:'1f1E'})
- check('function', 'void f(union E e)', {1:'f__E', 2:'1f1E'})
+ check('function', 'void f(struct E e)', {1: 'f__E', 2: '1f1E'})
+ check('function', 'void f(class E e)', {1: 'f__E', 2: '1f1E'})
+ check('function', 'void f(typename E e)', {1: 'f__E', 2: '1f1E'})
+ check('function', 'void f(enum E e)', {1: 'f__E', 2: '1f1E'})
+ check('function', 'void f(union E e)', {1: 'f__E', 2: '1f1E'})
# pointer to member (function)
- check('function', 'void f(int C::*)', {2:'1fM1Ci'})
- check('function', 'void f(int C::* p)', {2:'1fM1Ci'})
- check('function', 'void f(int ::C::* p)', {2:'1fM1Ci'})
- check('function', 'void f(int C::* const)', {2:'1fKM1Ci'})
- check('function', 'void f(int C::* const&)', {2:'1fRKM1Ci'})
- check('function', 'void f(int C::* volatile)', {2:'1fVM1Ci'})
- check('function', 'void f(int C::* const volatile)', {2:'1fVKM1Ci'},
+ check('function', 'void f(int C::*)', {2: '1fM1Ci'})
+ check('function', 'void f(int C::* p)', {2: '1fM1Ci'})
+ check('function', 'void f(int ::C::* p)', {2: '1fM1Ci'})
+ check('function', 'void f(int C::* const)', {2: '1fKM1Ci'})
+ check('function', 'void f(int C::* const&)', {2: '1fRKM1Ci'})
+ check('function', 'void f(int C::* volatile)', {2: '1fVM1Ci'})
+ check('function', 'void f(int C::* const volatile)', {2: '1fVKM1Ci'},
output='void f(int C::* volatile const)')
- check('function', 'void f(int C::* volatile const)', {2:'1fVKM1Ci'})
- check('function', 'void f(int (C::*)(float, double))', {2:'1fM1CFifdE'})
- check('function', 'void f(int (C::* p)(float, double))', {2:'1fM1CFifdE'})
- check('function', 'void f(int (::C::* p)(float, double))', {2:'1fM1CFifdE'})
- check('function', 'void f(void (C::*)() const &)', {2:'1fM1CKRFvvE'})
- check('function', 'int C::* f(int, double)', {2:'1fid'})
- check('function', 'void f(int C::* *)', {2:'1fPM1Ci'})
+ check('function', 'void f(int C::* volatile const)', {2: '1fVKM1Ci'})
+ check('function', 'void f(int (C::*)(float, double))', {2: '1fM1CFifdE'})
+ check('function', 'void f(int (C::* p)(float, double))', {2: '1fM1CFifdE'})
+ check('function', 'void f(int (::C::* p)(float, double))', {2: '1fM1CFifdE'})
+ check('function', 'void f(void (C::*)() const &)', {2: '1fM1CKRFvvE'})
+ check('function', 'int C::* f(int, double)', {2: '1fid'})
+ check('function', 'void f(int C::* *)', {2: '1fPM1Ci'})
def test_operators():
check('function', 'void operator new [ ] ()',
- {1:"new-array-operator", 2:"nav"}, output='void operator new[]()')
+ {1: "new-array-operator", 2: "nav"}, output='void operator new[]()')
check('function', 'void operator delete ()',
- {1:"delete-operator", 2:"dlv"}, output='void operator delete()')
+ {1: "delete-operator", 2: "dlv"}, output='void operator delete()')
check('function', 'operator bool() const',
- {1:"castto-b-operatorC", 2:"NKcvbEv"}, output='operator bool() const')
+ {1: "castto-b-operatorC", 2: "NKcvbEv"}, output='operator bool() const')
check('function', 'void operator * ()',
- {1:"mul-operator", 2:"mlv"}, output='void operator*()')
+ {1: "mul-operator", 2: "mlv"}, output='void operator*()')
check('function', 'void operator - ()',
- {1:"sub-operator", 2:"miv"}, output='void operator-()')
+ {1: "sub-operator", 2: "miv"}, output='void operator-()')
check('function', 'void operator + ()',
- {1:"add-operator", 2:"plv"}, output='void operator+()')
+ {1: "add-operator", 2: "plv"}, output='void operator+()')
check('function', 'void operator = ()',
- {1:"assign-operator", 2:"aSv"}, output='void operator=()')
+ {1: "assign-operator", 2: "aSv"}, output='void operator=()')
check('function', 'void operator / ()',
- {1:"div-operator", 2:"dvv"}, output='void operator/()')
+ {1: "div-operator", 2: "dvv"}, output='void operator/()')
check('function', 'void operator % ()',
- {1:"mod-operator", 2:"rmv"}, output='void operator%()')
+ {1: "mod-operator", 2: "rmv"}, output='void operator%()')
check('function', 'void operator ! ()',
- {1:"not-operator", 2:"ntv"}, output='void operator!()')
+ {1: "not-operator", 2: "ntv"}, output='void operator!()')
check('function', 'void operator "" _udl()',
- {2:'li4_udlv'}, output='void operator""_udl()')
+ {2: 'li4_udlv'}, output='void operator""_udl()')
def test_class_definitions():
- check('class', 'public A', {1:"A", 2:"1A"}, output='A')
- check('class', 'private A', {1:"A", 2:"1A"})
- check('class', 'A final', {1:'A', 2:'1A'})
+ check('class', 'public A', {1: "A", 2: "1A"}, output='A')
+ check('class', 'private A', {1: "A", 2: "1A"})
+ check('class', 'A final', {1: 'A', 2: '1A'})
# test bases
- check('class', 'A', {1:"A", 2:"1A"})
- check('class', 'A::B::C', {1:"A::B::C", 2:"N1A1B1CE"})
- check('class', 'A : B', {1:"A", 2:"1A"})
- check('class', 'A : private B', {1:"A", 2:"1A"}, output='A : B')
- check('class', 'A : public B', {1:"A", 2:"1A"})
- check('class', 'A : B, C', {1:"A", 2:"1A"})
- check('class', 'A : B, protected C, D', {1:"A", 2:"1A"})
- check('class', 'A : virtual private B', {1:'A', 2:'1A'}, output='A : virtual B')
- check('class', 'A : B, virtual C', {1:'A', 2:'1A'})
- check('class', 'A : public virtual B', {1:'A', 2:'1A'})
- check('class', 'A : B, C...', {1:'A', 2:'1A'})
- check('class', 'A : B..., C', {1:'A', 2:'1A'})
+ check('class', 'A', {1: "A", 2: "1A"})
+ check('class', 'A::B::C', {1: "A::B::C", 2: "N1A1B1CE"})
+ check('class', 'A : B', {1: "A", 2: "1A"})
+ check('class', 'A : private B', {1: "A", 2: "1A"}, output='A : B')
+ check('class', 'A : public B', {1: "A", 2: "1A"})
+ check('class', 'A : B, C', {1: "A", 2: "1A"})
+ check('class', 'A : B, protected C, D', {1: "A", 2: "1A"})
+ check('class', 'A : virtual private B', {1: 'A', 2: '1A'}, output='A : virtual B')
+ check('class', 'A : B, virtual C', {1: 'A', 2: '1A'})
+ check('class', 'A : public virtual B', {1: 'A', 2: '1A'})
+ check('class', 'A : B, C...', {1: 'A', 2: '1A'})
+ check('class', 'A : B..., C', {1: 'A', 2: '1A'})
# from #4094
- check('class', 'template<class, class = std::void_t<>> has_var', {2:'I00E7has_var'})
- check('class', 'template<class T> has_var<T, std::void_t<decltype(&T::var)>>', {2:'I0E7has_varI1TNSt6void_tIDTadN1T3varEEEEE'})
+ check('class', 'template<class, class = std::void_t<>> has_var', {2: 'I00E7has_var'})
+ check('class', 'template<class T> has_var<T, std::void_t<decltype(&T::var)>>',
+ {2: 'I0E7has_varI1TNSt6void_tIDTadN1T3varEEEEE'})
def test_enum_definitions():
- check('enum', 'A', {2:"1A"})
- check('enum', 'A : std::underlying_type<B>::type', {2:"1A"})
- check('enum', 'A : unsigned int', {2:"1A"})
- check('enum', 'public A', {2:"1A"}, output='A')
- check('enum', 'private A', {2:"1A"})
+ check('enum', 'A', {2: "1A"})
+ check('enum', 'A : std::underlying_type<B>::type', {2: "1A"})
+ check('enum', 'A : unsigned int', {2: "1A"})
+ check('enum', 'public A', {2: "1A"}, output='A')
+ check('enum', 'private A', {2: "1A"})
- check('enumerator', 'A', {2:"1A"})
- check('enumerator', 'A = std::numeric_limits<unsigned long>::max()', {2:"1A"})
+ check('enumerator', 'A', {2: "1A"})
+ check('enumerator', 'A = std::numeric_limits<unsigned long>::max()', {2: "1A"})
def test_templates():
- check('class', "A<T>", {2:"IE1AI1TE"}, output="template<> A<T>")
+ check('class', "A<T>", {2: "IE1AI1TE"}, output="template<> A<T>")
# first just check which objects support templating
- check('class', "template<> A", {2:"IE1A"})
- check('function', "template<> void A()", {2:"IE1Av"})
- check('member', "template<> A a", {2:"IE1a"})
- check('type', "template<> a = A", {2:"IE1a"})
+ check('class', "template<> A", {2: "IE1A"})
+ check('function', "template<> void A()", {2: "IE1Av"})
+ check('member', "template<> A a", {2: "IE1a"})
+ check('type', "template<> a = A", {2: "IE1a"})
with pytest.raises(DefinitionError):
parse('enum', "template<> A")
with pytest.raises(DefinitionError):
parse('enumerator', "template<> A")
# then all the real tests
- check('class', "template<typename T1, typename T2> A", {2:"I00E1A"})
- check('type', "template<> a", {2:"IE1a"})
+ check('class', "template<typename T1, typename T2> A", {2: "I00E1A"})
+ check('type', "template<> a", {2: "IE1a"})
- check('class', "template<typename T> A", {2:"I0E1A"})
- check('class', "template<class T> A", {2:"I0E1A"})
- check('class', "template<typename ...T> A", {2:"IDpE1A"})
- check('class', "template<typename...> A", {2:"IDpE1A"})
- check('class', "template<typename = Test> A", {2:"I0E1A"})
- check('class', "template<typename T = Test> A", {2:"I0E1A"})
+ check('class', "template<typename T> A", {2: "I0E1A"})
+ check('class', "template<class T> A", {2: "I0E1A"})
+ check('class', "template<typename ...T> A", {2: "IDpE1A"})
+ check('class', "template<typename...> A", {2: "IDpE1A"})
+ check('class', "template<typename = Test> A", {2: "I0E1A"})
+ check('class', "template<typename T = Test> A", {2: "I0E1A"})
- check('class', "template<template<typename> typename T> A", {2:"II0E0E1A"})
- check('class', "template<int> A", {2:"I_iE1A"})
- check('class', "template<int T> A", {2:"I_iE1A"})
- check('class', "template<int... T> A", {2:"I_DpiE1A"})
- check('class', "template<int T = 42> A", {2:"I_iE1A"})
- check('class', "template<int = 42> A", {2:"I_iE1A"})
+ check('class', "template<template<typename> typename T> A", {2: "II0E0E1A"})
+ check('class', "template<template<typename> typename> A", {2: "II0E0E1A"})
+ check('class', "template<template<typename> typename ...T> A", {2: "II0EDpE1A"})
+ check('class', "template<template<typename> typename...> A", {2: "II0EDpE1A"})
- check('class', "template<> A<NS::B<>>", {2:"IE1AIN2NS1BIEEE"})
+ check('class', "template<int> A", {2: "I_iE1A"})
+ check('class', "template<int T> A", {2: "I_iE1A"})
+ check('class', "template<int... T> A", {2: "I_DpiE1A"})
+ check('class', "template<int T = 42> A", {2: "I_iE1A"})
+ check('class', "template<int = 42> A", {2: "I_iE1A"})
+
+ check('class', "template<> A<NS::B<>>", {2: "IE1AIN2NS1BIEEE"})
# from #2058
check('function',
@@ -522,8 +537,8 @@ def test_templates():
"inline std::basic_ostream<Char, Traits> &operator<<("
"std::basic_ostream<Char, Traits> &os, "
"const c_string_view_base<const Char, Traits> &str)",
- {2:"I00ElsRNSt13basic_ostreamI4Char6TraitsEE"
- "RK18c_string_view_baseIK4Char6TraitsE"})
+ {2: "I00ElsRNSt13basic_ostreamI4Char6TraitsEE"
+ "RK18c_string_view_baseIK4Char6TraitsE"})
# template introductions
with pytest.raises(DefinitionError):
@@ -531,32 +546,42 @@ def test_templates():
with pytest.raises(DefinitionError):
parse('enumerator', 'abc::ns::foo{id_0, id_1, id_2} A')
check('class', 'abc::ns::foo{id_0, id_1, id_2} xyz::bar',
- {2:'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'})
+ {2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'})
check('class', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar',
- {2:'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'})
+ {2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'})
check('class', 'abc::ns::foo{id_0, id_1, id_2} xyz::bar<id_0, id_1, id_2>',
- {2:'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barI4id_04id_14id_2EE'})
+ {2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barI4id_04id_14id_2EE'})
check('class', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar<id_0, id_1, id_2...>',
- {2:'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barI4id_04id_1Dp4id_2EE'})
+ {2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barI4id_04id_1Dp4id_2EE'})
- check('class', 'template<> Concept{U} A<int>::B', {2:'IEI0EX7ConceptI1UEEN1AIiE1BE'})
+ check('class', 'template<> Concept{U} A<int>::B', {2: 'IEI0EX7ConceptI1UEEN1AIiE1BE'})
check('type', 'abc::ns::foo{id_0, id_1, id_2} xyz::bar = ghi::qux',
- {2:'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'})
+ {2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'})
check('type', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar = ghi::qux',
- {2:'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'})
+ {2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'})
check('function', 'abc::ns::foo{id_0, id_1, id_2} void xyz::bar()',
- {2:'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barEv'})
+ {2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barEv'})
check('function', 'abc::ns::foo{id_0, id_1, ...id_2} void xyz::bar()',
- {2:'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barEv'})
+ {2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barEv'})
check('member', 'abc::ns::foo{id_0, id_1, id_2} ghi::qux xyz::bar',
- {2:'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'})
+ {2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'})
check('member', 'abc::ns::foo{id_0, id_1, ...id_2} ghi::qux xyz::bar',
- {2:'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'})
- check('concept', 'Iterator{T, U} Another', {2:'I00EX8IteratorI1T1UEE7Another'})
+ {2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'})
+ check('concept', 'Iterator{T, U} Another', {2: 'I00EX8IteratorI1T1UEE7Another'})
check('concept', 'template<typename ...Pack> Numerics = (... && Numeric<Pack>)',
- {2:'IDpE8Numerics'})
+ {2: 'IDpE8Numerics'})
+
+ # explicit specializations of members
+ check('member', 'template<> int A<int>::a', {2: 'IEN1AIiE1aE'})
+ check('member', 'template int A<int>::a', {2: 'IEN1AIiE1aE'},
+ output='template<> int A<int>::a') # same as above
+ check('member', 'template<> template<> int A<int>::B<int>::b', {2: 'IEIEN1AIiE1BIiE1bE'})
+ check('member', 'template int A<int>::B<int>::b', {2: 'IEIEN1AIiE1BIiE1bE'},
+ output='template<> template<> int A<int>::B<int>::b') # same as above
+ # defaulted constrained type parameters
+ check('type', 'template<C T = int&> A', {2: 'I_1CE1A'})
def test_template_args():
@@ -564,33 +589,32 @@ def test_template_args():
check('function',
"template<typename F> "
"void allow(F *f, typename func<F, B, G != 1>::type tt)",
- {2:"I0E5allowP1FN4funcI1F1BXG != 1EE4typeE",
- 3:"I0E5allowP1FN4funcI1F1BXne1GL1EEE4typeE"})
+ {2: "I0E5allowP1FN4funcI1F1BXG != 1EE4typeE",
+ 3: "I0E5allowP1FN4funcI1F1BXne1GL1EEE4typeE"})
# from #3542
check('type', "template<typename T> "
"enable_if_not_array_t = std::enable_if_t<!is_array<T>::value, int>",
- {2:"I0E21enable_if_not_array_t"})
-
+ {2: "I0E21enable_if_not_array_t"})
def test_attributes():
# style: C++
- check('member', '[[]] int f', {1:'f__i', 2:'1f'})
- check('member', '[ [ ] ] int f', {1:'f__i', 2:'1f'},
+ check('member', '[[]] int f', {1: 'f__i', 2: '1f'})
+ check('member', '[ [ ] ] int f', {1: 'f__i', 2: '1f'},
# this will fail when the proper grammar is implemented
output='[[ ]] int f')
- check('member', '[[a]] int f', {1:'f__i', 2:'1f'})
+ check('member', '[[a]] int f', {1: 'f__i', 2: '1f'})
# style: GNU
- check('member', '__attribute__(()) int f', {1:'f__i', 2:'1f'})
- check('member', '__attribute__((a)) int f', {1:'f__i', 2:'1f'})
- check('member', '__attribute__((a, b)) int f', {1:'f__i', 2:'1f'})
+ check('member', '__attribute__(()) int f', {1: 'f__i', 2: '1f'})
+ check('member', '__attribute__((a)) int f', {1: 'f__i', 2: '1f'})
+ check('member', '__attribute__((a, b)) int f', {1: 'f__i', 2: '1f'})
# style: user-defined id
- check('member', 'id_attr int f', {1:'f__i', 2:'1f'})
+ check('member', 'id_attr int f', {1: 'f__i', 2: '1f'})
# style: user-defined paren
- check('member', 'paren_attr() int f', {1:'f__i', 2:'1f'})
- check('member', 'paren_attr(a) int f', {1:'f__i', 2:'1f'})
- check('member', 'paren_attr("") int f', {1:'f__i', 2:'1f'})
- check('member', 'paren_attr(()[{}][]{}) int f', {1:'f__i', 2:'1f'})
+ check('member', 'paren_attr() int f', {1: 'f__i', 2: '1f'})
+ check('member', 'paren_attr(a) int f', {1: 'f__i', 2: '1f'})
+ check('member', 'paren_attr("") int f', {1: 'f__i', 2: '1f'})
+ check('member', 'paren_attr(()[{}][]{}) int f', {1: 'f__i', 2: '1f'})
with pytest.raises(DefinitionError):
parse('member', 'paren_attr(() int f')
with pytest.raises(DefinitionError):
@@ -606,7 +630,7 @@ def test_attributes():
# position: decl specs
check('function', 'static inline __attribute__(()) void f()',
- {1:'f', 2:'1fv'},
+ {1: 'f', 2: '1fv'},
output='__attribute__(()) static inline void f()')
diff --git a/tests/test_domain_js.py b/tests/test_domain_js.py
index 22faf4458..a609dcefe 100644
--- a/tests/test_domain_js.py
+++ b/tests/test_domain_js.py
@@ -5,7 +5,7 @@
Tests the JavaScript Domain
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py
index bf391053f..0c1d28dd9 100644
--- a/tests/test_domain_py.py
+++ b/tests/test_domain_py.py
@@ -5,7 +5,7 @@
Tests the Python Domain
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_domain_rst.py b/tests/test_domain_rst.py
index 1e55e92a1..8cfe7e284 100644
--- a/tests/test_domain_rst.py
+++ b/tests/test_domain_rst.py
@@ -5,7 +5,7 @@
Tests the reStructuredText domain.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_domain_std.py b/tests/test_domain_std.py
index edd5a0ebf..06573fa38 100644
--- a/tests/test_domain_std.py
+++ b/tests/test_domain_std.py
@@ -5,7 +5,7 @@
Tests the std domain
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_environment.py b/tests/test_environment.py
index 611d34577..6f9ffec08 100644
--- a/tests/test_environment.py
+++ b/tests/test_environment.py
@@ -5,7 +5,7 @@
Test the BuildEnvironment class.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import pytest
@@ -22,7 +22,7 @@ def setup_module(rootdir, sphinx_test_tempdir):
global app, env
srcdir = sphinx_test_tempdir / 'root-envtest'
if not srcdir.exists():
- (rootdir/'test-root').copytree(srcdir)
+ (rootdir / 'test-root').copytree(srcdir)
app = SphinxTestApp(srcdir=srcdir)
env = app.env
yield
diff --git a/tests/test_environment_indexentries.py b/tests/test_environment_indexentries.py
index b9de151cc..03e4d9662 100644
--- a/tests/test_environment_indexentries.py
+++ b/tests/test_environment_indexentries.py
@@ -5,7 +5,7 @@
Test the sphinx.environment.managers.indexentries.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_environment_toctree.py b/tests/test_environment_toctree.py
index f7a24d1fc..26334858b 100644
--- a/tests/test_environment_toctree.py
+++ b/tests/test_environment_toctree.py
@@ -5,7 +5,7 @@
Test the sphinx.environment.managers.toctree.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_ext_apidoc.py b/tests/test_ext_apidoc.py
index d98dbabb6..2bfc8016e 100644
--- a/tests/test_ext_apidoc.py
+++ b/tests/test_ext_apidoc.py
@@ -5,7 +5,7 @@
Test the sphinx.apidoc module.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -188,3 +188,80 @@ def test_extension_parsed(make_app, apidoc):
with open(outdir / 'conf.py') as f:
rst = f.read()
assert "sphinx.ext.mathjax" in rst
+
+
+@pytest.mark.apidoc(
+ coderoot='test-apidoc-toc/mypackage',
+ options=["--implicit-namespaces"],
+)
+def test_toc_all_references_should_exist_pep420_enabled(make_app, apidoc):
+ """All references in toc should exist. This test doesn't say if
+ directories with empty __init__.py and and nothing else should be
+ skipped, just ensures consistency between what's referenced in the toc
+ and what is created. This is the variant with pep420 enabled.
+ """
+ outdir = apidoc.outdir
+ assert (outdir / 'conf.py').isfile()
+
+ toc = extract_toc(outdir / 'mypackage.rst')
+
+ refs = [l.strip() for l in toc.splitlines() if l.strip()]
+ found_refs = []
+ missing_files = []
+ for ref in refs:
+ if ref and ref[0] in (':', '#'):
+ continue
+ found_refs.append(ref)
+ filename = "{}.rst".format(ref)
+ if not (outdir / filename).isfile():
+ missing_files.append(filename)
+
+ assert len(missing_files) == 0, \
+ 'File(s) referenced in TOC not found: {}\n' \
+ 'TOC:\n{}'.format(", ".join(missing_files), toc)
+
+
+@pytest.mark.apidoc(
+ coderoot='test-apidoc-toc/mypackage',
+)
+def test_toc_all_references_should_exist_pep420_disabled(make_app, apidoc):
+ """All references in toc should exist. This test doesn't say if
+ directories with empty __init__.py and and nothing else should be
+ skipped, just ensures consistency between what's referenced in the toc
+ and what is created. This is the variant with pep420 disabled.
+ """
+ outdir = apidoc.outdir
+ assert (outdir / 'conf.py').isfile()
+
+ toc = extract_toc(outdir / 'mypackage.rst')
+
+ refs = [l.strip() for l in toc.splitlines() if l.strip()]
+ found_refs = []
+ missing_files = []
+ for ref in refs:
+ if ref and ref[0] in (':', '#'):
+ continue
+ filename = "{}.rst".format(ref)
+ found_refs.append(ref)
+ if not (outdir / filename).isfile():
+ missing_files.append(filename)
+
+ assert len(missing_files) == 0, \
+ 'File(s) referenced in TOC not found: {}\n' \
+ 'TOC:\n{}'.format(", ".join(missing_files), toc)
+
+
+def extract_toc(path):
+ """Helper: Extract toc section from package rst file"""
+ with open(path) as f:
+ rst = f.read()
+
+ # Read out the part containing the toctree
+ toctree_start = "\n.. toctree::\n"
+ toctree_end = "\nSubmodules"
+
+ start_idx = rst.index(toctree_start)
+ end_idx = rst.index(toctree_end, start_idx)
+ toctree = rst[start_idx + len(toctree_start):end_idx]
+
+ return toctree
diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py
index 1c1ebf7a0..e7057df0f 100644
--- a/tests/test_ext_autodoc.py
+++ b/tests/test_ext_autodoc.py
@@ -5,7 +5,7 @@
Test the autodoc extension.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_ext_autosectionlabel.py b/tests/test_ext_autosectionlabel.py
index 4726a2378..1266edbc3 100644
--- a/tests/test_ext_autosectionlabel.py
+++ b/tests/test_ext_autosectionlabel.py
@@ -5,7 +5,7 @@
Test sphinx.ext.autosectionlabel extension.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_ext_autosummary.py b/tests/test_ext_autosummary.py
index b59f0cbc8..ce5aa6e85 100644
--- a/tests/test_ext_autosummary.py
+++ b/tests/test_ext_autosummary.py
@@ -5,7 +5,7 @@
Test the autosummary extension.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -57,10 +57,14 @@ def test_mangle_signature():
@pytest.mark.sphinx('dummy', **default_kw)
-def test_get_items_summary(app, status, warning):
+def test_get_items_summary(make_app, app_params):
+ import sphinx.ext.autosummary
+ import sphinx.ext.autosummary.generate
+ args, kwargs = app_params
+ app = make_app(*args, **kwargs)
+ sphinx.ext.autosummary.generate.setup_documenters(app)
# monkey-patch Autosummary.get_items so we can easily get access to it's
# results..
- import sphinx.ext.autosummary
orig_get_items = sphinx.ext.autosummary.Autosummary.get_items
autosummary_items = {}
@@ -73,6 +77,10 @@ def test_get_items_summary(app, status, warning):
def handler(app, what, name, obj, options, lines):
assert isinstance(lines, list)
+
+ # ensure no docstring is processed twice:
+ assert 'THIS HAS BEEN HANDLED' not in lines
+ lines.append('THIS HAS BEEN HANDLED')
app.connect('autodoc-process-docstring', handler)
sphinx.ext.autosummary.Autosummary.get_items = new_get_items
@@ -81,7 +89,7 @@ def test_get_items_summary(app, status, warning):
finally:
sphinx.ext.autosummary.Autosummary.get_items = orig_get_items
- html_warnings = warning.getvalue()
+ html_warnings = app._warning.getvalue()
assert html_warnings == ''
expected_values = {
@@ -163,7 +171,8 @@ def test_import_by_name():
assert parent is sphinx.ext.autosummary
assert modname == 'sphinx.ext.autosummary'
- prefixed_name, obj, parent, modname = import_by_name('sphinx.ext.autosummary.Autosummary.get_items')
+ prefixed_name, obj, parent, modname = \
+ import_by_name('sphinx.ext.autosummary.Autosummary.get_items')
assert prefixed_name == 'sphinx.ext.autosummary.Autosummary.get_items'
assert obj == sphinx.ext.autosummary.Autosummary.get_items
assert parent is sphinx.ext.autosummary.Autosummary
diff --git a/tests/test_ext_coverage.py b/tests/test_ext_coverage.py
index ff3fb4c02..a8f222a00 100644
--- a/tests/test_ext_coverage.py
+++ b/tests/test_ext_coverage.py
@@ -5,7 +5,7 @@
Test the coverage builder.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -21,9 +21,9 @@ def test_build(app, status, warning):
py_undoc = (app.outdir / 'python.txt').text()
assert py_undoc.startswith('Undocumented Python objects\n'
'===========================\n')
- assert 'test_autodoc\n------------\n' in py_undoc
+ assert 'autodoc_target\n--------------\n' in py_undoc
assert ' * Class -- missing methods:\n' in py_undoc
- assert ' * process_docstring\n' in py_undoc
+ assert ' * raises\n' in py_undoc
assert ' * function\n' not in py_undoc # these two are documented
assert ' * Class\n' not in py_undoc # in autodoc.txt
@@ -40,9 +40,9 @@ def test_build(app, status, warning):
# the key is the full path to the header file, which isn't testable
assert list(undoc_c.values())[0] == set([('function', 'Py_SphinxTest')])
- assert 'test_autodoc' in undoc_py
- assert 'funcs' in undoc_py['test_autodoc']
- assert 'process_docstring' in undoc_py['test_autodoc']['funcs']
- assert 'classes' in undoc_py['test_autodoc']
- assert 'Class' in undoc_py['test_autodoc']['classes']
- assert 'undocmeth' in undoc_py['test_autodoc']['classes']['Class']
+ assert 'autodoc_target' in undoc_py
+ assert 'funcs' in undoc_py['autodoc_target']
+ assert 'raises' in undoc_py['autodoc_target']['funcs']
+ assert 'classes' in undoc_py['autodoc_target']
+ assert 'Class' in undoc_py['autodoc_target']['classes']
+ assert 'undocmeth' in undoc_py['autodoc_target']['classes']['Class']
diff --git a/tests/test_ext_doctest.py b/tests/test_ext_doctest.py
index fa3ad6bc4..7d907d086 100644
--- a/tests/test_ext_doctest.py
+++ b/tests/test_ext_doctest.py
@@ -5,11 +5,13 @@
Test the doctest extension.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import pytest
-from sphinx.ext.doctest import compare_version
+from sphinx.ext.doctest import is_allowed_version
+from packaging.version import InvalidVersion
+from packaging.specifiers import InvalidSpecifier
cleanup_called = 0
@@ -26,19 +28,28 @@ def test_build(app, status, warning):
assert cleanup_called == 3, 'testcleanup did not get executed enough times'
-def test_compare_version():
- assert compare_version('3.3', '3.4', '<') is True
- assert compare_version('3.3', '3.2', '<') is False
- assert compare_version('3.3', '3.4', '<=') is True
- assert compare_version('3.3', '3.2', '<=') is False
- assert compare_version('3.3', '3.3', '==') is True
- assert compare_version('3.3', '3.4', '==') is False
- assert compare_version('3.3', '3.2', '>=') is True
- assert compare_version('3.3', '3.4', '>=') is False
- assert compare_version('3.3', '3.2', '>') is True
- assert compare_version('3.3', '3.4', '>') is False
- with pytest.raises(ValueError):
- compare_version('3.3', '3.4', '+')
+def test_is_allowed_version():
+ assert is_allowed_version('<3.4', '3.3') is True
+ assert is_allowed_version('<3.4', '3.3') is True
+ assert is_allowed_version('<3.2', '3.3') is False
+ assert is_allowed_version('<=3.4', '3.3') is True
+ assert is_allowed_version('<=3.2', '3.3') is False
+ assert is_allowed_version('==3.3', '3.3') is True
+ assert is_allowed_version('==3.4', '3.3') is False
+ assert is_allowed_version('>=3.2', '3.3') is True
+ assert is_allowed_version('>=3.4', '3.3') is False
+ assert is_allowed_version('>3.2', '3.3') is True
+ assert is_allowed_version('>3.4', '3.3') is False
+ assert is_allowed_version('~=3.4', '3.4.5') is True
+ assert is_allowed_version('~=3.4', '3.5.0') is True
+
+ # invalid spec
+ with pytest.raises(InvalidSpecifier):
+ is_allowed_version('&3.4', '3.5')
+
+ # invalid version
+ with pytest.raises(InvalidVersion):
+ is_allowed_version('>3.4', 'Sphinx')
def cleanup_call():
diff --git a/tests/test_ext_githubpages.py b/tests/test_ext_githubpages.py
index 56ce7b775..18ee51480 100644
--- a/tests/test_ext_githubpages.py
+++ b/tests/test_ext_githubpages.py
@@ -5,7 +5,7 @@
Test sphinx.ext.githubpages extension.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_ext_graphviz.py b/tests/test_ext_graphviz.py
index 1d2a3ab2f..762add6f0 100644
--- a/tests/test_ext_graphviz.py
+++ b/tests/test_ext_graphviz.py
@@ -5,7 +5,7 @@
Test sphinx.ext.graphviz extension.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -40,6 +40,7 @@ def test_graphviz_png_html(app, status, warning):
r'}\" />\n</div>')
assert re.search(html, content, re.S)
+
@pytest.mark.sphinx('html', testroot='ext-graphviz',
confoverrides={'graphviz_output_format': 'svg'})
@pytest.mark.usefixtures('if_graphviz_found')
@@ -80,6 +81,7 @@ def test_graphviz_svg_html(app, status, warning):
r'</div>')
assert re.search(html, content, re.S)
+
@pytest.mark.sphinx('latex', testroot='ext-graphviz')
@pytest.mark.usefixtures('if_graphviz_found')
def test_graphviz_latex(app, status, warning):
diff --git a/tests/test_ext_ifconfig.py b/tests/test_ext_ifconfig.py
index 5c59caaaf..b4c941512 100644
--- a/tests/test_ext_ifconfig.py
+++ b/tests/test_ext_ifconfig.py
@@ -5,7 +5,7 @@
Test sphinx.ext.ifconfig extension.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_ext_imgconverter.py b/tests/test_ext_imgconverter.py
index cc84001df..8f610377c 100644
--- a/tests/test_ext_imgconverter.py
+++ b/tests/test_ext_imgconverter.py
@@ -5,7 +5,7 @@
Test sphinx.ext.imgconverter extension.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_ext_inheritance.py b/tests/test_ext_inheritance.py
new file mode 100644
index 000000000..fcf313a30
--- /dev/null
+++ b/tests/test_ext_inheritance.py
@@ -0,0 +1,133 @@
+# -*- coding: utf-8 -*-
+"""
+ test_inheritance
+ ~~~~~~~~~~~~~~~~
+
+ Tests for :mod:`sphinx.ext.inheritance_diagram` module.
+
+ :copyright: Copyright 2015 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import os
+import pytest
+from sphinx.ext.inheritance_diagram import InheritanceDiagram
+
+
+@pytest.mark.sphinx(buildername="html", testroot="inheritance")
+@pytest.mark.usefixtures('if_graphviz_found')
+def test_inheritance_diagram(app, status, warning):
+ # monkey-patch InheritaceDiagram.run() so we can get access to its
+ # results.
+ orig_run = InheritanceDiagram.run
+ graphs = {}
+
+ def new_run(self):
+ result = orig_run(self)
+ node = result[0]
+ source = os.path.basename(node.document.current_source).replace(".rst", "")
+ graphs[source] = node['graph']
+ return result
+
+ InheritanceDiagram.run = new_run
+
+ try:
+ app.builder.build_all()
+ finally:
+ InheritanceDiagram.run = orig_run
+
+ assert app.statuscode == 0
+
+ html_warnings = warning.getvalue()
+ assert html_warnings == ""
+
+ # note: it is better to split these asserts into separate test functions
+ # but I can't figure out how to build only a specific .rst file
+
+ # basic inheritance diagram showing all classes
+ for cls in graphs['basic_diagram'].class_info:
+ # use in b/c traversing order is different sometimes
+ assert cls in [
+ ('dummy.test.A', 'dummy.test.A', [], None),
+ ('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
+ ('dummy.test.C', 'dummy.test.C', ['dummy.test.A'], None),
+ ('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
+ ('dummy.test.D', 'dummy.test.D',
+ ['dummy.test.B', 'dummy.test.C'], None),
+ ('dummy.test.B', 'dummy.test.B', ['dummy.test.A'], None)
+ ]
+
+ # inheritance diagram using :parts: 1 option
+ for cls in graphs['diagram_w_parts'].class_info:
+ assert cls in [
+ ('A', 'dummy.test.A', [], None),
+ ('F', 'dummy.test.F', ['C'], None),
+ ('C', 'dummy.test.C', ['A'], None),
+ ('E', 'dummy.test.E', ['B'], None),
+ ('D', 'dummy.test.D', ['B', 'C'], None),
+ ('B', 'dummy.test.B', ['A'], None)
+ ]
+
+ # inheritance diagram with 1 top class
+ # :top-classes: dummy.test.B
+ # rendering should be
+ # A
+ # \
+ # B C
+ # / \ / \
+ # E D F
+ #
+ for cls in graphs['diagram_w_1_top_class'].class_info:
+ assert cls in [
+ ('dummy.test.A', 'dummy.test.A', [], None),
+ ('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
+ ('dummy.test.C', 'dummy.test.C', ['dummy.test.A'], None),
+ ('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
+ ('dummy.test.D', 'dummy.test.D',
+ ['dummy.test.B', 'dummy.test.C'], None),
+ ('dummy.test.B', 'dummy.test.B', [], None)
+ ]
+
+
+ # inheritance diagram with 2 top classes
+ # :top-classes: dummy.test.B, dummy.test.C
+ # Note: we're specifying separate classes, not the entire module here
+ # rendering should be
+ #
+ # B C
+ # / \ / \
+ # E D F
+ #
+ for cls in graphs['diagram_w_2_top_classes'].class_info:
+ assert cls in [
+ ('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
+ ('dummy.test.C', 'dummy.test.C', [], None),
+ ('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
+ ('dummy.test.D', 'dummy.test.D',
+ ['dummy.test.B', 'dummy.test.C'], None),
+ ('dummy.test.B', 'dummy.test.B', [], None)
+ ]
+
+ # inheritance diagram with 2 top classes and specifiying the entire module
+ # rendering should be
+ #
+ # A
+ # B C
+ # / \ / \
+ # E D F
+ #
+ # Note: dummy.test.A is included in the graph before its descendants are even processed
+ # b/c we've specified to load the entire module. The way InheritanceGraph works it is very
+ # hard to exclude parent classes once after they have been included in the graph.
+ # If you'd like to not show class A in the graph don't specify the entire module.
+ # this is a known issue.
+ for cls in graphs['diagram_module_w_2_top_classes'].class_info:
+ assert cls in [
+ ('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
+ ('dummy.test.C', 'dummy.test.C', [], None),
+ ('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
+ ('dummy.test.D', 'dummy.test.D',
+ ['dummy.test.B', 'dummy.test.C'], None),
+ ('dummy.test.B', 'dummy.test.B', [], None),
+ ('dummy.test.A', 'dummy.test.A', [], None),
+ ]
diff --git a/tests/test_ext_inheritance_diagram.py b/tests/test_ext_inheritance_diagram.py
index 40edfa937..deb04ce15 100644
--- a/tests/test_ext_inheritance_diagram.py
+++ b/tests/test_ext_inheritance_diagram.py
@@ -5,7 +5,7 @@
Test sphinx.ext.inheritance_diagram extension.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_ext_intersphinx.py b/tests/test_ext_intersphinx.py
index 594aa81b8..19f8613c6 100644
--- a/tests/test_ext_intersphinx.py
+++ b/tests/test_ext_intersphinx.py
@@ -5,7 +5,7 @@
Test the intersphinx extension.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -194,7 +194,7 @@ def test_missing_reference_stddomain(tempdir, app, status, warning):
inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2)
app.config.intersphinx_mapping = {
- 'https://docs.python.org/': inv_file,
+ 'cmd': ('https://docs.python.org/', inv_file),
}
app.config.intersphinx_cache_limit = 0
@@ -213,6 +213,12 @@ def test_missing_reference_stddomain(tempdir, app, status, warning):
rn = missing_reference(app, app.env, node, contnode)
assert rn.astext() == 'ls -l'
+ # refers inventory by name
+ kwargs = {}
+ node, contnode = fake_node('std', 'option', 'cmd:ls -l', '-l', **kwargs)
+ rn = missing_reference(app, app.env, node, contnode)
+ assert rn.astext() == '-l'
+
@pytest.mark.sphinx('html', testroot='ext-intersphinx-cppdomain')
def test_missing_reference_cppdomain(tempdir, app, status, warning):
@@ -240,7 +246,6 @@ def test_missing_reference_cppdomain(tempdir, app, status, warning):
' title="(in foo v2.0)">bartype</a>' in html)
-
def test_missing_reference_jsdomain(tempdir, app, status, warning):
inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2)
diff --git a/tests/test_ext_math.py b/tests/test_ext_math.py
index 92501a3db..5bf4ebb15 100644
--- a/tests/test_ext_math.py
+++ b/tests/test_ext_math.py
@@ -5,15 +5,29 @@
Test math extensions.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
: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 '<div class="math">\na + 1 &lt; b</div>' 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'})
@@ -139,3 +157,52 @@ def test_math_eqref_format_latex(app, status, warning):
content = (app.outdir / 'test.tex').text()
macro = r'Referencing equation Eq.\\ref{equation:math:foo}.'
assert re.search(macro, content, re.S)
+
+
+@pytest.mark.sphinx('html', testroot='ext-math',
+ confoverrides={'extensions': ['sphinx.ext.mathjax'],
+ 'numfig': True,
+ 'math_numfig': True})
+def test_mathjax_numfig_html(app, status, warning):
+ app.builder.build_all()
+
+ content = (app.outdir / 'math.html').text()
+ html = ('<div class="math" id="equation-math:0">\n'
+ '<span class="eqno">(1.2)')
+ assert html in content
+ html = ('<p>Referencing equation <a class="reference internal" '
+ 'href="#equation-foo">(1.1)</a>.</p>')
+ assert html in content
+
+
+@pytest.mark.sphinx('html', testroot='ext-math',
+ confoverrides={'extensions': ['sphinx.ext.jsmath'],
+ 'jsmath_path': 'dummy.js',
+ 'numfig': True,
+ 'math_numfig': True})
+def test_jsmath_numfig_html(app, status, warning):
+ app.builder.build_all()
+
+ content = (app.outdir / 'math.html').text()
+ html = '<span class="eqno">(1.2)<a class="headerlink" href="#equation-math:0"'
+ assert html in content
+ html = ('<p>Referencing equation <a class="reference internal" '
+ 'href="#equation-foo">(1.1)</a>.</p>')
+ assert html in content
+
+
+@pytest.mark.sphinx('html', testroot='ext-math',
+ confoverrides={'extensions': ['sphinx.ext.imgmath'],
+ 'numfig': True,
+ 'numfig_secnum_depth': 0,
+ 'math_numfig': True})
+def test_imgmath_numfig_html(app, status, warning):
+ app.builder.build_all()
+
+ content = (app.outdir / 'page.html').text()
+ html = '<span class="eqno">(3)<a class="headerlink" href="#equation-bar"'
+ assert html in content
+ html = ('<p>Referencing equations <a class="reference internal" '
+ 'href="math.html#equation-foo">(1)</a> and '
+ '<a class="reference internal" href="#equation-bar">(3)</a>.</p>')
+ assert html in content
diff --git a/tests/test_ext_napoleon.py b/tests/test_ext_napoleon.py
index b2ca7fe7a..d8d6adc65 100644
--- a/tests/test_ext_napoleon.py
+++ b/tests/test_ext_napoleon.py
@@ -6,7 +6,7 @@
Tests for :mod:`sphinx.ext.napoleon.__init__` module.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_ext_napoleon_docstring.py b/tests/test_ext_napoleon_docstring.py
index e71d517fe..1865b004c 100644
--- a/tests/test_ext_napoleon_docstring.py
+++ b/tests/test_ext_napoleon_docstring.py
@@ -6,7 +6,7 @@
Tests for :mod:`sphinx.ext.napoleon.docstring` module.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_ext_napoleon_iterators.py b/tests/test_ext_napoleon_iterators.py
index 5258d9b79..bf144275d 100644
--- a/tests/test_ext_napoleon_iterators.py
+++ b/tests/test_ext_napoleon_iterators.py
@@ -6,7 +6,7 @@
Tests for :mod:`sphinx.ext.napoleon.iterators` module.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_ext_todo.py b/tests/test_ext_todo.py
index 4f01a07ab..0260b821d 100644
--- a/tests/test_ext_todo.py
+++ b/tests/test_ext_todo.py
@@ -5,7 +5,7 @@
Test sphinx.ext.todo extension.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -85,8 +85,9 @@ def test_todo_not_included(app, status, warning):
assert len(todos) == 2
assert set(todo[1].astext() for todo in todos) == set(['todo in foo', 'todo in bar'])
+
@pytest.mark.sphinx('latex', testroot='ext-todo', freshenv=True,
- confoverrides={'todo_include_todos': True, 'todo_emit_warnings': True})
+ confoverrides={'todo_include_todos': True})
def test_todo_valid_link(app, status, warning):
"""
Test that the inserted "original entry" links for todo items have a target
@@ -99,16 +100,17 @@ def test_todo_valid_link(app, status, warning):
content = (app.outdir / 'TodoTests.tex').text()
- # Look for the link to foo. We could equally well look for the link to bar.
+ # Look for the link to foo. Note that there are two of them because the
+ # source document uses todolist twice. We could equally well look for links
+ # to bar.
link = r'\{\\hyperref\[\\detokenize\{(.*?foo.*?)}]\{\\sphinxcrossref{' \
r'\\sphinxstyleemphasis{original entry}}}}'
m = re.findall(link, content)
- assert len(m) == 1
+ assert len(m) == 2
target = m[0]
# Look for the targets of this link.
- labels = [m for m in re.findall(r'\\label\{([^}]*)}', content)
- if m == target]
+ labels = [m for m in re.findall(r'\\label\{([^}]*)}', content) if m == target]
# If everything is correct we should have exactly one target.
assert len(labels) == 1
diff --git a/tests/test_ext_viewcode.py b/tests/test_ext_viewcode.py
index 4dceaa488..3f6612c76 100644
--- a/tests/test_ext_viewcode.py
+++ b/tests/test_ext_viewcode.py
@@ -5,7 +5,7 @@
Test sphinx.ext.viewcode extension.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_highlighting.py b/tests/test_highlighting.py
index 938181fe1..5660869bd 100644
--- a/tests/test_highlighting.py
+++ b/tests/test_highlighting.py
@@ -5,7 +5,7 @@
Test the Pygments highlighting bridge.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_intl.py b/tests/test_intl.py
index 8eff52340..cb13b00f3 100644
--- a/tests/test_intl.py
+++ b/tests/test_intl.py
@@ -6,7 +6,7 @@
Test message patching for internationalization purposes. Runs the text
builder in the test root.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
@@ -222,6 +222,7 @@ def test_text_inconsistency_warnings(app, warning):
u'.*/refs_inconsistency.txt:\\d+: WARNING: citation not found: ref3')
assert_re_search(expected_citation_warning_expr, warnings)
+
@sphinx_intl
@pytest.mark.sphinx('text')
@pytest.mark.test_params(shared_result='test_intl_basic')
@@ -520,7 +521,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 +534,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
diff --git a/tests/test_io.py b/tests/test_io.py
new file mode 100644
index 000000000..1c8fee86b
--- /dev/null
+++ b/tests/test_io.py
@@ -0,0 +1,118 @@
+# -*- coding: utf-8 -*-
+"""
+ test_sphinx_io
+ ~~~~~~~~~~~~~~
+
+ Tests io modules.
+
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import pytest
+from six import StringIO
+
+from sphinx.io import SphinxRSTFileInput
+
+
+@pytest.mark.sphinx(testroot='basic')
+def test_SphinxRSTFileInput(app):
+ app.env.temp_data['docname'] = 'index'
+
+ # normal case
+ text = ('hello Sphinx world\n'
+ 'Sphinx is a document generator')
+ source = SphinxRSTFileInput(app, app.env, source=StringIO(text),
+ source_path='dummy.rst', encoding='utf-8')
+ result = source.read()
+ assert result.data == ['hello Sphinx world',
+ 'Sphinx is a document generator']
+ assert result.info(0) == ('dummy.rst', 0)
+ assert result.info(1) == ('dummy.rst', 1)
+ assert result.info(2) == ('dummy.rst', None) # out of range
+
+ # having rst_prolog ends without CR
+ app.env.config.rst_prolog = 'this is rst_prolog\nhello reST!'
+ source = SphinxRSTFileInput(app, app.env, source=StringIO(text),
+ source_path='dummy.rst', encoding='utf-8')
+ result = source.read()
+ assert result.data == ['this is rst_prolog',
+ 'hello reST!',
+ '',
+ 'hello Sphinx world',
+ 'Sphinx is a document generator']
+ assert result.info(0) == ('<rst_prolog>', 0)
+ assert result.info(1) == ('<rst_prolog>', 1)
+ assert result.info(2) == ('<generated>', 0)
+ assert result.info(3) == ('dummy.rst', 0)
+ assert result.info(4) == ('dummy.rst', 1)
+
+ # having rst_prolog ends with CR
+ app.env.config.rst_prolog = 'this is rst_prolog\nhello reST!\n'
+ source = SphinxRSTFileInput(app, app.env, source=StringIO(text),
+ source_path='dummy.rst', encoding='utf-8')
+ result = source.read()
+ assert result.data == ['this is rst_prolog',
+ 'hello reST!',
+ '',
+ 'hello Sphinx world',
+ 'Sphinx is a document generator']
+
+ # having docinfo and rst_prolog
+ docinfo_text = (':title: test of SphinxFileInput\n'
+ ':author: Sphinx team\n'
+ '\n'
+ 'hello Sphinx world\n'
+ 'Sphinx is a document generator\n')
+ app.env.config.rst_prolog = 'this is rst_prolog\nhello reST!'
+ source = SphinxRSTFileInput(app, app.env, source=StringIO(docinfo_text),
+ source_path='dummy.rst', encoding='utf-8')
+ result = source.read()
+ assert result.data == [':title: test of SphinxFileInput',
+ ':author: Sphinx team',
+ '',
+ 'this is rst_prolog',
+ 'hello reST!',
+ '',
+ '',
+ 'hello Sphinx world',
+ 'Sphinx is a document generator']
+ assert result.info(0) == ('dummy.rst', 0)
+ assert result.info(1) == ('dummy.rst', 1)
+ assert result.info(2) == ('<generated>', 0)
+ assert result.info(3) == ('<rst_prolog>', 0)
+ assert result.info(4) == ('<rst_prolog>', 1)
+ assert result.info(5) == ('<generated>', 0)
+ assert result.info(6) == ('dummy.rst', 2)
+ assert result.info(7) == ('dummy.rst', 3)
+ assert result.info(8) == ('dummy.rst', 4)
+ assert result.info(9) == ('dummy.rst', None) # out of range
+
+ # having rst_epilog
+ app.env.config.rst_prolog = None
+ app.env.config.rst_epilog = 'this is rst_epilog\ngood-bye reST!'
+ source = SphinxRSTFileInput(app, app.env, source=StringIO(text),
+ source_path='dummy.rst', encoding='utf-8')
+ result = source.read()
+ assert result.data == ['hello Sphinx world',
+ 'Sphinx is a document generator',
+ '',
+ 'this is rst_epilog',
+ 'good-bye reST!']
+ assert result.info(0) == ('dummy.rst', 0)
+ assert result.info(1) == ('dummy.rst', 1)
+ assert result.info(2) == ('<generated>', 0)
+ assert result.info(3) == ('<rst_epilog>', 0)
+ assert result.info(4) == ('<rst_epilog>', 1)
+ assert result.info(5) == ('<rst_epilog>', None) # out of range
+
+ # expandtabs / convert whitespaces
+ app.env.config.rst_prolog = None
+ app.env.config.rst_epilog = None
+ text = ('\thello Sphinx world\n'
+ '\v\fSphinx is a document generator')
+ source = SphinxRSTFileInput(app, app.env, source=StringIO(text),
+ source_path='dummy.rst', encoding='utf-8')
+ result = source.read()
+ assert result.data == [' hello Sphinx world',
+ ' Sphinx is a document generator']
diff --git a/tests/test_markup.py b/tests/test_markup.py
index dfa4d74cf..c48096e34 100644
--- a/tests/test_markup.py
+++ b/tests/test_markup.py
@@ -5,7 +5,7 @@
Test various Sphinx-specific markup extensions.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -135,7 +135,7 @@ def get_verifier(verify, verify_re):
'``code sample``',
('<p><code class="(samp )?docutils literal"><span class="pre">'
'code</span>&#160;&#160; <span class="pre">sample</span></code></p>'),
- r'\\sphinxcode{code sample}',
+ r'\\sphinxcode{\\sphinxupquote{code sample}}',
),
(
# correct interpretation of code with whitespace
@@ -143,7 +143,7 @@ def get_verifier(verify, verify_re):
':samp:`code sample`',
('<p><code class="(samp )?docutils literal"><span class="pre">'
'code</span>&#160;&#160; <span class="pre">sample</span></code></p>'),
- r'\\sphinxcode{code sample}',
+ r'\\sphinxcode{\\sphinxupquote{code sample}}',
),
(
# interpolation of braces in samp and file roles (HTML only)
@@ -152,7 +152,7 @@ def get_verifier(verify, verify_re):
('<p><code class="samp docutils literal"><span class="pre">a</span>'
'<em><span class="pre">b</span></em>'
'<span class="pre">c</span></code></p>'),
- '\\sphinxcode{a\\sphinxstyleemphasis{b}c}',
+ '\\sphinxcode{\\sphinxupquote{a\\sphinxstyleemphasis{b}c}}',
),
(
# interpolation of arrows in menuselection
@@ -175,7 +175,7 @@ def get_verifier(verify, verify_re):
':option:`--with-option`',
('<p><code( class="xref std std-option docutils literal")?>'
'<span class="pre">--with-option</span></code></p>$'),
- r'\\sphinxcode{-{-}with-option}$',
+ r'\\sphinxcode{\\sphinxupquote{-{-}with-option}}$',
),
(
# verify smarty-pants quotes
@@ -190,14 +190,14 @@ def get_verifier(verify, verify_re):
'``"John"``',
('<p><code class="docutils literal"><span class="pre">'
'&quot;John&quot;</span></code></p>'),
- '\\sphinxcode{"John"}',
+ '\\sphinxcode{\\sphinxupquote{"John"}}',
),
(
# verify classes for inline roles
'verify',
':manpage:`mp(1)`',
'<p><em class="manpage">mp(1)</em></p>',
- '\\sphinxstyleliteralemphasis{mp(1)}',
+ '\\sphinxstyleliteralemphasis{\\sphinxupquote{mp(1)}}',
),
(
# correct escaping in normal mode
@@ -211,7 +211,8 @@ def get_verifier(verify, verify_re):
'verify',
u'::\n\n @Γ\\∞${}',
None,
- (u'\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]\n'
+ (u'\\fvset{hllines={, ,}}%\n'
+ u'\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]\n'
u'@\\(\\Gamma\\)\\PYGZbs{}\\(\\infty\\)\\PYGZdl{}\\PYGZob{}\\PYGZcb{}\n'
u'\\end{sphinxVerbatim}'),
),
diff --git a/tests/test_metadata.py b/tests/test_metadata.py
index 58f573b0a..a00d76f87 100644
--- a/tests/test_metadata.py
+++ b/tests/test_metadata.py
@@ -5,7 +5,7 @@
Test our handling of metadata in files with bibliographic metadata.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_pycode.py b/tests/test_pycode.py
index 2b5ae1514..400c47dc5 100644
--- a/tests/test_pycode.py
+++ b/tests/test_pycode.py
@@ -41,7 +41,8 @@ def test_ModuleAnalyzer_for_file():
def test_ModuleAnalyzer_for_module():
analyzer = ModuleAnalyzer.for_module('sphinx')
assert analyzer.modname == 'sphinx'
- assert analyzer.srcname == SPHINX_MODULE_PATH
+ assert analyzer.srcname in (SPHINX_MODULE_PATH,
+ os.path.abspath(SPHINX_MODULE_PATH))
assert analyzer.encoding == 'utf-8'
diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py
index 46ed3a0c1..b1b0fc535 100644
--- a/tests/test_quickstart.py
+++ b/tests/test_quickstart.py
@@ -5,7 +5,7 @@
Test the sphinx.quickstart module.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -61,27 +61,7 @@ def teardown_module():
coloron()
-def test_quickstart_inputstrip():
- d = {}
- answers = {
- 'Q1': 'Y',
- 'Q2': ' Yes ',
- 'Q3': 'N',
- 'Q4': 'N ',
- }
- qs.term_input = mock_input(answers)
- qs.do_prompt(d, 'k1', 'Q1')
- assert d['k1'] == 'Y'
- qs.do_prompt(d, 'k2', 'Q2')
- assert d['k2'] == 'Yes'
- qs.do_prompt(d, 'k3', 'Q3')
- assert d['k3'] == 'N'
- qs.do_prompt(d, 'k4', 'Q4')
- assert d['k4'] == 'N'
-
-
def test_do_prompt():
- d = {}
answers = {
'Q2': 'v2',
'Q3': 'v3',
@@ -90,39 +70,43 @@ def test_do_prompt():
'Q6': 'foo',
}
qs.term_input = mock_input(answers)
- try:
- qs.do_prompt(d, 'k1', 'Q1')
- except AssertionError:
- assert 'k1' not in d
- else:
- assert False, 'AssertionError not raised'
- qs.do_prompt(d, 'k1', 'Q1', default='v1')
- assert d['k1'] == 'v1'
- qs.do_prompt(d, 'k3', 'Q3', default='v3_default')
- assert d['k3'] == 'v3'
- qs.do_prompt(d, 'k2', 'Q2')
- assert d['k2'] == 'v2'
- qs.do_prompt(d, 'k4', 'Q4', validator=qs.boolean)
- assert d['k4'] is True
- qs.do_prompt(d, 'k5', 'Q5', validator=qs.boolean)
- assert d['k5'] is False
+
+ assert qs.do_prompt('Q1', default='v1') == 'v1'
+ assert qs.do_prompt('Q3', default='v3_default') == 'v3'
+ assert qs.do_prompt('Q2') == 'v2'
+ assert qs.do_prompt('Q4', validator=qs.boolean) is True
+ assert qs.do_prompt('Q5', validator=qs.boolean) is False
with pytest.raises(AssertionError):
- qs.do_prompt(d, 'k6', 'Q6', validator=qs.boolean)
+ qs.do_prompt('Q6', validator=qs.boolean)
+
+
+def test_do_prompt_inputstrip():
+ answers = {
+ 'Q1': 'Y',
+ 'Q2': ' Yes ',
+ 'Q3': 'N',
+ 'Q4': 'N ',
+ }
+ qs.term_input = mock_input(answers)
+
+ assert qs.do_prompt('Q1') == 'Y'
+ assert qs.do_prompt('Q2') == 'Yes'
+ assert qs.do_prompt('Q3') == 'N'
+ assert qs.do_prompt('Q4') == 'N'
def test_do_prompt_with_nonascii():
- d = {}
answers = {
'Q1': u'\u30c9\u30a4\u30c4',
}
qs.term_input = mock_input(answers)
try:
- qs.do_prompt(d, 'k1', 'Q1', default=u'\u65e5\u672c')
+ result = qs.do_prompt('Q1', default=u'\u65e5\u672c')
except UnicodeEncodeError:
raise pytest.skip.Exception(
'non-ASCII console input not supported on this encoding: %s',
qs.TERM_ENCODING)
- assert d['k1'] == u'\u30c9\u30a4\u30c4'
+ assert result == u'\u30c9\u30a4\u30c4'
def test_quickstart_defaults(tempdir):
@@ -149,7 +133,6 @@ def test_quickstart_defaults(tempdir):
assert ns['copyright'] == '%s, Georg Brandl' % time.strftime('%Y')
assert ns['version'] == '0.1'
assert ns['release'] == '0.1'
- assert ns['todo_include_todos'] is False
assert ns['html_static_path'] == ['_static']
assert ns['latex_documents'] == [
('index', 'SphinxTest.tex', 'Sphinx Test Documentation',
diff --git a/tests/test_search.py b/tests/test_search.py
index f1825dfa4..fc5fb7e04 100644
--- a/tests/test_search.py
+++ b/tests/test_search.py
@@ -5,7 +5,7 @@
Test the search index builder.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_setup_command.py b/tests/test_setup_command.py
index 562b0a715..e1f976b8a 100644
--- a/tests/test_setup_command.py
+++ b/tests/test_setup_command.py
@@ -5,7 +5,7 @@
Test setup_command for distutils.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_smartquotes.py b/tests/test_smartquotes.py
new file mode 100644
index 000000000..f9ea9d726
--- /dev/null
+++ b/tests/test_smartquotes.py
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+"""
+ test_smartquotes
+ ~~~~~~~~~~~~~~~~
+
+ Test smart quotes.
+
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import pytest
+from sphinx.util import docutils
+
+
+@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True)
+def test_basic(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'index.html').text()
+ assert u'<p>– “Sphinx” is a tool that makes it easy …</p>' in content
+
+
+@pytest.mark.sphinx(buildername='text', testroot='smartquotes', freshenv=True)
+def test_text_builder(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'index.txt').text()
+ assert u'-- "Sphinx" is a tool that makes it easy ...' in content
+
+
+@pytest.mark.sphinx(buildername='man', testroot='smartquotes', freshenv=True)
+def test_man_builder(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'python.1').text()
+ assert u'\\-\\- "Sphinx" is a tool that makes it easy ...' in content
+
+
+@pytest.mark.sphinx(buildername='latex', testroot='smartquotes', freshenv=True)
+def test_latex_builder(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'test.tex').text()
+ assert u'\\textendash{} “Sphinx” is a tool that makes it easy …' in content
+
+
+@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True,
+ confoverrides={'language': 'ja'})
+def test_ja_html_builder(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'index.html').text()
+ assert u'<p>-- &quot;Sphinx&quot; is a tool that makes it easy ...</p>' in content
+
+
+@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True,
+ confoverrides={'smartquotes': False})
+def test_smartquotes_disabled(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'index.html').text()
+ assert u'<p>-- &quot;Sphinx&quot; is a tool that makes it easy ...</p>' in content
+
+
+@pytest.mark.skipif(docutils.__version_info__ < (0, 14),
+ reason='docutils-0.14 or above is required')
+@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True,
+ confoverrides={'smartquotes_action': 'q'})
+def test_smartquotes_action(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'index.html').text()
+ assert u'<p>-- “Sphinx” is a tool that makes it easy ...</p>' in content
+
+
+@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True,
+ confoverrides={'language': 'ja', 'smartquotes_excludes': {}})
+def test_smartquotes_excludes_language(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'index.html').text()
+ assert u'<p>– 「Sphinx」 is a tool that makes it easy …</p>' in content
+
+
+@pytest.mark.sphinx(buildername='man', testroot='smartquotes', freshenv=True,
+ confoverrides={'smartquotes_excludes': {}})
+def test_smartquotes_excludes_builders(app, status, warning):
+ app.build()
+
+ content = (app.outdir / 'python.1').text()
+ assert u'– “Sphinx” is a tool that makes it easy …' in content
diff --git a/tests/test_templating.py b/tests/test_templating.py
index b0070f06a..550b3bc7d 100644
--- a/tests/test_templating.py
+++ b/tests/test_templating.py
@@ -5,15 +5,19 @@
Test templating.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import pytest
+from sphinx.ext.autosummary.generate import setup_documenters
@pytest.mark.sphinx('html', testroot='templating')
-def test_layout_overloading(app, status, warning):
+def test_layout_overloading(make_app, app_params):
+ args, kwargs = app_params
+ app = make_app(*args, **kwargs)
+ setup_documenters(app)
app.builder.build_update()
result = (app.outdir / 'contents.html').text(encoding='utf-8')
@@ -22,7 +26,10 @@ def test_layout_overloading(app, status, warning):
@pytest.mark.sphinx('html', testroot='templating')
-def test_autosummary_class_template_overloading(app, status, warning):
+def test_autosummary_class_template_overloading(make_app, app_params):
+ args, kwargs = app_params
+ app = make_app(*args, **kwargs)
+ setup_documenters(app)
app.builder.build_update()
result = (app.outdir / 'generated' / 'sphinx.application.TemplateBridge.html').text(
diff --git a/tests/test_theming.py b/tests/test_theming.py
index 4a6b8c956..dfe583918 100644
--- a/tests/test_theming.py
+++ b/tests/test_theming.py
@@ -5,7 +5,7 @@
Test the Theme class.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -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,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', 'testtheme', 'ziptheme', 'epub', 'nature',
- 'pyramid', 'bizstyle', 'classic', 'nonav'])
- assert app.html_themes['testtheme'] == app.srcdir / 'testtheme'
+ '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
@@ -97,6 +99,21 @@ def test_nested_zipped_theme(app, status, warning):
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() == (
+ '<!-- testing static templates -->\n'
+ '<html><project>Python</project></html>'
+ )
+
+ result = (app.outdir / 'index.html').text()
+ assert '<meta name="testopt" content="optdefault" />' in result
+
+
@pytest.mark.sphinx(testroot='theming')
def test_theme_sidebars(app, status, warning):
app.build()
diff --git a/tests/test_toctree.py b/tests/test_toctree.py
index 18910197f..42ec0ce89 100644
--- a/tests/test_toctree.py
+++ b/tests/test_toctree.py
@@ -5,7 +5,7 @@
Test the HTML builder and check output against XPath.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import pytest
diff --git a/tests/test_util.py b/tests/test_util.py
index 84ce44007..189e221b2 100644
--- a/tests/test_util.py
+++ b/tests/test_util.py
@@ -5,7 +5,7 @@
Tests util functions.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -14,8 +14,7 @@ from mock import patch
from sphinx.util import logging
from sphinx.util import (
- display_chunk, encode_uri, parselinenos, split_docinfo, status_iterator,
- xmlname_checker
+ display_chunk, encode_uri, parselinenos, status_iterator, xmlname_checker
)
from sphinx.testing.util import strip_escseq
@@ -36,28 +35,6 @@ def test_encode_uri():
assert expected, encode_uri(uri)
-def test_splitdocinfo():
- source = "Hello world.\n"
- docinfo, content = split_docinfo(source)
- assert docinfo == ''
- assert content == 'Hello world.\n'
-
- source = ":orphan:\n\nHello world.\n"
- docinfo, content = split_docinfo(source)
- assert docinfo == ':orphan:\n'
- assert content == '\nHello world.\n'
-
- source = ":author: Georg Brandl\n:title: Manual of Sphinx\n\nHello world.\n"
- docinfo, content = split_docinfo(source)
- assert docinfo == ':author: Georg Brandl\n:title: Manual of Sphinx\n'
- assert content == '\nHello world.\n'
-
- source = ":multiline: one\n\ttwo\n\tthree\n\nHello world.\n"
- docinfo, content = split_docinfo(source)
- assert docinfo == ":multiline: one\n\ttwo\n\tthree\n"
- assert content == '\nHello world.\n'
-
-
def test_display_chunk():
assert display_chunk('hello') == 'hello'
assert display_chunk(['hello']) == 'hello'
@@ -118,7 +95,6 @@ def test_parselinenos():
parselinenos('3-1', 10)
-
def test_xmlname_check():
checker = xmlname_checker()
assert checker.match('id-pub')
diff --git a/tests/test_util_fileutil.py b/tests/test_util_fileutil.py
index 849ccce22..69f51f52c 100644
--- a/tests/test_util_fileutil.py
+++ b/tests/test_util_fileutil.py
@@ -5,7 +5,7 @@
Tests sphinx.util.fileutil functions.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from sphinx.util.fileutil import copy_asset, copy_asset_file
diff --git a/tests/test_util_i18n.py b/tests/test_util_i18n.py
index 53e0e4cf1..bec4e91e9 100644
--- a/tests/test_util_i18n.py
+++ b/tests/test_util_i18n.py
@@ -5,7 +5,7 @@
Test i18n util.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
diff --git a/tests/test_util_images.py b/tests/test_util_images.py
index 6f67dcc82..624690831 100644
--- a/tests/test_util_images.py
+++ b/tests/test_util_images.py
@@ -5,7 +5,7 @@
Test images util.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
@@ -44,22 +44,22 @@ def test_guess_mimetype(testroot):
assert guess_mimetype('IMG.PNG') == 'image/png'
# guess by content
- assert guess_mimetype(content=(testroot/GIF_FILENAME).bytes()) == 'image/gif'
- assert guess_mimetype(content=(testroot/PNG_FILENAME).bytes()) == 'image/png'
- assert guess_mimetype(content=(testroot/PDF_FILENAME).bytes()) is None
- assert guess_mimetype(content=(testroot/TXT_FILENAME).bytes()) is None
- assert guess_mimetype(content=(testroot/TXT_FILENAME).bytes(),
+ assert guess_mimetype(content=(testroot / GIF_FILENAME).bytes()) == 'image/gif'
+ assert guess_mimetype(content=(testroot / PNG_FILENAME).bytes()) == 'image/png'
+ assert guess_mimetype(content=(testroot / PDF_FILENAME).bytes()) is None
+ assert guess_mimetype(content=(testroot / TXT_FILENAME).bytes()) is None
+ assert guess_mimetype(content=(testroot / TXT_FILENAME).bytes(),
default='text/plain') == 'text/plain'
# the priority of params: filename > content > default
assert guess_mimetype('img.png',
- content=(testroot/GIF_FILENAME).bytes(),
+ content=(testroot / GIF_FILENAME).bytes(),
default='text/plain') == 'image/png'
assert guess_mimetype('no_extension',
- content=(testroot/GIF_FILENAME).bytes(),
+ content=(testroot / GIF_FILENAME).bytes(),
default='text/plain') == 'image/gif'
assert guess_mimetype('no_extension',
- content=(testroot/TXT_FILENAME).bytes(),
+ content=(testroot / TXT_FILENAME).bytes(),
default='text/plain') == 'text/plain'
diff --git a/tests/test_util_inspect.py b/tests/test_util_inspect.py
index 63e04ee76..b5d50ed71 100644
--- a/tests/test_util_inspect.py
+++ b/tests/test_util_inspect.py
@@ -5,7 +5,7 @@
Tests util.inspect functions.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys
@@ -113,7 +113,6 @@ def test_getargspec_bound_methods():
assert expected_bound == inspect.getargspec(wrapped_bound_method)
-
def test_Signature():
# literals
with pytest.raises(TypeError):
@@ -212,15 +211,15 @@ def test_Signature_annotations():
# Generic types with concrete parameters
sig = inspect.Signature(f1).format_args()
- assert sig == '(x: typing.List[int]) -> typing.List[int]'
+ assert sig == '(x: List[int]) -> List[int]'
# TypeVars and generic types with TypeVars
sig = inspect.Signature(f2).format_args()
- assert sig == '(x: typing.List[T], y: typing.List[T_co], z: T) -> typing.List[T_contra]'
+ assert sig == '(x: List[T], y: List[T_co], z: T) -> List[T_contra]'
# Union types
sig = inspect.Signature(f3).format_args()
- assert sig == '(x: typing.Union[str, numbers.Integral]) -> None'
+ assert sig == '(x: Union[str, numbers.Integral]) -> None'
# Quoted annotations
sig = inspect.Signature(f4).format_args()
@@ -240,14 +239,14 @@ def test_Signature_annotations():
# Callable types
sig = inspect.Signature(f8).format_args()
- assert sig == '(x: typing.Callable[[int, str], int]) -> None'
+ assert sig == '(x: Callable[[int, str], int]) -> None'
sig = inspect.Signature(f9).format_args()
- assert sig == '(x: typing.Callable) -> None'
+ assert sig == '(x: Callable) -> None'
# Tuple types
sig = inspect.Signature(f10).format_args()
- assert sig == '(x: typing.Tuple[int, str], y: typing.Tuple[int, ...]) -> None'
+ assert sig == '(x: Tuple[int, str], y: Tuple[int, ...]) -> None'
# Instance annotations
sig = inspect.Signature(f11).format_args()
diff --git a/tests/test_util_logging.py b/tests/test_util_logging.py
index 7ae086872..48eed82b0 100644
--- a/tests/test_util_logging.py
+++ b/tests/test_util_logging.py
@@ -5,7 +5,7 @@
Test logging util.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
@@ -183,7 +183,7 @@ def test_warning_location(app, status, warning):
assert 'index.txt:10: WARNING: message2' in warning.getvalue()
logger.warning('message3', location=None)
- assert colorize('darkred', 'WARNING: message3') in warning.getvalue()
+ assert colorize('red', 'WARNING: message3') in warning.getvalue()
node = nodes.Node()
node.source, node.line = ('index.txt', 10)
@@ -200,7 +200,7 @@ def test_warning_location(app, status, warning):
node.source, node.line = (None, None)
logger.warning('message7', location=node)
- assert colorize('darkred', 'WARNING: message7') in warning.getvalue()
+ assert colorize('red', 'WARNING: message7') in warning.getvalue()
def test_pending_warnings(app, status, warning):
@@ -236,7 +236,7 @@ def test_colored_logs(app, status, warning):
assert colorize('darkgray', 'message1') in status.getvalue()
assert 'message2\n' in status.getvalue() # not colored
assert 'message3\n' in status.getvalue() # not colored
- assert colorize('darkred', 'WARNING: message4') in warning.getvalue()
+ assert colorize('red', 'WARNING: message4') in warning.getvalue()
assert 'WARNING: message5\n' in warning.getvalue() # not colored
assert colorize('darkred', 'WARNING: message6') in warning.getvalue()
diff --git a/tests/test_util_matching.py b/tests/test_util_matching.py
index 3b84f4735..fc38470d3 100644
--- a/tests/test_util_matching.py
+++ b/tests/test_util_matching.py
@@ -5,7 +5,7 @@
Tests sphinx.util.matching functions.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from sphinx.util.matching import compile_matchers, Matcher
diff --git a/tests/test_util_nodes.py b/tests/test_util_nodes.py
index c392c2bc7..c58ecc205 100644
--- a/tests/test_util_nodes.py
+++ b/tests/test_util_nodes.py
@@ -5,7 +5,7 @@
Tests uti.nodes functions.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from textwrap import dedent
diff --git a/tests/test_util_rst.py b/tests/test_util_rst.py
index 5fce6e3eb..406ea710e 100644
--- a/tests/test_util_rst.py
+++ b/tests/test_util_rst.py
@@ -5,7 +5,7 @@
Tests sphinx.util.rst functions.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from sphinx.util.rst import escape
diff --git a/tests/test_versioning.py b/tests/test_versioning.py
index b73c00fa6..e17d250e5 100644
--- a/tests/test_versioning.py
+++ b/tests/test_versioning.py
@@ -5,7 +5,7 @@
Test the versioning implementation.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -28,7 +28,7 @@ def setup_module(rootdir, sphinx_test_tempdir):
global app, original, original_uids
srcdir = sphinx_test_tempdir / 'test-versioning'
if not srcdir.exists():
- (rootdir/'test-versioning').copytree(srcdir)
+ (rootdir / 'test-versioning').copytree(srcdir)
app = SphinxTestApp(srcdir=srcdir)
app.builder.env.app = app
app.connect('doctree-resolved', on_doctree_resolved)
diff --git a/tests/test_websupport.py b/tests/test_websupport.py
index 51cb2b287..10942798c 100644
--- a/tests/test_websupport.py
+++ b/tests/test_websupport.py
@@ -5,7 +5,7 @@
Test the Web Support Package
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_writer_latex.py b/tests/test_writer_latex.py
index b026f8d17..5c73469ec 100644
--- a/tests/test_writer_latex.py
+++ b/tests/test_writer_latex.py
@@ -5,7 +5,7 @@
Test the LaTeX writer
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
diff --git a/tox.ini b/tox.ini
index 00b3c99e2..810b76f0c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,69 +1,65 @@
[tox]
-minversion=2.0
-envlist=flake8,mypy,py{27,34,35,36},pypy,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
+usedevelop = True
+passenv =
+ https_proxy http_proxy no_proxy PERL PERL5LIB
+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.
+
# 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
#
# 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 =
+ PYTHONWARNINGS = all,ignore::ImportWarning:pkgutil,ignore::ImportWarning:importlib._bootstrap,ignore::ImportWarning:importlib._bootstrap_external,ignore::ImportWarning:pytest_cov.plugin,ignore::DeprecationWarning:site,ignore::DeprecationWarning:_pytest.assertion.rewrite,ignore::DeprecationWarning:_pytest.fixtures
SPHINX_TEST_TEMPDIR = {envdir}/testbuild
commands=
- {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
-
-[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}
+ pytest -Wall --durations 25 {posargs}
[testenv:flake8]
-deps=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: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:coverage]
+description =
+ Run code coverage checks.
+setenv =
+ PYTEST_ADDOPTS = --cov sphinx --cov-config {toxinidir}/setup.cfg
+commands =
+ {[testenv]commands}
+ coverage report
[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}
diff --git a/utils/checks.py b/utils/checks.py
index 03104d78a..90d89439e 100644
--- a/utils/checks.py
+++ b/utils/checks.py
@@ -5,7 +5,7 @@
Custom, Sphinx-only flake8 plugins.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/utils/jssplitter_generator.py b/utils/jssplitter_generator.py
index 05b8628b3..b7273a5c1 100644
--- a/utils/jssplitter_generator.py
+++ b/utils/jssplitter_generator.py
@@ -122,7 +122,7 @@ python_src = '''# -*- coding: utf-8 -*-
DO NOT EDIT. This is generated by utils/jssplitter_generator.py
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""