summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/config.yml5
-rw-r--r--CHANGES70
-rw-r--r--EXAMPLES1
-rw-r--r--Makefile4
-rw-r--r--doc/_themes/sphinx13/static/sphinx13.css2
-rw-r--r--doc/conf.py2
-rw-r--r--doc/develop.rst10
-rw-r--r--doc/extdev/appapi.rst15
-rw-r--r--doc/extdev/deprecated.rst20
-rw-r--r--doc/internals/contributing.rst4
-rw-r--r--doc/usage/configuration.rst12
-rw-r--r--doc/usage/extensions/autodoc.rst40
-rw-r--r--doc/usage/extensions/coverage.rst2
-rw-r--r--doc/usage/installation.rst2
-rw-r--r--doc/usage/quickstart.rst2
-rw-r--r--doc/usage/restructuredtext/directives.rst4
-rw-r--r--doc/usage/restructuredtext/domains.rst12
-rw-r--r--package-lock.json22
-rw-r--r--sphinx/__init__.py6
-rw-r--r--sphinx/application.py74
-rw-r--r--sphinx/builders/gettext.py2
-rw-r--r--sphinx/builders/html/__init__.py25
-rw-r--r--sphinx/builders/latex/__init__.py34
-rw-r--r--sphinx/builders/linkcheck.py8
-rw-r--r--sphinx/builders/manpage.py10
-rw-r--r--sphinx/directives/code.py2
-rw-r--r--sphinx/domains/c.py70
-rw-r--r--sphinx/domains/std.py3
-rw-r--r--sphinx/environment/collectors/toctree.py4
-rw-r--r--sphinx/ext/autodoc/__init__.py106
-rw-r--r--sphinx/ext/inheritance_diagram.py5
-rw-r--r--sphinx/ext/napoleon/docstring.py6
-rw-r--r--sphinx/locale/__init__.py2
-rw-r--r--sphinx/locale/ar/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/bg/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/bn/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/ca/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/cak/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/cs/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/cy/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/da/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/de/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/el/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/eo/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/es/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/et/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/eu/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/fa/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/fi/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/fr/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/he/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/hi/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/hr/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/hu/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/id/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/it/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/ja/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/ko/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/lt/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/lv/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/mk/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/ne/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/nl/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/pl/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/pt/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/ro/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/ru/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/si/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/sk/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/sl/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/sq/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/sr/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/sr@latin/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/sr_RS/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/sv/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/ta/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/te/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/tr/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/ur/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/vi/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/pycode/ast.py29
-rw-r--r--sphinx/registry.py13
-rw-r--r--sphinx/search/non-minified-js/danish-stemmer.js4
-rw-r--r--sphinx/search/non-minified-js/dutch-stemmer.js4
-rw-r--r--sphinx/search/non-minified-js/finnish-stemmer.js4
-rw-r--r--sphinx/search/non-minified-js/french-stemmer.js4
-rw-r--r--sphinx/search/non-minified-js/german-stemmer.js4
-rw-r--r--sphinx/search/non-minified-js/hungarian-stemmer.js4
-rw-r--r--sphinx/search/non-minified-js/italian-stemmer.js4
-rw-r--r--sphinx/search/non-minified-js/norwegian-stemmer.js4
-rw-r--r--sphinx/search/non-minified-js/porter-stemmer.js4
-rw-r--r--sphinx/search/non-minified-js/portuguese-stemmer.js4
-rw-r--r--sphinx/search/non-minified-js/romanian-stemmer.js4
-rw-r--r--sphinx/search/non-minified-js/russian-stemmer.js4
-rw-r--r--sphinx/search/non-minified-js/spanish-stemmer.js4
-rw-r--r--sphinx/search/non-minified-js/swedish-stemmer.js4
-rw-r--r--sphinx/search/non-minified-js/turkish-stemmer.js4
-rw-r--r--sphinx/testing/util.py6
-rw-r--r--sphinx/texinputs/sphinx.sty4
-rw-r--r--sphinx/texinputs/sphinxcyrillic.sty2
-rw-r--r--sphinx/texinputs/sphinxhowto.cls2
-rw-r--r--sphinx/themes/nature/static/nature.css_t66
-rw-r--r--sphinx/themes/pyramid/static/pyramid.css_t64
-rw-r--r--sphinx/themes/sphinxdoc/static/sphinxdoc.css_t2
-rw-r--r--sphinx/transforms/post_transforms/images.py15
-rw-r--r--sphinx/util/fileutil.py18
-rw-r--r--sphinx/util/i18n.py6
-rw-r--r--sphinx/util/inspect.py36
-rw-r--r--sphinx/util/inventory.py7
-rw-r--r--sphinx/util/template.py2
-rw-r--r--sphinx/util/typing.py11
-rw-r--r--sphinx/writers/html.py2
-rw-r--r--sphinx/writers/html5.py2
-rw-r--r--sphinx/writers/latex.py6
-rw-r--r--sphinx/writers/texinfo.py8
-rw-r--r--tests/roots/test-circular/conf.py2
-rw-r--r--tests/roots/test-ext-autodoc/target/annotations.py25
-rw-r--r--tests/roots/test-ext-autodoc/target/cached_property.py7
-rw-r--r--tests/roots/test-ext-autodoc/target/generic_class.py12
-rw-r--r--tests/roots/test-ext-autodoc/target/overload2.py5
-rw-r--r--tests/roots/test-intl/xx/LC_MESSAGES/toctree.po2
-rw-r--r--tests/roots/test-linkcheck-localserver/conf.py2
-rw-r--r--tests/roots/test-linkcheck-localserver/index.rst1
-rw-r--r--tests/roots/test-numbered-circular/conf.py2
-rw-r--r--tests/roots/test-theming/MANIFEST.in4
-rw-r--r--tests/roots/test-theming/conf.py6
-rw-r--r--tests/roots/test-theming/index.rst10
-rw-r--r--tests/roots/test-theming/setup.py22
-rw-r--r--tests/roots/test-theming/test_theme/__init__.py10
-rw-r--r--tests/test_build_gettext.py18
-rw-r--r--tests/test_build_html.py5
-rw-r--r--tests/test_build_linkcheck.py36
-rw-r--r--tests/test_build_manpage.py7
-rw-r--r--tests/test_domain_py.py13
-rw-r--r--tests/test_ext_autodoc.py51
-rw-r--r--tests/test_ext_autodoc_configs.py48
-rw-r--r--tests/test_ext_autodoc_events.py3
-rw-r--r--tests/test_intl.py29
-rw-r--r--tests/test_pycode.py2
-rw-r--r--tests/test_pycode_ast.py16
-rw-r--r--tests/test_util_typing.py8
-rw-r--r--tox.ini11
-rw-r--r--utils/pylintrc301
150 files changed, 1031 insertions, 682 deletions
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index c9009b90e..226532b79 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -2,5 +2,8 @@
blank_issues_enabled: false # default: true
contact_links:
- name: Question
+ url: https://stackoverflow.com/questions/tagged/python-sphinx
+ about: For Q&A purpose, please use Stackoverflow with the tag python-sphinx
+- name: Discussion
url: https://groups.google.com/forum/#!forum/sphinx-users
- about: For Q&A purpose, please use sphinx-users mailing list.
+ about: For general discussion, please use sphinx-users mailing list.
diff --git a/CHANGES b/CHANGES
index a8426dff8..0405e1d93 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,69 @@
+Release 3.3.0 (in development)
+==============================
+
+Dependencies
+------------
+
+Incompatible changes
+--------------------
+
+Deprecated
+----------
+
+* ``sphinx.builders.latex.LaTeXBuilder.usepackages``
+* ``sphinx.builders.latex.LaTeXBuilder.usepackages_afger_hyperref``
+* ``sphinx.ext.autodoc.SingledispatchFunctionDocumenter``
+* ``sphinx.ext.autodoc.SingledispatchMethodDocumenter``
+
+Features added
+--------------
+
+* #8100: html: Show a better error message for failures on copying
+ html_static_files
+* #8141: C: added a ``maxdepth`` option to :rst:dir:`c:alias` to insert
+ nested declarations.
+* #8081: LaTeX: Allow to add LaTeX package via ``app.add_latex_package()`` until
+ just before writing .tex file
+* #7996: manpage: Add :confval:`man_make_section_directory` to make a section
+ directory on build man page
+
+Bugs fixed
+----------
+
+* #8085: i18n: Add support for having single text domain
+* #6640: i18n: Failed to override system message translation
+* #8143: autodoc: AttributeError is raised when False value is passed to
+ autodoc_default_options
+* #8103: autodoc: functools.cached_property is not considered as a property
+* #8190: autodoc: parsing error is raised if some extension replaces docstring
+ by string not ending with blank lines
+* #8142: autodoc: Wrong constructor signature for the class derived from
+ typing.Generic
+* #8157: autodoc: TypeError is raised when annotation has invalid __args__
+* #7964: autodoc: Tuple in default value is wrongly rendered
+* #8200: autodoc: type aliases break type formatting of autoattribute
+* #7786: autodoc: can't detect overloaded methods defined in other file
+* #8192: napoleon: description is disappeared when it contains inline literals
+* #8142: napoleon: Potential of regex denial of service in google style docs
+* #8169: LaTeX: pxjahyper loaded even when latex_engine is not platex
+* #8175: intersphinx: Potential of regex denial of service by broken inventory
+* #8277: sphinx-build: missing and redundant spacing (and etc) for console
+ output on building
+* #7973: imgconverter: Check availability of imagemagick many times
+* #8255: py domain: number in default argument value is changed from hexadecimal
+ to decimal
+* #8093: The highlight warning has wrong location in some builders (LaTeX,
+ singlehtml and so on)
+* #8239: Failed to refer a token in productionlist if it is indented
+* #8268: linkcheck: Report HTTP errors when ``linkcheck_anchors`` is ``True``
+* #8245: linkcheck: take source directory into account for local files
+* #6914: figure numbers are unexpectedly assigned to uncaptioned items
+
+Testing
+--------
+
+* #8257: Support parallel build in sphinx.testing
+
Release 3.2.2 (in development)
==============================
@@ -127,7 +193,7 @@ Bugs fixed
contains a hyperlink target
* #7469: autosummary: "Module attributes" header is not translatable
* #7940: apidoc: An extra newline is generated at the end of the rst file if a
- module has submodules
+ module has submodules
* #4258: napoleon: decorated special methods are not shown
* #7799: napoleon: parameters are not escaped for combined params in numpydoc
* #7780: napoleon: multiple paramaters declaration in numpydoc was wrongly
@@ -294,7 +360,7 @@ Features added
* #7543: html theme: Add top and bottom margins to tables
* #7695: html theme: Add viewport meta tag for basic theme
* #7721: html theme: classic: default codetextcolor/codebgcolor doesn't override
- Pygments
+ Pygments
* C and C++: allow semicolon in the end of declarations.
* C++, parse parameterized noexcept specifiers.
* #7294: C++, parse expressions with user-defined literals.
diff --git a/EXAMPLES b/EXAMPLES
index b87d8e73e..19f23172f 100644
--- a/EXAMPLES
+++ b/EXAMPLES
@@ -330,6 +330,7 @@ Documentation using a custom theme or integrated in a website
* `Lasso <http://lassoguide.com/>`__
* `Mako <http://docs.makotemplates.org/>`__
* `MirrorBrain <http://mirrorbrain.org/docs/>`__
+* `Mitiq <https://mitiq.readthedocs.io/>`__
* `MongoDB <https://docs.mongodb.com/>`__
* `Music21 <https://web.mit.edu/music21/doc/>`__
* `MyHDL <http://docs.myhdl.org/>`__
diff --git a/Makefile b/Makefile
index d37d8546e..9cff012d4 100644
--- a/Makefile
+++ b/Makefile
@@ -64,10 +64,6 @@ type-check:
doclinter:
python utils/doclinter.py CHANGES *.rst doc/
-.PHONY: pylint
-pylint:
- @pylint --rcfile utils/pylintrc sphinx
-
.PHONY: test
test:
@$(PYTHON) -m pytest -v $(TEST)
diff --git a/doc/_themes/sphinx13/static/sphinx13.css b/doc/_themes/sphinx13/static/sphinx13.css
index 7c1d46e83..c8fb2e5c9 100644
--- a/doc/_themes/sphinx13/static/sphinx13.css
+++ b/doc/_themes/sphinx13/static/sphinx13.css
@@ -239,7 +239,7 @@ div.footer a {
/* -- body styles ----------------------------------------------------------- */
-p {
+p {
margin: 0.8em 0 0.5em 0;
}
diff --git a/doc/conf.py b/doc/conf.py
index f62e02a34..74e5a8b80 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -110,8 +110,6 @@ texinfo_documents = [
1),
]
-# We're not using intersphinx right now, but if we did, this would be part of
-# the mapping:
intersphinx_mapping = {'python': ('https://docs.python.org/3/', None)}
# Sphinx document translation with sphinx gettext feature uses these settings:
diff --git a/doc/develop.rst b/doc/develop.rst
index 080251ba6..1287a6539 100644
--- a/doc/develop.rst
+++ b/doc/develop.rst
@@ -140,14 +140,14 @@ started with writing your own extensions.
.. _slideshare: https://www.slideshare.net/
.. _TikZ/PGF LaTeX package: https://sourceforge.net/projects/pgf/
.. _MATLAB: https://www.mathworks.com/products/matlab.html
-.. _swf: https://bitbucket.org/klorenz/sphinxcontrib-swf
-.. _findanything: https://bitbucket.org/klorenz/sphinxcontrib-findanything
-.. _cmakedomain: https://bitbucket.org/klorenz/sphinxcontrib-cmakedomain
+.. _swf: https://github.com/sphinx-contrib/swf
+.. _findanything: https://github.com/sphinx-contrib/findanything
+.. _cmakedomain: https://github.com/sphinx-contrib/cmakedomain
.. _GNU Make: https://www.gnu.org/software/make/
-.. _makedomain: https://bitbucket.org/klorenz/sphinxcontrib-makedomain
+.. _makedomain: https://github.com/sphinx-contrib/makedomain
.. _inlinesyntaxhighlight: https://sphinxcontrib-inlinesyntaxhighlight.readthedocs.io/
.. _CMake: https://cmake.org
-.. _domaintools: https://bitbucket.org/klorenz/sphinxcontrib-domaintools
+.. _domaintools: https://github.com/sphinx-contrib/domaintools
.. _restbuilder: https://pypi.org/project/sphinxcontrib-restbuilder/
.. _Lasso: http://www.lassosoft.com/
.. _beamer: https://pypi.org/project/sphinxcontrib-beamer/
diff --git a/doc/extdev/appapi.rst b/doc/extdev/appapi.rst
index 036b57ec1..df3eb3d67 100644
--- a/doc/extdev/appapi.rst
+++ b/doc/extdev/appapi.rst
@@ -177,17 +177,18 @@ type for that event::
9. (if running in parallel mode, for each process) event.env-merged-info(app, env, docnames, other)
10. event.env-updated(app, env)
11. event.env-get-updated(app, env)
- 11. event.env-check-consistency(app, env)
+ 12. event.env-check-consistency(app, env)
+ # The updated-docs list can be builder dependent, but generally includes all new/changed documents,
+ # plus any output from `env-get-updated`, and then all "parent" documents in the ToC tree
# For builders that output a single page, they are first joined into a single doctree before post-transforms/doctree-resolved
- for docname in docnames:
- 12. apply post-transforms (by priority): docutils.document -> docutils.document
- 13. event.doctree-resolved(app, doctree, docname)
+ for docname in updated-docs:
+ 13. apply post-transforms (by priority): docutils.document -> docutils.document
+ 14. event.doctree-resolved(app, doctree, docname)
- (for any reference node that fails to resolve) event.missing-reference(env, node, contnode)
- 14. Generate output files
-
- 15. event.build-finished(app, exception)
+ 15. Generate output files
+ 16. event.build-finished(app, exception)
Here is a more detailed list of these events.
diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst
index 829bfc32b..2bb8aebfd 100644
--- a/doc/extdev/deprecated.rst
+++ b/doc/extdev/deprecated.rst
@@ -26,6 +26,26 @@ The following is a list of deprecated interfaces.
- (will be) Removed
- Alternatives
+ * - ``sphinx.builders.latex.LaTeXBuilder.usepackages``
+ - 3.3
+ - 5.0
+ - N/A
+
+ * - ``sphinx.builders.latex.LaTeXBuilder.usepackages_afger_hyperref``
+ - 3.3
+ - 5.0
+ - N/A
+
+ * - ``sphinx.ext.autodoc.SingledispatchFunctionDocumenter``
+ - 3.3
+ - 5.0
+ - ``sphinx.ext.autodoc.FunctionDocumenter``
+
+ * - ``sphinx.ext.autodoc.SingledispatchMethodDocumenter``
+ - 3.3
+ - 5.0
+ - ``sphinx.ext.autodoc.MethodDocumenter``
+
* - ``sphinx.ext.autodoc.members_set_option()``
- 3.2
- 5.0
diff --git a/doc/internals/contributing.rst b/doc/internals/contributing.rst
index 0e464478a..1f4a31013 100644
--- a/doc/internals/contributing.rst
+++ b/doc/internals/contributing.rst
@@ -12,6 +12,9 @@ Getting help
The Sphinx community maintains a number of mailing lists and IRC channels.
+Stack Overflow with tag `python-sphinx`_
+ Questions and answers about use and development.
+
sphinx-users <sphinx-users@googlegroups.com>
Mailing list for user support.
@@ -21,6 +24,7 @@ sphinx-dev <sphinx-dev@googlegroups.com>
#sphinx-doc on irc.freenode.net
IRC channel for development questions and user support.
+.. _python-sphinx: https://stackoverflow.com/questions/tagged/python-sphinx
Bug Reports and Feature Requests
--------------------------------
diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst
index add78326b..270fcbf33 100644
--- a/doc/usage/configuration.rst
+++ b/doc/usage/configuration.rst
@@ -756,9 +756,15 @@ documentation on :ref:`intl` for details.
If true, a document's text domain is its docname if it is a top-level
project file and its very base directory otherwise.
+ If set to string, all document's text domain is this string, making all
+ documents use single text domain.
+
By default, the document ``markup/code.rst`` ends up in the ``markup`` text
domain. With this option set to ``False``, it is ``markup/code``.
+ .. versionchanged:: 3.3
+ The string value is now accepted.
+
.. confval:: gettext_uuid
If true, Sphinx generates uuid information for version tracking in message
@@ -2239,6 +2245,12 @@ These options influence manual page output.
.. versionadded:: 1.1
+.. confval:: man_make_section_directory
+
+ If true, make a section directory on build man page. Default is False.
+
+ .. versionadded:: 3.3
+
.. _texinfo-options:
diff --git a/doc/usage/extensions/autodoc.rst b/doc/usage/extensions/autodoc.rst
index 71f49c240..2d8a216c0 100644
--- a/doc/usage/extensions/autodoc.rst
+++ b/doc/usage/extensions/autodoc.rst
@@ -229,7 +229,7 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
.. versionchanged:: 3.0
- It takes an anchestor class name as an argument.
+ It takes an ancestor class name as an argument.
* It's possible to override the signature for explicitly documented callable
objects (functions, methods, classes) with the regular syntax that will
@@ -515,6 +515,44 @@ There are also config values that you can set:
New option ``'description'`` is added.
+.. confval:: autodoc_type_aliases
+
+ A dictionary for users defined `type aliases`__ that maps a type name to the
+ full-qualified object name. It is used to keep type aliases not evaluated in
+ the document. Defaults to empty (``{}``).
+
+ The type aliases are only available if your program enables `Postponed
+ Evaluation of Annotations (PEP 563)`__ feature via ``from __future__ import
+ annotations``.
+
+ For example, there is code using a type alias::
+
+ from __future__ import annotations
+
+ AliasType = Union[List[Dict[Tuple[int, str], Set[int]]], Tuple[str, List[str]]]
+
+ def f() -> AliasType:
+ ...
+
+ If ``autodoc_type_aliases`` is not set, autodoc will generate internal mark-up
+ from this code as following::
+
+ .. py:function:: f() -> Union[List[Dict[Tuple[int, str], Set[int]]], Tuple[str, List[str]]]
+
+ ...
+
+ If you set ``autodoc_type_aliases`` as
+ ``{'AliasType': 'your.module.TypeAlias'}``, it generates a following document
+ internally::
+
+ .. py:function:: f() -> your.module.AliasType:
+
+ ...
+
+ .. __: https://www.python.org/dev/peps/pep-0563/
+ .. __: https://mypy.readthedocs.io/en/latest/kinds_of_types.html#type-aliases
+ .. versionadded:: 3.3
+
.. confval:: autodoc_warningiserror
This value controls the behavior of :option:`sphinx-build -W` during
diff --git a/doc/usage/extensions/coverage.rst b/doc/usage/extensions/coverage.rst
index db989f38d..5e6b04feb 100644
--- a/doc/usage/extensions/coverage.rst
+++ b/doc/usage/extensions/coverage.rst
@@ -51,7 +51,7 @@ should check:
.. versionadded:: 1.1
-.. confval:: coverage_show_missing_items
+.. confval:: coverage_show_missing_items
Print objects that are missing to standard output also.
``False`` by default.
diff --git a/doc/usage/installation.rst b/doc/usage/installation.rst
index c9bef974d..46ef6a51e 100644
--- a/doc/usage/installation.rst
+++ b/doc/usage/installation.rst
@@ -171,7 +171,7 @@ Docker images for Sphinx are published on the `Docker Hub <https://hub.docker.co
- `sphinxdoc/sphinx <https://hub.docker.com/repository/docker/sphinxdoc/sphinx>`_
- `sphinxdoc/sphinx-latexpdf <https://hub.docker.com/repository/docker/sphinxdoc/sphinx-latexpdf>`_
-Former one is used for standard usage of Sphinx, and latter one is mainly used for PDF builds using LaTeX.
+Former one is used for standard usage of Sphinx, and latter one is mainly used for PDF builds using LaTeX.
Please choose one for your purpose.
.. note::
diff --git a/doc/usage/quickstart.rst b/doc/usage/quickstart.rst
index 8d2de021e..1d7e540a6 100644
--- a/doc/usage/quickstart.rst
+++ b/doc/usage/quickstart.rst
@@ -15,7 +15,7 @@ Much of Sphinx's power comes from the richness of its default plain-text markup
format, :doc:`reStructuredText </usage/restructuredtext/index>`, along with
it's :doc:`significant extensibility capabilities </development/index>`.
-The goal of this document is to give you a quick taste of what Sphinx it is and
+The goal of this document is to give you a quick taste of what Sphinx is and
how you might use it. When you're done here, you can check out the
:doc:`installation guide </usage/installation>` followed by the intro to the
default markup format used by Sphinx, :doc:`reStucturedText
diff --git a/doc/usage/restructuredtext/directives.rst b/doc/usage/restructuredtext/directives.rst
index e94106148..92bf78489 100644
--- a/doc/usage/restructuredtext/directives.rst
+++ b/doc/usage/restructuredtext/directives.rst
@@ -665,7 +665,7 @@ __ http://pygments.org/docs/lexers
.. note::
If you want to select only ``[second-section]`` of ini file like the
- following, you can use ``:start-at: [second-section]`` and
+ following, you can use ``:start-at: [second-section]`` and
``:end-before: [third-section]``:
.. code-block:: ini
@@ -692,7 +692,7 @@ __ http://pygments.org/docs/lexers
# [initialize]
app.start(":8000")
# [initialize]
-
+
When lines have been selected in any of the ways described above, the line
numbers in ``emphasize-lines`` refer to those selected lines, counted
diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst
index 311b03d66..f3754ab7c 100644
--- a/doc/usage/restructuredtext/domains.rst
+++ b/doc/usage/restructuredtext/domains.rst
@@ -744,6 +744,18 @@ The following directive can be used for this purpose.
.. versionadded:: 3.2
+
+ .. rubric:: Options
+
+ .. rst:directive:option:: maxdepth: int
+
+ Insert nested declarations as well, up to the total depth given.
+ Use 0 for infinite depth and 1 for just the mentioned declaration.
+ Defaults to 1.
+
+ .. versionadded:: 3.3
+
+
.. c:namespace-pop::
diff --git a/package-lock.json b/package-lock.json
index e3fb91e21..087afcf3e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -385,12 +385,6 @@
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
"dev": true
},
- "eventemitter3": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
- "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==",
- "dev": true
- },
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -535,14 +529,22 @@
}
},
"http-proxy": {
- "version": "1.17.0",
- "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz",
- "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==",
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
"dev": true,
"requires": {
- "eventemitter3": "^3.0.0",
+ "eventemitter3": "^4.0.0",
"follow-redirects": "^1.0.0",
"requires-port": "^1.0.0"
+ },
+ "dependencies": {
+ "eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+ "dev": true
+ }
}
},
"iconv-lite": {
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index 473896630..fde2345a8 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -32,8 +32,8 @@ if 'PYTHONWARNINGS' not in os.environ:
warnings.filterwarnings('ignore', "'U' mode is deprecated",
DeprecationWarning, module='docutils.io')
-__version__ = '3.2.2+'
-__released__ = '3.2.2' # used when Sphinx builds its own docs
+__version__ = '3.3.0+'
+__released__ = '3.3.0' # used when Sphinx builds its own docs
#: Version info for better programmatic use.
#:
@@ -43,7 +43,7 @@ __released__ = '3.2.2' # used when Sphinx builds its own docs
#:
#: .. versionadded:: 1.2
#: Before version 1.2, check the string ``sphinx.__version__``.
-version_info = (3, 2, 2, 'beta', 0)
+version_info = (3, 3, 0, 'beta', 0)
package_dir = path.abspath(path.dirname(__file__))
diff --git a/sphinx/application.py b/sphinx/application.py
index d84a2c975..59ac04b20 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -18,10 +18,11 @@ import warnings
from collections import deque
from io import StringIO
from os import path
-from typing import Any, Callable, Dict, IO, List, Tuple, Union
+from typing import Any, Callable, Dict, IO, List, Optional, Tuple, Union
from docutils import nodes
from docutils.nodes import Element, TextElement
+from docutils.parsers import Parser
from docutils.parsers.rst import Directive, roles
from docutils.transforms import Transform
from pygments.lexer import Lexer
@@ -293,7 +294,10 @@ class Sphinx:
if catalog.domain == 'sphinx' and catalog.is_outdated():
catalog.write_mo(self.config.language)
- locale_dirs = [None, path.join(package_dir, 'locale')] + list(repo.locale_dirs)
+ locale_dirs = [None] # type: List[Optional[str]]
+ locale_dirs += list(repo.locale_dirs)
+ locale_dirs += [path.join(package_dir, 'locale')]
+
self.translator, has_translation = locale.init(locale_dirs, self.config.language)
if has_translation or self.config.language == 'en':
# "en" never needs to be translated
@@ -468,8 +472,10 @@ class Sphinx:
def add_builder(self, builder: "Type[Builder]", override: bool = False) -> None:
"""Register a new builder.
- *builder* must be a class that inherits from
- :class:`~sphinx.builders.Builder`.
+ *builder* must be a class that inherits from :class:`~sphinx.builders.Builder`.
+
+ If *override* is True, the given *builder* is forcedly installed even if
+ a builder having the same name is already installed.
.. versionchanged:: 1.8
Add *override* keyword.
@@ -526,6 +532,9 @@ class Sphinx:
builtin translator. This allows extensions to use custom translator
and define custom nodes for the translator (see :meth:`add_node`).
+ If *override* is True, the given *translator_class* is forcedly installed even if
+ a translator for *name* is already installed.
+
.. versionadded:: 1.3
.. versionchanged:: 1.8
Add *override* keyword.
@@ -560,6 +569,9 @@ class Sphinx:
Obviously, translators for which you don't specify visitor methods will
choke on the node when encountered in a document to translate.
+ If *override* is True, the given *node* is forcedly installed even if
+ a node having the same name is already installed.
+
.. versionchanged:: 0.5
Added the support for keyword arguments giving visit functions.
"""
@@ -595,6 +607,9 @@ class Sphinx:
Other keyword arguments are used for node visitor functions. See the
:meth:`.Sphinx.add_node` for details.
+ If *override* is True, the given *node* is forcedly installed even if
+ a node having the same name is already installed.
+
.. versionadded:: 1.4
"""
self.registry.add_enumerable_node(node, figtype, title_getter, override=override)
@@ -608,14 +623,14 @@ class Sphinx:
details, see `the Docutils docs
<http://docutils.sourceforge.net/docs/howto/rst-directives.html>`_ .
- For example, the (already existing) :rst:dir:`literalinclude` directive
- would be added like this:
+ For example, a custom directive named ``my-directive`` would be added
+ like this:
.. code-block:: python
from docutils.parsers.rst import Directive, directives
- class LiteralIncludeDirective(Directive):
+ class MyDirective(Directive):
has_content = True
required_arguments = 1
optional_arguments = 0
@@ -628,7 +643,11 @@ class Sphinx:
def run(self):
...
- add_directive('literalinclude', LiteralIncludeDirective)
+ def setup(app):
+ add_directive('my-directive', MyDirective)
+
+ If *override* is True, the given *cls* is forcedly installed even if
+ a directive named as *name* is already installed.
.. versionchanged:: 0.6
Docutils 0.5-style directive classes are now supported.
@@ -652,6 +671,9 @@ class Sphinx:
<http://docutils.sourceforge.net/docs/howto/rst-roles.html>`_ for
more information.
+ If *override* is True, the given *role* is forcedly installed even if
+ a role named as *name* is already installed.
+
.. versionchanged:: 1.8
Add *override* keyword.
"""
@@ -667,6 +689,9 @@ class Sphinx:
Register a Docutils role that does nothing but wrap its contents in the
node given by *nodeclass*.
+ If *override* is True, the given *nodeclass* is forcedly installed even if
+ a role named as *name* is already installed.
+
.. versionadded:: 0.6
.. versionchanged:: 1.8
Add *override* keyword.
@@ -686,6 +711,9 @@ class Sphinx:
Make the given *domain* (which must be a class; more precisely, a
subclass of :class:`~sphinx.domains.Domain`) known to Sphinx.
+ If *override* is True, the given *domain* is forcedly installed even if
+ a domain having the same name is already installed.
+
.. versionadded:: 1.0
.. versionchanged:: 1.8
Add *override* keyword.
@@ -699,6 +727,9 @@ class Sphinx:
Like :meth:`add_directive`, but the directive is added to the domain
named *domain*.
+ If *override* is True, the given *directive* is forcedly installed even if
+ a directive named as *name* is already installed.
+
.. versionadded:: 1.0
.. versionchanged:: 1.8
Add *override* keyword.
@@ -712,6 +743,9 @@ class Sphinx:
Like :meth:`add_role`, but the role is added to the domain named
*domain*.
+ If *override* is True, the given *role* is forcedly installed even if
+ a role named as *name* is already installed.
+
.. versionadded:: 1.0
.. versionchanged:: 1.8
Add *override* keyword.
@@ -725,6 +759,9 @@ class Sphinx:
Add a custom *index* class to the domain named *domain*. *index* must
be a subclass of :class:`~sphinx.domains.Index`.
+ If *override* is True, the given *index* is forcedly installed even if
+ an index having the same name is already installed.
+
.. versionadded:: 1.0
.. versionchanged:: 1.8
Add *override* keyword.
@@ -788,6 +825,9 @@ class Sphinx:
For the role content, you have the same syntactical possibilities as
for standard Sphinx roles (see :ref:`xref-syntax`).
+ If *override* is True, the given object_type is forcedly installed even if
+ an object_type having the same name is already installed.
+
.. versionchanged:: 1.8
Add *override* keyword.
"""
@@ -824,6 +864,9 @@ class Sphinx:
(Of course, the element following the ``topic`` directive needn't be a
section.)
+ If *override* is True, the given crossref_type is forcedly installed even if
+ a crossref_type having the same name is already installed.
+
.. versionchanged:: 1.8
Add *override* keyword.
"""
@@ -1004,7 +1047,7 @@ class Sphinx:
logger.debug('[app] adding lexer: %r', (alias, lexer))
if isinstance(lexer, Lexer):
warnings.warn('app.add_lexer() API changed; '
- 'Please give lexer class instead instance',
+ 'Please give lexer class instead of instance',
RemovedInSphinx40Warning, stacklevel=2)
lexers[alias] = lexer
else:
@@ -1019,6 +1062,9 @@ class Sphinx:
new types of objects. See the source of the autodoc module for
examples on how to subclass :class:`Documenter`.
+ If *override* is True, the given *cls* is forcedly installed even if
+ a documenter having the same name is already installed.
+
.. todo:: Add real docs for Documenter and subclassing
.. versionadded:: 0.6
@@ -1067,13 +1113,19 @@ class Sphinx:
Same as :confval:`source_suffix`. The users can override this
using the setting.
+ If *override* is True, the given *suffix* is forcedly installed even if
+ a same suffix is already installed.
+
.. versionadded:: 1.8
"""
self.registry.add_source_suffix(suffix, filetype, override=override)
- def add_source_parser(self, *args: Any, **kwargs: Any) -> None:
+ def add_source_parser(self, parser: "Type[Parser]", override: bool = False) -> None:
"""Register a parser class.
+ If *override* is True, the given *parser* is forcedly installed even if
+ a parser for the same suffix is already installed.
+
.. versionadded:: 1.4
.. versionchanged:: 1.8
*suffix* argument is deprecated. It only accepts *parser* argument.
@@ -1081,7 +1133,7 @@ class Sphinx:
.. versionchanged:: 1.8
Add *override* keyword.
"""
- self.registry.add_source_parser(*args, **kwargs)
+ self.registry.add_source_parser(parser, override=override)
def add_env_collector(self, collector: "Type[EnvironmentCollector]") -> None:
"""Register an environment collector class.
diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py
index 8e30762e9..f8a19c57a 100644
--- a/sphinx/builders/gettext.py
+++ b/sphinx/builders/gettext.py
@@ -316,7 +316,7 @@ class MessageCatalogBuilder(I18nBuilder):
def setup(app: Sphinx) -> Dict[str, Any]:
app.add_builder(MessageCatalogBuilder)
- app.add_config_value('gettext_compact', True, 'gettext')
+ app.add_config_value('gettext_compact', True, 'gettext', Any)
app.add_config_value('gettext_location', True, 'gettext')
app.add_config_value('gettext_uuid', False, 'gettext')
app.add_config_value('gettext_auto_build', True, 'env')
diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py
index 923212a99..beb650991 100644
--- a/sphinx/builders/html/__init__.py
+++ b/sphinx/builders/html/__init__.py
@@ -641,17 +641,17 @@ class StandaloneHTMLBuilder(Builder):
def gen_additional_pages(self) -> None:
# additional pages from conf.py
for pagename, template in self.config.html_additional_pages.items():
- logger.info(' ' + pagename, nonl=True)
+ logger.info(pagename + ' ', nonl=True)
self.handle_page(pagename, {}, template)
# the search page
if self.search:
- logger.info(' search', nonl=True)
+ logger.info('search ', nonl=True)
self.handle_page('search', {}, 'search.html')
# the opensearch xml file
if self.config.html_use_opensearch and self.search:
- logger.info(' opensearch', nonl=True)
+ logger.info('opensearch ', nonl=True)
fn = path.join(self.outdir, '_static', 'opensearch.xml')
self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn)
@@ -669,7 +669,7 @@ class StandaloneHTMLBuilder(Builder):
'genindexcounts': indexcounts,
'split_index': self.config.html_split_index,
}
- logger.info(' genindex', nonl=True)
+ logger.info('genindex ', nonl=True)
if self.config.html_split_index:
self.handle_page('genindex', genindexcontext,
@@ -691,7 +691,7 @@ class StandaloneHTMLBuilder(Builder):
'content': content,
'collapse_index': collapse,
}
- logger.info(' ' + indexname, nonl=True)
+ logger.info(indexname + ' ', nonl=True)
self.handle_page(indexname, indexcontext, 'domainindex.html')
def copy_image_files(self) -> None:
@@ -751,18 +751,27 @@ class StandaloneHTMLBuilder(Builder):
copyfile(jsfile, path.join(self.outdir, '_static', '_stemmer.js'))
def copy_theme_static_files(self, context: Dict) -> None:
+ def onerror(filename: str, error: Exception) -> None:
+ logger.warning(__('Failed to copy a file in html_static_file: %s: %r'),
+ filename, error)
+
if self.theme:
for entry in self.theme.get_theme_dirs()[::-1]:
copy_asset(path.join(entry, 'static'),
path.join(self.outdir, '_static'),
- excluded=DOTFILES, context=context, renderer=self.templates)
+ excluded=DOTFILES, context=context,
+ renderer=self.templates, onerror=onerror)
def copy_html_static_files(self, context: Dict) -> None:
+ def onerror(filename: str, error: Exception) -> None:
+ logger.warning(__('Failed to copy a file in html_static_file: %s: %r'),
+ filename, error)
+
excluded = Matcher(self.config.exclude_patterns + ["**/.*"])
for entry in self.config.html_static_path:
copy_asset(path.join(self.confdir, entry),
path.join(self.outdir, '_static'),
- excluded, context=context, renderer=self.templates)
+ excluded, context=context, renderer=self.templates, onerror=onerror)
def copy_html_logo(self) -> None:
if self.config.html_logo:
@@ -776,7 +785,7 @@ class StandaloneHTMLBuilder(Builder):
def copy_static_files(self) -> None:
try:
- with progress_message(__('copying static files... ')):
+ with progress_message(__('copying static files')):
ensuredir(path.join(self.outdir, '_static'))
# prepare context for templates
diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py
index 88c471675..8fd1c077d 100644
--- a/sphinx/builders/latex/__init__.py
+++ b/sphinx/builders/latex/__init__.py
@@ -24,7 +24,7 @@ from sphinx.builders.latex.constants import ADDITIONAL_SETTINGS, DEFAULT_SETTING
from sphinx.builders.latex.theming import Theme, ThemeFactory
from sphinx.builders.latex.util import ExtBabel
from sphinx.config import Config, ENUM
-from sphinx.deprecation import RemovedInSphinx40Warning
+from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.errors import NoUri, SphinxError
from sphinx.locale import _, __
@@ -128,8 +128,6 @@ class LaTeXBuilder(Builder):
self.docnames = [] # type: Iterable[str]
self.document_data = [] # type: List[Tuple[str, str, str, str, str, bool]]
self.themes = ThemeFactory(self.app)
- self.usepackages = self.app.registry.latex_packages
- self.usepackages_after_hyperref = self.app.registry.latex_packages_after_hyperref
texescape.init()
self.init_context()
@@ -179,10 +177,6 @@ class LaTeXBuilder(Builder):
key = (self.config.latex_engine, self.config.language[:2])
self.context.update(ADDITIONAL_SETTINGS.get(key, {}))
- # Apply extension settings to context
- self.context['packages'] = self.usepackages
- self.context['packages_after_hyperref'] = self.usepackages_after_hyperref
-
# Apply user settings to context
self.context.update(self.config.latex_elements)
self.context['release'] = self.config.release
@@ -203,6 +197,13 @@ class LaTeXBuilder(Builder):
# Show the release label only if release value exists
self.context.setdefault('releasename', _('Release'))
+ def update_context(self) -> None:
+ """Update template variables for .tex file just before writing."""
+ # Apply extension settings to context
+ registry = self.app.registry
+ self.context['packages'] = registry.latex_packages
+ self.context['packages_after_hyperref'] = registry.latex_packages_after_hyperref
+
def init_babel(self) -> None:
self.babel = ExtBabel(self.config.language, not self.context['babel'])
if self.config.language and not self.babel.is_supported_language():
@@ -290,6 +291,7 @@ class LaTeXBuilder(Builder):
doctree['tocdepth'] = tocdepth
self.post_process_images(doctree)
self.update_doc_context(title, author, theme)
+ self.update_context()
with progress_message(__("writing")):
docsettings._author = author
@@ -448,6 +450,18 @@ class LaTeXBuilder(Builder):
filename = path.join(package_dir, 'templates', 'latex', 'sphinxmessages.sty_t')
copy_asset_file(filename, self.outdir, context=context, renderer=LaTeXRenderer())
+ @property
+ def usepackages(self) -> List[Tuple[str, str]]:
+ warnings.warn('LaTeXBuilder.usepackages is deprecated.',
+ RemovedInSphinx50Warning, stacklevel=2)
+ return self.app.registry.latex_packages
+
+ @property
+ def usepackages_after_hyperref(self) -> List[Tuple[str, str]]:
+ warnings.warn('LaTeXBuilder.usepackages_after_hyperref is deprecated.',
+ RemovedInSphinx50Warning, stacklevel=2)
+ return self.app.registry.latex_packages_after_hyperref
+
def patch_settings(settings: Any) -> Any:
"""Make settings object to show deprecation messages."""
@@ -503,9 +517,9 @@ def validate_latex_theme_options(app: Sphinx, config: Config) -> None:
config.latex_theme_options.pop(key)
-def install_pakcages_for_ja(app: Sphinx) -> None:
+def install_packages_for_ja(app: Sphinx) -> None:
"""Install packages for Japanese."""
- if app.config.language == 'ja':
+ if app.config.language == 'ja' and app.config.latex_engine in ('platex', 'uplatex'):
app.add_latex_package('pxjahyper', after_hyperref=True)
@@ -556,7 +570,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_builder(LaTeXBuilder)
app.connect('config-inited', validate_config_values, priority=800)
app.connect('config-inited', validate_latex_theme_options, priority=800)
- app.connect('builder-inited', install_pakcages_for_ja)
+ app.connect('builder-inited', install_packages_for_ja)
app.add_config_value('latex_engine', default_latex_engine, None,
ENUM('pdflatex', 'xelatex', 'lualatex', 'platex', 'uplatex'))
diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py
index 9b54afc7c..a9e6b05b0 100644
--- a/sphinx/builders/linkcheck.py
+++ b/sphinx/builders/linkcheck.py
@@ -166,6 +166,7 @@ class CheckExternalLinksBuilder(Builder):
# Read the whole document and see if #anchor exists
response = requests.get(req_url, stream=True, config=self.app.config,
auth=auth_info, **kwargs)
+ response.raise_for_status()
found = check_anchor(response, unquote(anchor))
if not found:
@@ -210,7 +211,7 @@ class CheckExternalLinksBuilder(Builder):
else:
return 'redirected', new_url, 0
- def check() -> Tuple[str, str, int]:
+ def check(docname: str) -> Tuple[str, str, int]:
# check for various conditions without bothering the network
if len(uri) == 0 or uri.startswith(('#', 'mailto:')):
return 'unchecked', '', 0
@@ -219,7 +220,8 @@ class CheckExternalLinksBuilder(Builder):
# non supported URI schemes (ex. ftp)
return 'unchecked', '', 0
else:
- if path.exists(path.join(self.srcdir, uri)):
+ srcdir = path.dirname(self.env.doc2path(docname))
+ if path.exists(path.join(srcdir, uri)):
return 'working', '', 0
else:
for rex in self.to_ignore:
@@ -256,7 +258,7 @@ class CheckExternalLinksBuilder(Builder):
uri, docname, lineno = self.wqueue.get()
if uri is None:
break
- status, info, code = check()
+ status, info, code = check(docname)
self.rqueue.put((uri, docname, lineno, status, info, code))
def process_result(self, result: Tuple[str, str, int, str, str, int]) -> None:
diff --git a/sphinx/builders/manpage.py b/sphinx/builders/manpage.py
index 4166dece9..2a10ba62f 100644
--- a/sphinx/builders/manpage.py
+++ b/sphinx/builders/manpage.py
@@ -24,7 +24,7 @@ from sphinx.util import logging
from sphinx.util import progress_message
from sphinx.util.console import darkgreen # type: ignore
from sphinx.util.nodes import inline_all_toctrees
-from sphinx.util.osutil import make_filename_from_project
+from sphinx.util.osutil import ensuredir, make_filename_from_project
from sphinx.writers.manpage import ManualPageWriter, ManualPageTranslator
@@ -80,7 +80,12 @@ class ManualPageBuilder(Builder):
docsettings.authors = authors
docsettings.section = section
- targetname = '%s.%s' % (name, section)
+ if self.config.man_make_section_directory:
+ ensuredir(path.join(self.outdir, str(section)))
+ targetname = '%s/%s.%s' % (section, name, section)
+ else:
+ targetname = '%s.%s' % (name, section)
+
logger.info(darkgreen(targetname) + ' { ', nonl=True)
destination = FileOutput(
destination_path=path.join(self.outdir, targetname),
@@ -115,6 +120,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('man_pages', default_man_pages, None)
app.add_config_value('man_show_urls', False, None)
+ app.add_config_value('man_make_section_directory', False, None)
return {
'version': 'builtin',
diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py
index f1f4a341a..4ca849cf0 100644
--- a/sphinx/directives/code.py
+++ b/sphinx/directives/code.py
@@ -72,7 +72,7 @@ def dedent_lines(lines: List[str], dedent: int, location: Tuple[str, int] = None
return lines
if any(s[:dedent].strip() for s in lines):
- logger.warning(__('Over dedent has detected'), location=location)
+ logger.warning(__('non-whitespace stripped by dedent'), location=location)
new_lines = []
for line in lines:
diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py
index 16f2c876f..fafe827af 100644
--- a/sphinx/domains/c.py
+++ b/sphinx/domains/c.py
@@ -140,8 +140,8 @@ class ASTIdentifier(ASTBaseBase):
reftype='identifier',
reftarget=targetText, modname=None,
classname=None)
- # key = symbol.get_lookup_key()
- # pnode['c:parent_key'] = key
+ key = symbol.get_lookup_key()
+ pnode['c:parent_key'] = key
if self.is_anon():
pnode += nodes.strong(text="[anonymous]")
else:
@@ -1584,6 +1584,11 @@ class Symbol:
yield s
@property
+ def children(self) -> Iterator["Symbol"]:
+ for c in self._children:
+ yield c
+
+ @property
def children_recurse_anon(self) -> Iterator["Symbol"]:
for c in self._children:
yield c
@@ -3425,10 +3430,13 @@ class CNamespacePopObject(SphinxDirective):
class AliasNode(nodes.Element):
- def __init__(self, sig: str, env: "BuildEnvironment" = None,
+ def __init__(self, sig: str, maxdepth: int, document: Any, env: "BuildEnvironment" = None,
parentKey: LookupKey = None) -> None:
super().__init__()
self.sig = sig
+ self.maxdepth = maxdepth
+ assert maxdepth >= 0
+ self.document = document
if env is not None:
if 'c:parent_symbol' not in env.temp_data:
root = env.domaindata['c']['root_symbol']
@@ -3445,6 +3453,37 @@ class AliasNode(nodes.Element):
class AliasTransform(SphinxTransform):
default_priority = ReferencesResolver.default_priority - 1
+ def _render_symbol(self, s: Symbol, maxdepth: int, document: Any) -> List[Node]:
+ nodes = [] # type: List[Node]
+ options = dict() # type: ignore
+ signode = addnodes.desc_signature('', '')
+ nodes.append(signode)
+ s.declaration.describe_signature(signode, 'markName', self.env, options)
+ if maxdepth == 0:
+ recurse = True
+ elif maxdepth == 1:
+ recurse = False
+ else:
+ maxdepth -= 1
+ recurse = True
+ if recurse:
+ content = addnodes.desc_content()
+ desc = addnodes.desc()
+ content.append(desc)
+ desc.document = document
+ desc['domain'] = 'c'
+ # 'desctype' is a backwards compatible attribute
+ desc['objtype'] = desc['desctype'] = 'alias'
+ desc['noindex'] = True
+
+ for sChild in s.children:
+ childNodes = self._render_symbol(sChild, maxdepth, document)
+ desc.extend(childNodes)
+
+ if len(desc.children) != 0:
+ nodes.append(content)
+ return nodes
+
def apply(self, **kwargs: Any) -> None:
for node in self.document.traverse(AliasNode):
sig = node.sig
@@ -3485,17 +3524,16 @@ class AliasTransform(SphinxTransform):
logger.warning("Could not find C declaration for alias '%s'." % name,
location=node)
node.replace_self(signode)
- else:
- nodes = []
- options = dict() # type: ignore
- signode = addnodes.desc_signature(sig, '')
- nodes.append(signode)
- s.declaration.describe_signature(signode, 'markName', self.env, options)
- node.replace_self(nodes)
+ continue
+
+ nodes = self._render_symbol(s, maxdepth=node.maxdepth, document=node.document)
+ node.replace_self(nodes)
class CAliasObject(ObjectDescription):
- option_spec = {} # type: Dict
+ option_spec = {
+ 'maxdepth': directives.nonnegative_int
+ } # type: Dict
def run(self) -> List[Node]:
if ':' in self.name:
@@ -3511,16 +3549,10 @@ class CAliasObject(ObjectDescription):
node['noindex'] = True
self.names = [] # type: List[str]
+ maxdepth = self.options.get('maxdepth', 1)
signatures = self.get_signatures()
for i, sig in enumerate(signatures):
- node.append(AliasNode(sig, env=self.env))
-
- contentnode = addnodes.desc_content()
- node.append(contentnode)
- self.before_content()
- self.state.nested_parse(self.content, self.content_offset, contentnode)
- self.env.temp_data['object'] = None
- self.after_content()
+ node.append(AliasNode(sig, maxdepth, self.state.document, env=self.env))
return [node]
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
index 7eaaa531d..39f67b54e 100644
--- a/sphinx/domains/std.py
+++ b/sphinx/domains/std.py
@@ -500,7 +500,8 @@ class ProductionList(SphinxDirective):
except ValueError:
break
subnode = addnodes.production(rule)
- subnode['tokenname'] = name.strip()
+ name = name.strip()
+ subnode['tokenname'] = name
if subnode['tokenname']:
prefix = 'grammar-token-%s' % productionGroup
node_id = make_id(self.env, self.state.document, prefix, name)
diff --git a/sphinx/environment/collectors/toctree.py b/sphinx/environment/collectors/toctree.py
index e168bd9c4..d6cdc8354 100644
--- a/sphinx/environment/collectors/toctree.py
+++ b/sphinx/environment/collectors/toctree.py
@@ -224,6 +224,10 @@ class TocTreeCollector(EnvironmentCollector):
def get_figtype(node: Node) -> str:
for domain in env.domains.values():
figtype = domain.get_enumerable_node_type(node)
+ if domain.name == 'std' and not domain.get_numfig_title(node): # type: ignore
+ # Skip if uncaptioned node
+ continue
+
if figtype:
return figtype
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 23cdc4b28..978fd5df8 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -94,7 +94,10 @@ def members_option(arg: Any) -> Union[object, List[str]]:
"""Used to convert the :members: option to auto directives."""
if arg is None or arg is True:
return ALL
- return [x.strip() for x in arg.split(',') if x.strip()]
+ elif arg is False:
+ return None
+ else:
+ return [x.strip() for x in arg.split(',') if x.strip()]
def members_set_option(arg: Any) -> Union[object, Set[str]]:
@@ -172,7 +175,7 @@ def merge_members_option(options: Dict) -> None:
members = options.setdefault('members', [])
for key in {'private-members', 'special-members'}:
- if key in options and options[key] is not ALL:
+ if key in options and options[key] not in (ALL, None):
for member in options[key]:
if member not in members:
members.append(member)
@@ -532,6 +535,11 @@ class Documenter:
self.env.app.emit('autodoc-process-docstring',
self.objtype, self.fullname, self.object,
self.options, docstringlines)
+
+ if docstringlines and docstringlines[-1] != '':
+ # append a blank line to the end of the docstring
+ docstringlines.append('')
+
yield from docstringlines
def get_sourcename(self) -> str:
@@ -1205,7 +1213,8 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
try:
self.env.app.emit('autodoc-before-process-signature', self.object, False)
- sig = inspect.signature(self.object, follow_wrapped=True)
+ sig = inspect.signature(self.object, follow_wrapped=True,
+ type_aliases=self.env.config.autodoc_type_aliases)
args = stringify_signature(sig, **kwargs)
except TypeError as exc:
logger.warning(__("Failed to get a function signature for %s: %s"),
@@ -1254,7 +1263,9 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
if overloaded:
__globals__ = safe_getattr(self.object, '__globals__', {})
for overload in self.analyzer.overloads.get('.'.join(self.objpath)):
- overload = evaluate_signature(overload, __globals__)
+ overload = evaluate_signature(overload, __globals__,
+ self.env.config.autodoc_type_aliases)
+
sig = stringify_signature(overload, **kwargs)
sigs.append(sig)
@@ -1263,7 +1274,7 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
"""Annotate type hint to the first argument of function if needed."""
try:
- sig = inspect.signature(func)
+ sig = inspect.signature(func, type_aliases=self.env.config.autodoc_type_aliases)
except TypeError as exc:
logger.warning(__("Failed to get a function signature for %s: %s"),
self.fullname, exc)
@@ -1291,6 +1302,11 @@ class SingledispatchFunctionDocumenter(FunctionDocumenter):
Retained for backwards compatibility, now does the same as the FunctionDocumenter
"""
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
+ warnings.warn("%s is deprecated." % self.__class__.__name__,
+ RemovedInSphinx50Warning, stacklevel=2)
+ super().__init__(*args, **kwargs)
+
class DecoratorDocumenter(FunctionDocumenter):
"""
@@ -1317,6 +1333,12 @@ _METACLASS_CALL_BLACKLIST = [
]
+# Types whose __new__ signature is a pass-thru.
+_CLASS_NEW_BLACKLIST = [
+ 'typing.Generic.__new__',
+]
+
+
class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: ignore
"""
Specialized Documenter subclass for classes.
@@ -1378,17 +1400,24 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
if call is not None:
self.env.app.emit('autodoc-before-process-signature', call, True)
try:
- sig = inspect.signature(call, bound_method=True)
+ sig = inspect.signature(call, bound_method=True,
+ type_aliases=self.env.config.autodoc_type_aliases)
return type(self.object), '__call__', sig
except ValueError:
pass
# Now we check if the 'obj' class has a '__new__' method
new = get_user_defined_function_or_method(self.object, '__new__')
+
+ if new is not None:
+ if "{0.__module__}.{0.__qualname__}".format(new) in _CLASS_NEW_BLACKLIST:
+ new = None
+
if new is not None:
self.env.app.emit('autodoc-before-process-signature', new, True)
try:
- sig = inspect.signature(new, bound_method=True)
+ sig = inspect.signature(new, bound_method=True,
+ type_aliases=self.env.config.autodoc_type_aliases)
return self.object, '__new__', sig
except ValueError:
pass
@@ -1398,7 +1427,8 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
if init is not None:
self.env.app.emit('autodoc-before-process-signature', init, True)
try:
- sig = inspect.signature(init, bound_method=True)
+ sig = inspect.signature(init, bound_method=True,
+ type_aliases=self.env.config.autodoc_type_aliases)
return self.object, '__init__', sig
except ValueError:
pass
@@ -1409,7 +1439,8 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
# the signature from, so just pass the object itself to our hook.
self.env.app.emit('autodoc-before-process-signature', self.object, False)
try:
- sig = inspect.signature(self.object, bound_method=False)
+ sig = inspect.signature(self.object, bound_method=False,
+ type_aliases=self.env.config.autodoc_type_aliases)
return None, None, sig
except ValueError:
pass
@@ -1440,23 +1471,16 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
return ''
sig = super().format_signature()
-
- overloaded = False
- qualname = None
- # TODO: recreate analyzer for the module of class (To be clear, owner of the method)
- if self._signature_class and self._signature_method_name and self.analyzer:
- qualname = '.'.join([self._signature_class.__qualname__,
- self._signature_method_name])
- if qualname in self.analyzer.overloads:
- overloaded = True
-
sigs = []
- if overloaded:
+
+ overloads = self.get_overloaded_signatures()
+ if overloads:
# Use signatures for overloaded methods instead of the implementation method.
method = safe_getattr(self._signature_class, self._signature_method_name, None)
__globals__ = safe_getattr(method, '__globals__', {})
- for overload in self.analyzer.overloads.get(qualname):
- overload = evaluate_signature(overload, __globals__)
+ for overload in overloads:
+ overload = evaluate_signature(overload, __globals__,
+ self.env.config.autodoc_type_aliases)
parameters = list(overload.parameters.values())
overload = overload.replace(parameters=parameters[1:],
@@ -1468,6 +1492,20 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
return "\n".join(sigs)
+ def get_overloaded_signatures(self) -> List[Signature]:
+ if self._signature_class and self._signature_method_name:
+ for cls in self._signature_class.__mro__:
+ try:
+ analyzer = ModuleAnalyzer.for_module(cls.__module__)
+ analyzer.parse()
+ qualname = '.'.join([cls.__qualname__, self._signature_method_name])
+ if qualname in analyzer.overloads:
+ return analyzer.overloads.get(qualname)
+ except PycodeError:
+ pass
+
+ return []
+
def add_directive_header(self, sig: str) -> None:
sourcename = self.get_sourcename()
@@ -1704,7 +1742,8 @@ class GenericAliasDocumenter(DataDocumenter):
return inspect.isgenericalias(member)
def add_directive_header(self, sig: str) -> None:
- self.options.annotation = SUPPRESS # type: ignore
+ self.options = Options(self.options)
+ self.options['annotation'] = SUPPRESS
super().add_directive_header(sig)
def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
@@ -1728,7 +1767,8 @@ class TypeVarDocumenter(DataDocumenter):
return isinstance(member, TypeVar) and isattr
def add_directive_header(self, sig: str) -> None:
- self.options.annotation = SUPPRESS # type: ignore
+ self.options = Options(self.options)
+ self.options['annotation'] = SUPPRESS
super().add_directive_header(sig)
def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]:
@@ -1801,11 +1841,13 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
else:
if inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name):
self.env.app.emit('autodoc-before-process-signature', self.object, False)
- sig = inspect.signature(self.object, bound_method=False)
+ sig = inspect.signature(self.object, bound_method=False,
+ type_aliases=self.env.config.autodoc_type_aliases)
else:
self.env.app.emit('autodoc-before-process-signature', self.object, True)
sig = inspect.signature(self.object, bound_method=True,
- follow_wrapped=True)
+ follow_wrapped=True,
+ type_aliases=self.env.config.autodoc_type_aliases)
args = stringify_signature(sig, **kwargs)
except TypeError as exc:
logger.warning(__("Failed to get a method signature for %s: %s"),
@@ -1865,7 +1907,9 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
if overloaded:
__globals__ = safe_getattr(self.object, '__globals__', {})
for overload in self.analyzer.overloads.get('.'.join(self.objpath)):
- overload = evaluate_signature(overload, __globals__)
+ overload = evaluate_signature(overload, __globals__,
+ self.env.config.autodoc_type_aliases)
+
if not inspect.isstaticmethod(self.object, cls=self.parent,
name=self.object_name):
parameters = list(overload.parameters.values())
@@ -1878,7 +1922,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
"""Annotate type hint to the first argument of function if needed."""
try:
- sig = inspect.signature(func)
+ sig = inspect.signature(func, type_aliases=self.env.config.autodoc_type_aliases)
except TypeError as exc:
logger.warning(__("Failed to get a method signature for %s: %s"),
self.fullname, exc)
@@ -1905,6 +1949,11 @@ class SingledispatchMethodDocumenter(MethodDocumenter):
Retained for backwards compatibility, now does the same as the MethodDocumenter
"""
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
+ warnings.warn("%s is deprecated." % self.__class__.__name__,
+ RemovedInSphinx50Warning, stacklevel=2)
+ super().__init__(*args, **kwargs)
+
class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # type: ignore
"""
@@ -2218,6 +2267,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('autodoc_mock_imports', [], True)
app.add_config_value('autodoc_typehints', "signature", True,
ENUM("signature", "description", "none"))
+ app.add_config_value('autodoc_type_aliases', {}, True)
app.add_config_value('autodoc_warningiserror', True, True)
app.add_config_value('autodoc_inherit_docstrings', True, True)
app.add_event('autodoc-before-process-signature')
diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py
index 7b2383fca..71a123b15 100644
--- a/sphinx/ext/inheritance_diagram.py
+++ b/sphinx/ext/inheritance_diagram.py
@@ -66,6 +66,10 @@ module_sig_re = re.compile(r'''^(?:([\w.]*)\.)? # module names
''', re.VERBOSE)
+py_builtins = [obj for obj in vars(builtins).values()
+ if inspect.isclass(obj)]
+
+
def try_import(objname: str) -> Any:
"""Import a object or module using *name* and *currentmodule*.
*name* should be a relative name from *currentmodule* or
@@ -178,7 +182,6 @@ class InheritanceGraph:
traverse to. Multiple names can be specified separated by comma.
"""
all_classes = {}
- py_builtins = vars(builtins).values()
def recurse(cls: Any) -> None:
if not show_builtins and cls in py_builtins:
diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py
index 4397d0e41..d36fdd17a 100644
--- a/sphinx/ext/napoleon/docstring.py
+++ b/sphinx/ext/napoleon/docstring.py
@@ -31,12 +31,12 @@ logger = logging.getLogger(__name__)
_directive_regex = re.compile(r'\.\. \S+::')
_google_section_regex = re.compile(r'^(\s|\w)+:\s*$')
-_google_typed_arg_regex = re.compile(r'\s*(.+?)\s*\(\s*(.*[^\s]+)\s*\)')
+_google_typed_arg_regex = re.compile(r'(.+?)\(\s*(.*[^\s]+)\s*\)')
_numpy_section_regex = re.compile(r'^[=\-`:\'"~^_*+#<>]{2,}\s*$')
_single_colon_regex = re.compile(r'(?<!:):(?!:)')
_xref_or_code_regex = re.compile(
r'((?::(?:[a-zA-Z0-9]+[\-_+:.])*[a-zA-Z0-9]+:`.+?`)|'
- r'(?:``.+``))')
+ r'(?:``.+?``))')
_xref_regex = re.compile(
r'(?:(?::(?:[a-zA-Z0-9]+[\-_+:.])*[a-zA-Z0-9]+:)?`.+?`)'
)
@@ -254,7 +254,7 @@ class GoogleDocstring:
if parse_type:
match = _google_typed_arg_regex.match(before)
if match:
- _name = match.group(1)
+ _name = match.group(1).strip()
_type = match.group(2)
_name = self._escape_args_and_kwargs(_name)
diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py
index 385ca3566..5210dc725 100644
--- a/sphinx/locale/__init__.py
+++ b/sphinx/locale/__init__.py
@@ -106,7 +106,7 @@ class _TranslationProxy(UserString):
translators = defaultdict(NullTranslations) # type: Dict[Tuple[str, str], NullTranslations]
-def init(locale_dirs: List[str], language: str,
+def init(locale_dirs: List[Optional[str]], language: str,
catalog: str = 'sphinx', namespace: str = 'general') -> Tuple[NullTranslations, bool]:
"""Look for message catalogs in `locale_dirs` and *ensure* that there is at
least a NullTranslations catalog set in `translators`. If called multiple
diff --git a/sphinx/locale/ar/LC_MESSAGES/sphinx.po b/sphinx/locale/ar/LC_MESSAGES/sphinx.po
index bd69d2aeb..371395b7b 100644
--- a/sphinx/locale/ar/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/ar/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Mohammed Shannaq <sam@ms.per.jo>, 2018
msgid ""
diff --git a/sphinx/locale/bg/LC_MESSAGES/sphinx.po b/sphinx/locale/bg/LC_MESSAGES/sphinx.po
index 79329a57a..6d36b05f3 100644
--- a/sphinx/locale/bg/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/bg/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
msgid ""
msgstr ""
diff --git a/sphinx/locale/bn/LC_MESSAGES/sphinx.po b/sphinx/locale/bn/LC_MESSAGES/sphinx.po
index 63aae6877..98ecbe5d0 100644
--- a/sphinx/locale/bn/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/bn/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# FIRST AUTHOR <EMAIL@ADDRESS>, 2009
msgid ""
diff --git a/sphinx/locale/ca/LC_MESSAGES/sphinx.po b/sphinx/locale/ca/LC_MESSAGES/sphinx.po
index 6a8e317b1..9cbebc74a 100644
--- a/sphinx/locale/ca/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/ca/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# FIRST AUTHOR <EMAIL@ADDRESS>, 2009
msgid ""
diff --git a/sphinx/locale/cak/LC_MESSAGES/sphinx.po b/sphinx/locale/cak/LC_MESSAGES/sphinx.po
index b729d5f1f..2ea8716e3 100644
--- a/sphinx/locale/cak/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/cak/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Julien Malard <julien.malard@mail.mcgill.ca>, 2019
msgid ""
diff --git a/sphinx/locale/cs/LC_MESSAGES/sphinx.po b/sphinx/locale/cs/LC_MESSAGES/sphinx.po
index 8952713e1..1e7890782 100644
--- a/sphinx/locale/cs/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/cs/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# FIRST AUTHOR <EMAIL@ADDRESS>, 2008
# Vilibald W. <vilibald.wanca@gmail.com>, 2014-2015
diff --git a/sphinx/locale/cy/LC_MESSAGES/sphinx.po b/sphinx/locale/cy/LC_MESSAGES/sphinx.po
index 12f2aecd2..19a4c83d3 100644
--- a/sphinx/locale/cy/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/cy/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# FIRST AUTHOR <EMAIL@ADDRESS>, 2016
# Geraint Palmer <palmer.geraint@googlemail.com>, 2016
diff --git a/sphinx/locale/da/LC_MESSAGES/sphinx.po b/sphinx/locale/da/LC_MESSAGES/sphinx.po
index 317b832d2..47fcbfd6f 100644
--- a/sphinx/locale/da/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/da/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# askhl <asklarsen@gmail.com>, 2010-2011
# Jakob Lykke Andersen <jakob@caput.dk>, 2014,2016
diff --git a/sphinx/locale/de/LC_MESSAGES/sphinx.po b/sphinx/locale/de/LC_MESSAGES/sphinx.po
index 8d5cd4e43..2eaab8597 100644
--- a/sphinx/locale/de/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/de/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Georg Brandl <g.brandl@gmx.net>, 2013-2015
# Jean-François B. <jfbu@free.fr>, 2018
diff --git a/sphinx/locale/el/LC_MESSAGES/sphinx.po b/sphinx/locale/el/LC_MESSAGES/sphinx.po
index 54eb341ec..03f5d01fe 100644
--- a/sphinx/locale/el/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/el/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Stelios Vitalis <liberostelios@gmail.com>, 2015
# tzoumakers tzoumakers <tzoumakersx@gmail.com>, 2019
diff --git a/sphinx/locale/eo/LC_MESSAGES/sphinx.po b/sphinx/locale/eo/LC_MESSAGES/sphinx.po
index 6c94360c6..1af0769f3 100644
--- a/sphinx/locale/eo/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/eo/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Dinu Gherman <gherman@darwin.in-berlin.de>, 2014
msgid ""
diff --git a/sphinx/locale/es/LC_MESSAGES/sphinx.po b/sphinx/locale/es/LC_MESSAGES/sphinx.po
index be149e88a..a01ae6c80 100644
--- a/sphinx/locale/es/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/es/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Edward Villegas-Pulgarin <cosmoscalibur@gmail.com>, 2018
# Edward Villegas-Pulgarin <cosmoscalibur@gmail.com>, 2019
diff --git a/sphinx/locale/et/LC_MESSAGES/sphinx.po b/sphinx/locale/et/LC_MESSAGES/sphinx.po
index a87de4d11..1f40f10c0 100644
--- a/sphinx/locale/et/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/et/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Aivar Annamaa <aivar.annamaa@gmail.com>, 2011
# Ivar Smolin <okul at linux ee>, 2012
diff --git a/sphinx/locale/eu/LC_MESSAGES/sphinx.po b/sphinx/locale/eu/LC_MESSAGES/sphinx.po
index 80dbae5a3..c6f25f8b0 100644
--- a/sphinx/locale/eu/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/eu/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Ales Zabala Alava <shagi@gisa-elkartea.org>, 2011
# Asier Iturralde Sarasola <asier.iturralde@gmail.com>, 2018
diff --git a/sphinx/locale/fa/LC_MESSAGES/sphinx.po b/sphinx/locale/fa/LC_MESSAGES/sphinx.po
index ddca8112c..066cfc4bb 100644
--- a/sphinx/locale/fa/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/fa/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
msgid ""
msgstr ""
diff --git a/sphinx/locale/fi/LC_MESSAGES/sphinx.po b/sphinx/locale/fi/LC_MESSAGES/sphinx.po
index 19233894d..6e18d601a 100644
--- a/sphinx/locale/fi/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/fi/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# FIRST AUTHOR <EMAIL@ADDRESS>, 2009
msgid ""
diff --git a/sphinx/locale/fr/LC_MESSAGES/sphinx.po b/sphinx/locale/fr/LC_MESSAGES/sphinx.po
index b01d956c0..740e9ba70 100644
--- a/sphinx/locale/fr/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/fr/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Christophe CHAUVET <christophe.chauvet@gmail.com>, 2017
# Christophe CHAUVET <christophe.chauvet@gmail.com>, 2013,2015
diff --git a/sphinx/locale/he/LC_MESSAGES/sphinx.po b/sphinx/locale/he/LC_MESSAGES/sphinx.po
index 7332af27f..1f8d45480 100644
--- a/sphinx/locale/he/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/he/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011
msgid ""
diff --git a/sphinx/locale/hi/LC_MESSAGES/sphinx.po b/sphinx/locale/hi/LC_MESSAGES/sphinx.po
index 7fd40316b..5effb078c 100644
--- a/sphinx/locale/hi/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/hi/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Ajay Singh <ajaysajay@gmail.com>, 2019
# Purnank H. Ghumalia <me@purnank.in>, 2015-2016
diff --git a/sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po b/sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po
index 78f66e29e..b1047a51f 100644
--- a/sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
msgid ""
msgstr ""
diff --git a/sphinx/locale/hr/LC_MESSAGES/sphinx.po b/sphinx/locale/hr/LC_MESSAGES/sphinx.po
index c1f1d4be0..20e336487 100644
--- a/sphinx/locale/hr/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/hr/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Mario Šarić, 2015-2020
msgid ""
diff --git a/sphinx/locale/hu/LC_MESSAGES/sphinx.po b/sphinx/locale/hu/LC_MESSAGES/sphinx.po
index fe58d1895..ecf147f1f 100644
--- a/sphinx/locale/hu/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/hu/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011
# Molnár Dénes <denes.molnar2@stud.uni-corvinus.hu>, 2017
diff --git a/sphinx/locale/id/LC_MESSAGES/sphinx.po b/sphinx/locale/id/LC_MESSAGES/sphinx.po
index d11a59d35..d1ff01f34 100644
--- a/sphinx/locale/id/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/id/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Arif Budiman <arifpedia@gmail.com>, 2016-2017
# FIRST AUTHOR <EMAIL@ADDRESS>, 2009
diff --git a/sphinx/locale/it/LC_MESSAGES/sphinx.po b/sphinx/locale/it/LC_MESSAGES/sphinx.po
index 9c871d0d2..c0aeb2e06 100644
--- a/sphinx/locale/it/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/it/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Denis Cappellin <denis@cappell.in>, 2018
# Paolo Cavallini <cavallini@faunalia.it>, 2013-2017
diff --git a/sphinx/locale/ja/LC_MESSAGES/sphinx.po b/sphinx/locale/ja/LC_MESSAGES/sphinx.po
index d9299e58c..1477b3d8a 100644
--- a/sphinx/locale/ja/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/ja/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# shirou - しろう <shirou.faw@gmail.com>, 2013
# Akitoshi Ohta <fire.kuma8@gmail.com>, 2011
diff --git a/sphinx/locale/ko/LC_MESSAGES/sphinx.po b/sphinx/locale/ko/LC_MESSAGES/sphinx.po
index bf3bbe78e..56fff17d1 100644
--- a/sphinx/locale/ko/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/ko/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Minho Ryang <minhoryang@gmail.com>, 2019
# YT H <dev@theYT.net>, 2019
diff --git a/sphinx/locale/lt/LC_MESSAGES/sphinx.po b/sphinx/locale/lt/LC_MESSAGES/sphinx.po
index fbf5b0a1d..4df91eb54 100644
--- a/sphinx/locale/lt/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/lt/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# DALIUS DOBRAVOLSKAS <DALIUS@SANDBOX.LT>, 2010
msgid ""
diff --git a/sphinx/locale/lv/LC_MESSAGES/sphinx.po b/sphinx/locale/lv/LC_MESSAGES/sphinx.po
index a3968756d..ca8ef8240 100644
--- a/sphinx/locale/lv/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/lv/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
msgid ""
msgstr ""
diff --git a/sphinx/locale/mk/LC_MESSAGES/sphinx.po b/sphinx/locale/mk/LC_MESSAGES/sphinx.po
index 5543d45fd..397828d91 100644
--- a/sphinx/locale/mk/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/mk/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Vasil Vangelovski <vvangelovski@gmail.com>, 2013
msgid ""
diff --git a/sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po b/sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po
index b061b0361..05fe2c92c 100644
--- a/sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
msgid ""
msgstr ""
diff --git a/sphinx/locale/ne/LC_MESSAGES/sphinx.po b/sphinx/locale/ne/LC_MESSAGES/sphinx.po
index 46590097d..22e4ef626 100644
--- a/sphinx/locale/ne/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/ne/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011
# Takeshi KOMIYA <i.tkomiya@gmail.com>, 2016
diff --git a/sphinx/locale/nl/LC_MESSAGES/sphinx.po b/sphinx/locale/nl/LC_MESSAGES/sphinx.po
index d5eb16c6f..4bdf9f43b 100644
--- a/sphinx/locale/nl/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/nl/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Bram Geron, 2017
# brechtm, 2016
diff --git a/sphinx/locale/pl/LC_MESSAGES/sphinx.po b/sphinx/locale/pl/LC_MESSAGES/sphinx.po
index ac8488a45..73b7a6478 100644
--- a/sphinx/locale/pl/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/pl/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# m_aciek <maciej.olko@gmail.com>, 2017-2020
# Michael Gielda <michal.gielda@gmail.com>, 2014
diff --git a/sphinx/locale/pt/LC_MESSAGES/sphinx.po b/sphinx/locale/pt/LC_MESSAGES/sphinx.po
index 4b0c30cb7..e08ce7a5d 100644
--- a/sphinx/locale/pt/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/pt/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
msgid ""
msgstr ""
diff --git a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po
index dba58f93e..3e9e2965f 100644
--- a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Claudio Rogerio Carvalho Filho <excriptbrasil@gmail.com>, 2016
# FIRST AUTHOR <roger.demetrescu@gmail.com>, 2008
diff --git a/sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po b/sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po
index 7d219561a..6c78b0813 100644
--- a/sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Pedro Algarvio <pedro@algarvio.me>, 2013
# Takeshi KOMIYA <i.tkomiya@gmail.com>, 2016
diff --git a/sphinx/locale/ro/LC_MESSAGES/sphinx.po b/sphinx/locale/ro/LC_MESSAGES/sphinx.po
index 6a4591643..721f8dcd2 100644
--- a/sphinx/locale/ro/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/ro/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Razvan Stefanescu <razvan.stefanescu@gmail.com>, 2015-2017
# Takeshi KOMIYA <i.tkomiya@gmail.com>, 2016
diff --git a/sphinx/locale/ru/LC_MESSAGES/sphinx.po b/sphinx/locale/ru/LC_MESSAGES/sphinx.po
index e69207f4e..d8572db5f 100644
--- a/sphinx/locale/ru/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/ru/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Alex Salikov <Salikvo57@gmail.com>, 2019
# Dmitry Shachnev <mitya57@gmail.com>, 2013
diff --git a/sphinx/locale/si/LC_MESSAGES/sphinx.po b/sphinx/locale/si/LC_MESSAGES/sphinx.po
index a257711c5..d03be451e 100644
--- a/sphinx/locale/si/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/si/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# callkalpa <callkalpa@gmail.com>, 2013
msgid ""
diff --git a/sphinx/locale/sk/LC_MESSAGES/sphinx.po b/sphinx/locale/sk/LC_MESSAGES/sphinx.po
index 7d4bb2118..07d654695 100644
--- a/sphinx/locale/sk/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/sk/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# FIRST AUTHOR <EMAIL@ADDRESS>, 2008
# Slavko <linux@slavino.sk>, 2013-2019
diff --git a/sphinx/locale/sl/LC_MESSAGES/sphinx.po b/sphinx/locale/sl/LC_MESSAGES/sphinx.po
index 80dde8bd0..6ceca74a0 100644
--- a/sphinx/locale/sl/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/sl/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
msgid ""
msgstr ""
diff --git a/sphinx/locale/sq/LC_MESSAGES/sphinx.po b/sphinx/locale/sq/LC_MESSAGES/sphinx.po
index bf21f378d..fb1b0e26c 100644
--- a/sphinx/locale/sq/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/sq/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
msgid ""
msgstr ""
diff --git a/sphinx/locale/sr/LC_MESSAGES/sphinx.po b/sphinx/locale/sr/LC_MESSAGES/sphinx.po
index 1df55881c..2bcfcf51c 100644
--- a/sphinx/locale/sr/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/sr/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Risto Pejasinovic <risto.pejasinovic@gmail.com>, 2019
msgid ""
diff --git a/sphinx/locale/sr@latin/LC_MESSAGES/sphinx.po b/sphinx/locale/sr@latin/LC_MESSAGES/sphinx.po
index a7b82e633..574d218de 100644
--- a/sphinx/locale/sr@latin/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/sr@latin/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
msgid ""
msgstr ""
diff --git a/sphinx/locale/sr_RS/LC_MESSAGES/sphinx.po b/sphinx/locale/sr_RS/LC_MESSAGES/sphinx.po
index dad4f3542..3e7ac5ba4 100644
--- a/sphinx/locale/sr_RS/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/sr_RS/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
msgid ""
msgstr ""
diff --git a/sphinx/locale/sv/LC_MESSAGES/sphinx.po b/sphinx/locale/sv/LC_MESSAGES/sphinx.po
index bbf5e5ff1..af26ab2f1 100644
--- a/sphinx/locale/sv/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/sv/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
msgid ""
msgstr ""
diff --git a/sphinx/locale/ta/LC_MESSAGES/sphinx.po b/sphinx/locale/ta/LC_MESSAGES/sphinx.po
index bb1c77f28..cc19b6dfa 100644
--- a/sphinx/locale/ta/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/ta/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Julien Malard <julien.malard@mail.mcgill.ca>, 2019
msgid ""
diff --git a/sphinx/locale/te/LC_MESSAGES/sphinx.po b/sphinx/locale/te/LC_MESSAGES/sphinx.po
index c6c3c45f1..32c1fc59e 100644
--- a/sphinx/locale/te/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/te/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
msgid ""
msgstr ""
diff --git a/sphinx/locale/tr/LC_MESSAGES/sphinx.po b/sphinx/locale/tr/LC_MESSAGES/sphinx.po
index 996d6e67c..b37c18d50 100644
--- a/sphinx/locale/tr/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/tr/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# BouRock, 2020
# Fırat Özgül <ozgulfirat@gmail.com>, 2013-2016
diff --git a/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po b/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po
index 2b43faa32..791426dd7 100644
--- a/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Petro Sasnyk <petro@sasnyk.name>, 2009
msgid ""
diff --git a/sphinx/locale/ur/LC_MESSAGES/sphinx.po b/sphinx/locale/ur/LC_MESSAGES/sphinx.po
index c674b9283..c37314be1 100644
--- a/sphinx/locale/ur/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/ur/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
msgid ""
msgstr ""
diff --git a/sphinx/locale/vi/LC_MESSAGES/sphinx.po b/sphinx/locale/vi/LC_MESSAGES/sphinx.po
index 83b579ea4..20c77edeb 100644
--- a/sphinx/locale/vi/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/vi/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Hoat Le Van <hoatlevan@gmail.com>, 2014
msgid ""
diff --git a/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po b/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po
index 47cb76210..5b9491c53 100644
--- a/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Yinian Chin <yinian1992@live.com>, 2015,2017-2018
# Hsiaoming Yang <me@lepture.com>, 2018
diff --git a/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po b/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po
index 67b4d2b81..435fe0453 100644
--- a/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Adrian Liaw <adrianliaw2000@gmail.com>, 2018
# Fred Lin <gasolin@gmail.com>, 2008
diff --git a/sphinx/pycode/ast.py b/sphinx/pycode/ast.py
index 9bafff11c..17d78f4eb 100644
--- a/sphinx/pycode/ast.py
+++ b/sphinx/pycode/ast.py
@@ -58,17 +58,19 @@ def parse(code: str, mode: str = 'exec') -> "ast.AST":
return ast.parse(code, mode=mode)
-def unparse(node: Optional[ast.AST]) -> Optional[str]:
+def unparse(node: Optional[ast.AST], code: str = '') -> Optional[str]:
"""Unparse an AST to string."""
if node is None:
return None
elif isinstance(node, str):
return node
- return _UnparseVisitor().visit(node)
+ return _UnparseVisitor(code).visit(node)
# a greatly cut-down version of `ast._Unparser`
class _UnparseVisitor(ast.NodeVisitor):
+ def __init__(self, code: str = '') -> None:
+ self.code = code
def _visit_op(self, node: ast.AST) -> str:
return OPERATORS[node.__class__]
@@ -166,14 +168,28 @@ class _UnparseVisitor(ast.NodeVisitor):
return "{" + ", ".join(self.visit(e) for e in node.elts) + "}"
def visit_Subscript(self, node: ast.Subscript) -> str:
- return "%s[%s]" % (self.visit(node.value), self.visit(node.slice))
+ def is_simple_tuple(value: ast.AST) -> bool:
+ return (
+ isinstance(value, ast.Tuple) and
+ bool(value.elts) and
+ not any(isinstance(elt, ast.Starred) for elt in value.elts)
+ )
+
+ if is_simple_tuple(node.slice):
+ elts = ", ".join(self.visit(e) for e in node.slice.elts) # type: ignore
+ return "%s[%s]" % (self.visit(node.value), elts)
+ elif isinstance(node.slice, ast.Index) and is_simple_tuple(node.slice.value):
+ elts = ", ".join(self.visit(e) for e in node.slice.value.elts) # type: ignore
+ return "%s[%s]" % (self.visit(node.value), elts)
+ else:
+ return "%s[%s]" % (self.visit(node.value), self.visit(node.slice))
def visit_UnaryOp(self, node: ast.UnaryOp) -> str:
return "%s %s" % (self.visit(node.op), self.visit(node.operand))
def visit_Tuple(self, node: ast.Tuple) -> str:
if node.elts:
- return ", ".join(self.visit(e) for e in node.elts)
+ return "(" + ", ".join(self.visit(e) for e in node.elts) + ")"
else:
return "()"
@@ -181,6 +197,11 @@ class _UnparseVisitor(ast.NodeVisitor):
def visit_Constant(self, node: ast.Constant) -> str:
if node.value is Ellipsis:
return "..."
+ elif isinstance(node.value, (int, float, complex)):
+ if self.code and sys.version_info > (3, 8):
+ return ast.get_source_segment(self.code, node)
+ else:
+ return repr(node.value)
else:
return repr(node.value)
diff --git a/sphinx/registry.py b/sphinx/registry.py
index 0aec0a9fd..d0c00b85f 100644
--- a/sphinx/registry.py
+++ b/sphinx/registry.py
@@ -259,12 +259,12 @@ class SphinxComponentRegistry:
else:
self.source_suffix[suffix] = filetype
- def add_source_parser(self, parser: "Type[Parser]", **kwargs: Any) -> None:
+ def add_source_parser(self, parser: "Type[Parser]", override: bool = False) -> None:
logger.debug('[app] adding search source_parser: %r', parser)
# create a map from filetype to parser
for filetype in parser.supported:
- if filetype in self.source_parsers and not kwargs.get('override'):
+ if filetype in self.source_parsers and not override:
raise ExtensionError(__('source_parser for %r is already registered') %
filetype)
else:
@@ -367,7 +367,14 @@ class SphinxComponentRegistry:
logger.debug('[app] adding js_file: %r, %r', filename, attributes)
self.js_files.append((filename, attributes))
+ def has_latex_package(self, name: str) -> bool:
+ packages = self.latex_packages + self.latex_packages_after_hyperref
+ return bool([x for x in packages if x[0] == name])
+
def add_latex_package(self, name: str, options: str, after_hyperref: bool = False) -> None:
+ if self.has_latex_package(name):
+ logger.warn("latex package '%s' already included" % name)
+
logger.debug('[app] adding latex package: %r', name)
if after_hyperref:
self.latex_packages_after_hyperref.append((name, options))
@@ -394,7 +401,7 @@ class SphinxComponentRegistry:
def load_extension(self, app: "Sphinx", extname: str) -> None:
"""Load a Sphinx extension."""
- if extname in app.extensions: # alread loaded
+ if extname in app.extensions: # already loaded
return
if extname in EXTENSION_BLACKLIST:
logger.warning(__('the extension %r was already merged with Sphinx since '
diff --git a/sphinx/search/non-minified-js/danish-stemmer.js b/sphinx/search/non-minified-js/danish-stemmer.js
index f6309327f..36943d22a 100644
--- a/sphinx/search/non-minified-js/danish-stemmer.js
+++ b/sphinx/search/non-minified-js/danish-stemmer.js
@@ -143,7 +143,7 @@ JSX.resetProfileResults = function () {
return $__jsx_profiler.resetResults();
};
JSX.DEBUG = false;
-var GeneratorFunction$0 =
+var GeneratorFunction$0 =
(function () {
try {
return Function('import {GeneratorFunction} from "std:iteration"; return GeneratorFunction')();
@@ -151,7 +151,7 @@ var GeneratorFunction$0 =
return function GeneratorFunction () {};
}
})();
-var __jsx_generator_object$0 =
+var __jsx_generator_object$0 =
(function () {
function __jsx_generator_object() {
this.__next = 0;
diff --git a/sphinx/search/non-minified-js/dutch-stemmer.js b/sphinx/search/non-minified-js/dutch-stemmer.js
index 15c053a8d..997f1467b 100644
--- a/sphinx/search/non-minified-js/dutch-stemmer.js
+++ b/sphinx/search/non-minified-js/dutch-stemmer.js
@@ -143,7 +143,7 @@ JSX.resetProfileResults = function () {
return $__jsx_profiler.resetResults();
};
JSX.DEBUG = false;
-var GeneratorFunction$0 =
+var GeneratorFunction$0 =
(function () {
try {
return Function('import {GeneratorFunction} from "std:iteration"; return GeneratorFunction')();
@@ -151,7 +151,7 @@ var GeneratorFunction$0 =
return function GeneratorFunction () {};
}
})();
-var __jsx_generator_object$0 =
+var __jsx_generator_object$0 =
(function () {
function __jsx_generator_object() {
this.__next = 0;
diff --git a/sphinx/search/non-minified-js/finnish-stemmer.js b/sphinx/search/non-minified-js/finnish-stemmer.js
index 210c3e13d..5b520c00f 100644
--- a/sphinx/search/non-minified-js/finnish-stemmer.js
+++ b/sphinx/search/non-minified-js/finnish-stemmer.js
@@ -143,7 +143,7 @@ JSX.resetProfileResults = function () {
return $__jsx_profiler.resetResults();
};
JSX.DEBUG = false;
-var GeneratorFunction$0 =
+var GeneratorFunction$0 =
(function () {
try {
return Function('import {GeneratorFunction} from "std:iteration"; return GeneratorFunction')();
@@ -151,7 +151,7 @@ var GeneratorFunction$0 =
return function GeneratorFunction () {};
}
})();
-var __jsx_generator_object$0 =
+var __jsx_generator_object$0 =
(function () {
function __jsx_generator_object() {
this.__next = 0;
diff --git a/sphinx/search/non-minified-js/french-stemmer.js b/sphinx/search/non-minified-js/french-stemmer.js
index 3b3c0607f..75255a03d 100644
--- a/sphinx/search/non-minified-js/french-stemmer.js
+++ b/sphinx/search/non-minified-js/french-stemmer.js
@@ -143,7 +143,7 @@ JSX.resetProfileResults = function () {
return $__jsx_profiler.resetResults();
};
JSX.DEBUG = false;
-var GeneratorFunction$0 =
+var GeneratorFunction$0 =
(function () {
try {
return Function('import {GeneratorFunction} from "std:iteration"; return GeneratorFunction')();
@@ -151,7 +151,7 @@ var GeneratorFunction$0 =
return function GeneratorFunction () {};
}
})();
-var __jsx_generator_object$0 =
+var __jsx_generator_object$0 =
(function () {
function __jsx_generator_object() {
this.__next = 0;
diff --git a/sphinx/search/non-minified-js/german-stemmer.js b/sphinx/search/non-minified-js/german-stemmer.js
index 4f1dc1cf3..a5beb8f3a 100644
--- a/sphinx/search/non-minified-js/german-stemmer.js
+++ b/sphinx/search/non-minified-js/german-stemmer.js
@@ -143,7 +143,7 @@ JSX.resetProfileResults = function () {
return $__jsx_profiler.resetResults();
};
JSX.DEBUG = false;
-var GeneratorFunction$0 =
+var GeneratorFunction$0 =
(function () {
try {
return Function('import {GeneratorFunction} from "std:iteration"; return GeneratorFunction')();
@@ -151,7 +151,7 @@ var GeneratorFunction$0 =
return function GeneratorFunction () {};
}
})();
-var __jsx_generator_object$0 =
+var __jsx_generator_object$0 =
(function () {
function __jsx_generator_object() {
this.__next = 0;
diff --git a/sphinx/search/non-minified-js/hungarian-stemmer.js b/sphinx/search/non-minified-js/hungarian-stemmer.js
index c9a6347c6..67fd1cdf0 100644
--- a/sphinx/search/non-minified-js/hungarian-stemmer.js
+++ b/sphinx/search/non-minified-js/hungarian-stemmer.js
@@ -143,7 +143,7 @@ JSX.resetProfileResults = function () {
return $__jsx_profiler.resetResults();
};
JSX.DEBUG = false;
-var GeneratorFunction$0 =
+var GeneratorFunction$0 =
(function () {
try {
return Function('import {GeneratorFunction} from "std:iteration"; return GeneratorFunction')();
@@ -151,7 +151,7 @@ var GeneratorFunction$0 =
return function GeneratorFunction () {};
}
})();
-var __jsx_generator_object$0 =
+var __jsx_generator_object$0 =
(function () {
function __jsx_generator_object() {
this.__next = 0;
diff --git a/sphinx/search/non-minified-js/italian-stemmer.js b/sphinx/search/non-minified-js/italian-stemmer.js
index ca16ff24c..52daef58f 100644
--- a/sphinx/search/non-minified-js/italian-stemmer.js
+++ b/sphinx/search/non-minified-js/italian-stemmer.js
@@ -143,7 +143,7 @@ JSX.resetProfileResults = function () {
return $__jsx_profiler.resetResults();
};
JSX.DEBUG = false;
-var GeneratorFunction$0 =
+var GeneratorFunction$0 =
(function () {
try {
return Function('import {GeneratorFunction} from "std:iteration"; return GeneratorFunction')();
@@ -151,7 +151,7 @@ var GeneratorFunction$0 =
return function GeneratorFunction () {};
}
})();
-var __jsx_generator_object$0 =
+var __jsx_generator_object$0 =
(function () {
function __jsx_generator_object() {
this.__next = 0;
diff --git a/sphinx/search/non-minified-js/norwegian-stemmer.js b/sphinx/search/non-minified-js/norwegian-stemmer.js
index 38b64c5a8..5c6eba182 100644
--- a/sphinx/search/non-minified-js/norwegian-stemmer.js
+++ b/sphinx/search/non-minified-js/norwegian-stemmer.js
@@ -143,7 +143,7 @@ JSX.resetProfileResults = function () {
return $__jsx_profiler.resetResults();
};
JSX.DEBUG = false;
-var GeneratorFunction$0 =
+var GeneratorFunction$0 =
(function () {
try {
return Function('import {GeneratorFunction} from "std:iteration"; return GeneratorFunction')();
@@ -151,7 +151,7 @@ var GeneratorFunction$0 =
return function GeneratorFunction () {};
}
})();
-var __jsx_generator_object$0 =
+var __jsx_generator_object$0 =
(function () {
function __jsx_generator_object() {
this.__next = 0;
diff --git a/sphinx/search/non-minified-js/porter-stemmer.js b/sphinx/search/non-minified-js/porter-stemmer.js
index 7a0f4558a..d07e7a426 100644
--- a/sphinx/search/non-minified-js/porter-stemmer.js
+++ b/sphinx/search/non-minified-js/porter-stemmer.js
@@ -143,7 +143,7 @@ JSX.resetProfileResults = function () {
return $__jsx_profiler.resetResults();
};
JSX.DEBUG = false;
-var GeneratorFunction$0 =
+var GeneratorFunction$0 =
(function () {
try {
return Function('import {GeneratorFunction} from "std:iteration"; return GeneratorFunction')();
@@ -151,7 +151,7 @@ var GeneratorFunction$0 =
return function GeneratorFunction () {};
}
})();
-var __jsx_generator_object$0 =
+var __jsx_generator_object$0 =
(function () {
function __jsx_generator_object() {
this.__next = 0;
diff --git a/sphinx/search/non-minified-js/portuguese-stemmer.js b/sphinx/search/non-minified-js/portuguese-stemmer.js
index 35f21aa8f..4042c0aef 100644
--- a/sphinx/search/non-minified-js/portuguese-stemmer.js
+++ b/sphinx/search/non-minified-js/portuguese-stemmer.js
@@ -143,7 +143,7 @@ JSX.resetProfileResults = function () {
return $__jsx_profiler.resetResults();
};
JSX.DEBUG = false;
-var GeneratorFunction$0 =
+var GeneratorFunction$0 =
(function () {
try {
return Function('import {GeneratorFunction} from "std:iteration"; return GeneratorFunction')();
@@ -151,7 +151,7 @@ var GeneratorFunction$0 =
return function GeneratorFunction () {};
}
})();
-var __jsx_generator_object$0 =
+var __jsx_generator_object$0 =
(function () {
function __jsx_generator_object() {
this.__next = 0;
diff --git a/sphinx/search/non-minified-js/romanian-stemmer.js b/sphinx/search/non-minified-js/romanian-stemmer.js
index f71f44a68..545d3ee2e 100644
--- a/sphinx/search/non-minified-js/romanian-stemmer.js
+++ b/sphinx/search/non-minified-js/romanian-stemmer.js
@@ -143,7 +143,7 @@ JSX.resetProfileResults = function () {
return $__jsx_profiler.resetResults();
};
JSX.DEBUG = false;
-var GeneratorFunction$0 =
+var GeneratorFunction$0 =
(function () {
try {
return Function('import {GeneratorFunction} from "std:iteration"; return GeneratorFunction')();
@@ -151,7 +151,7 @@ var GeneratorFunction$0 =
return function GeneratorFunction () {};
}
})();
-var __jsx_generator_object$0 =
+var __jsx_generator_object$0 =
(function () {
function __jsx_generator_object() {
this.__next = 0;
diff --git a/sphinx/search/non-minified-js/russian-stemmer.js b/sphinx/search/non-minified-js/russian-stemmer.js
index 74d630968..87f4844e0 100644
--- a/sphinx/search/non-minified-js/russian-stemmer.js
+++ b/sphinx/search/non-minified-js/russian-stemmer.js
@@ -143,7 +143,7 @@ JSX.resetProfileResults = function () {
return $__jsx_profiler.resetResults();
};
JSX.DEBUG = false;
-var GeneratorFunction$0 =
+var GeneratorFunction$0 =
(function () {
try {
return Function('import {GeneratorFunction} from "std:iteration"; return GeneratorFunction')();
@@ -151,7 +151,7 @@ var GeneratorFunction$0 =
return function GeneratorFunction () {};
}
})();
-var __jsx_generator_object$0 =
+var __jsx_generator_object$0 =
(function () {
function __jsx_generator_object() {
this.__next = 0;
diff --git a/sphinx/search/non-minified-js/spanish-stemmer.js b/sphinx/search/non-minified-js/spanish-stemmer.js
index 21b648fa8..6c5d2da91 100644
--- a/sphinx/search/non-minified-js/spanish-stemmer.js
+++ b/sphinx/search/non-minified-js/spanish-stemmer.js
@@ -143,7 +143,7 @@ JSX.resetProfileResults = function () {
return $__jsx_profiler.resetResults();
};
JSX.DEBUG = false;
-var GeneratorFunction$0 =
+var GeneratorFunction$0 =
(function () {
try {
return Function('import {GeneratorFunction} from "std:iteration"; return GeneratorFunction')();
@@ -151,7 +151,7 @@ var GeneratorFunction$0 =
return function GeneratorFunction () {};
}
})();
-var __jsx_generator_object$0 =
+var __jsx_generator_object$0 =
(function () {
function __jsx_generator_object() {
this.__next = 0;
diff --git a/sphinx/search/non-minified-js/swedish-stemmer.js b/sphinx/search/non-minified-js/swedish-stemmer.js
index fd2a58f0a..1d8aba8f4 100644
--- a/sphinx/search/non-minified-js/swedish-stemmer.js
+++ b/sphinx/search/non-minified-js/swedish-stemmer.js
@@ -143,7 +143,7 @@ JSX.resetProfileResults = function () {
return $__jsx_profiler.resetResults();
};
JSX.DEBUG = false;
-var GeneratorFunction$0 =
+var GeneratorFunction$0 =
(function () {
try {
return Function('import {GeneratorFunction} from "std:iteration"; return GeneratorFunction')();
@@ -151,7 +151,7 @@ var GeneratorFunction$0 =
return function GeneratorFunction () {};
}
})();
-var __jsx_generator_object$0 =
+var __jsx_generator_object$0 =
(function () {
function __jsx_generator_object() {
this.__next = 0;
diff --git a/sphinx/search/non-minified-js/turkish-stemmer.js b/sphinx/search/non-minified-js/turkish-stemmer.js
index f8f088576..22bd7538d 100644
--- a/sphinx/search/non-minified-js/turkish-stemmer.js
+++ b/sphinx/search/non-minified-js/turkish-stemmer.js
@@ -143,7 +143,7 @@ JSX.resetProfileResults = function () {
return $__jsx_profiler.resetResults();
};
JSX.DEBUG = false;
-var GeneratorFunction$0 =
+var GeneratorFunction$0 =
(function () {
try {
return Function('import {GeneratorFunction} from "std:iteration"; return GeneratorFunction')();
@@ -151,7 +151,7 @@ var GeneratorFunction$0 =
return function GeneratorFunction () {};
}
})();
-var __jsx_generator_object$0 =
+var __jsx_generator_object$0 =
(function () {
function __jsx_generator_object() {
this.__next = 0;
diff --git a/sphinx/testing/util.py b/sphinx/testing/util.py
index 5ac334068..c0dd1feac 100644
--- a/sphinx/testing/util.py
+++ b/sphinx/testing/util.py
@@ -21,7 +21,6 @@ from docutils.nodes import Node
from docutils.parsers.rst import directives, roles
from sphinx import application, locale
-from sphinx.builders.latex import LaTeXBuilder
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.pycode import ModuleAnalyzer
from sphinx.testing.path import path
@@ -109,7 +108,7 @@ class SphinxTestApp(application.Sphinx):
def __init__(self, buildername: str = 'html', srcdir: path = None, freshenv: bool = False,
confoverrides: Dict = None, status: IO = None, warning: IO = None,
- tags: List[str] = None, docutilsconf: str = None) -> None:
+ tags: List[str] = None, docutilsconf: str = None, parallel: int = 0) -> None:
if docutilsconf is not None:
(srcdir / 'docutils.conf').write_text(docutilsconf)
@@ -134,14 +133,13 @@ class SphinxTestApp(application.Sphinx):
try:
super().__init__(srcdir, confdir, outdir, doctreedir,
buildername, confoverrides, status, warning,
- freshenv, warningiserror, tags)
+ freshenv, warningiserror, tags, parallel=parallel)
except Exception:
self.cleanup()
raise
def cleanup(self, doctrees: bool = False) -> None:
ModuleAnalyzer.cache.clear()
- LaTeXBuilder.usepackages = []
locale.translators.clear()
sys.path[:] = self._saved_path
sys.modules.pop('autodoc_fodder', None)
diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty
index a3e91ad34..2b83ab85b 100644
--- a/sphinx/texinputs/sphinx.sty
+++ b/sphinx/texinputs/sphinx.sty
@@ -1823,7 +1823,7 @@
% fix a space-gobbling issue due to LaTeX's original \do@noligs
% TODO: using \@noligs as patched by upquote.sty is now unneeded because
% either ` and ' are escaped (non-unicode engines) or they don't build
-% ligatures (unicode engines). Thus remove this and unify handling of `, <, >,
+% ligatures (unicode engines). Thus remove this and unify handling of `, <, >,
% ' and - with the characters . , ; ? ! / as handled via
% \sphinxbreaksviaactive.
% Hence \sphinx@do@noligs will be removed, or rather replaced with code
@@ -1905,7 +1905,7 @@
% Special characters
%
% This definition prevents en-dash and em-dash TeX ligatures.
-%
+%
% It inserts a potential breakpoint after the hyphen. This is to keep in sync
% with behavior in code-blocks, parsed and inline literals. For a breakpoint
% before the hyphen use \leavevmode\kern\z@- (within \makeatletter/\makeatother)
diff --git a/sphinx/texinputs/sphinxcyrillic.sty b/sphinx/texinputs/sphinxcyrillic.sty
index 482b4e3f7..6747b5ec6 100644
--- a/sphinx/texinputs/sphinxcyrillic.sty
+++ b/sphinx/texinputs/sphinxcyrillic.sty
@@ -15,7 +15,7 @@
% https://tex.stackexchange.com/a/460325/
% 159 Cyrillic glyphs as available in X2 TeX 8bit font encoding
% This assumes inputenc loaded with utf8 option, or LaTeX release
-% as recent as 2018/04/01 which does it automatically.
+% as recent as 2018/04/01 which does it automatically.
\@tfor\next:=%
{Ё}{Ђ}{Є}{Ѕ}{І}{Ј}{Љ}{Њ}{Ћ}{Ў}{Џ}{А}{Б}{В}{Г}{Д}{Е}{Ж}{З}{И}{Й}%
{К}{Л}{М}{Н}{О}{П}{Р}{С}{Т}{У}{Ф}{Х}{Ц}{Ч}{Ш}{Щ}{Ъ}{Ы}{Ь}{Э}{Ю}%
diff --git a/sphinx/texinputs/sphinxhowto.cls b/sphinx/texinputs/sphinxhowto.cls
index 57d73cebf..0848a79fd 100644
--- a/sphinx/texinputs/sphinxhowto.cls
+++ b/sphinx/texinputs/sphinxhowto.cls
@@ -76,7 +76,7 @@
\endgroup
\noindent\rule{\textwidth}{1pt}\par
\vspace{12pt}%
-}
+}
\newcommand\sphinxtableofcontentshook{}
\pagenumbering{arabic}
diff --git a/sphinx/themes/nature/static/nature.css_t b/sphinx/themes/nature/static/nature.css_t
index 34893b86a..7df474a91 100644
--- a/sphinx/themes/nature/static/nature.css_t
+++ b/sphinx/themes/nature/static/nature.css_t
@@ -8,11 +8,11 @@
* :license: BSD, see LICENSE for details.
*
*/
-
+
@import url("basic.css");
-
+
/* -- page layout ----------------------------------------------------------- */
-
+
body {
font-family: Arial, sans-serif;
font-size: 100%;
@@ -34,18 +34,18 @@ div.bodywrapper {
hr {
border: 1px solid #B1B4B6;
}
-
+
div.document {
background-color: #eee;
}
-
+
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 30px 30px;
font-size: 0.9em;
}
-
+
div.footer {
color: #555;
width: 100%;
@@ -53,12 +53,12 @@ div.footer {
text-align: center;
font-size: 75%;
}
-
+
div.footer a {
color: #444;
text-decoration: underline;
}
-
+
div.related {
background-color: #6BA81E;
line-height: 32px;
@@ -66,11 +66,11 @@ div.related {
text-shadow: 0px 1px 0 #444;
font-size: 0.9em;
}
-
+
div.related a {
color: #E2F3CC;
}
-
+
div.sphinxsidebar {
font-size: 0.75em;
line-height: 1.5em;
@@ -79,7 +79,7 @@ div.sphinxsidebar {
div.sphinxsidebarwrapper{
padding: 20px 0;
}
-
+
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: Arial, sans-serif;
@@ -95,30 +95,30 @@ div.sphinxsidebar h4 {
div.sphinxsidebar h4{
font-size: 1.1em;
}
-
+
div.sphinxsidebar h3 a {
color: #444;
}
-
-
+
+
div.sphinxsidebar p {
color: #888;
padding: 5px 20px;
}
-
+
div.sphinxsidebar p.topless {
}
-
+
div.sphinxsidebar ul {
margin: 10px 20px;
padding: 0;
color: #000;
}
-
+
div.sphinxsidebar a {
color: #444;
}
-
+
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: sans-serif;
@@ -131,17 +131,17 @@ div.sphinxsidebar .searchformwrapper {
}
/* -- body styles ----------------------------------------------------------- */
-
+
a {
color: #005B81;
text-decoration: none;
}
-
+
a:hover {
color: #E32E00;
text-decoration: underline;
}
-
+
div.body h1,
div.body h2,
div.body h3,
@@ -156,30 +156,30 @@ div.body h6 {
padding: 5px 0 5px 10px;
text-shadow: 0px 1px 0 white
}
-
+
div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; }
div.body h2 { font-size: 150%; background-color: #C8D5E3; }
div.body h3 { font-size: 120%; background-color: #D8DEE3; }
div.body h4 { font-size: 110%; background-color: #D8DEE3; }
div.body h5 { font-size: 100%; background-color: #D8DEE3; }
div.body h6 { font-size: 100%; background-color: #D8DEE3; }
-
+
a.headerlink {
color: #c60f0f;
font-size: 0.8em;
padding: 0 4px 0 4px;
text-decoration: none;
}
-
+
a.headerlink:hover {
background-color: #c60f0f;
color: white;
}
-
+
div.body p, div.body dd, div.body li {
line-height: 1.5em;
}
-
+
div.admonition p.admonition-title + p {
display: inline;
}
@@ -188,29 +188,29 @@ div.note {
background-color: #eee;
border: 1px solid #ccc;
}
-
+
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
-
+
div.topic {
background-color: #eee;
}
-
+
div.warning {
background-color: #ffe4e4;
border: 1px solid #f66;
}
-
+
p.admonition-title {
display: inline;
}
-
+
p.admonition-title:after {
content: ":";
}
-
+
pre {
padding: 10px;
line-height: 1.2em;
@@ -220,7 +220,7 @@ pre {
-webkit-box-shadow: 1px 1px 1px #d8d8d8;
-moz-box-shadow: 1px 1px 1px #d8d8d8;
}
-
+
code {
background-color: #ecf0f3;
color: #222;
diff --git a/sphinx/themes/pyramid/static/pyramid.css_t b/sphinx/themes/pyramid/static/pyramid.css_t
index dafd898d5..0bab3d1a2 100644
--- a/sphinx/themes/pyramid/static/pyramid.css_t
+++ b/sphinx/themes/pyramid/static/pyramid.css_t
@@ -8,11 +8,11 @@
* :license: BSD, see LICENSE for details.
*
*/
-
+
@import url("basic.css");
-
+
/* -- page layout ----------------------------------------------------------- */
-
+
body {
font-family: "Nobile", sans-serif;
font-size: 100%;
@@ -34,7 +34,7 @@ div.bodywrapper {
hr {
border: 1px solid #B1B4B6;
}
-
+
div.document {
background-color: #eee;
}
@@ -59,7 +59,7 @@ div.body {
border-right-style: none;
overflow: auto;
}
-
+
div.footer {
color: #ffffff;
width: 100%;
@@ -69,7 +69,7 @@ div.footer {
background: transparent;
clear:both;
}
-
+
div.footer a {
color: #ffffff;
text-decoration: none;
@@ -79,14 +79,14 @@ div.footer a:hover {
color: #e88f00;
text-decoration: underline;
}
-
+
div.related {
line-height: 30px;
color: #373839;
font-size: 0.8em;
background-color: #eee;
}
-
+
div.related a {
color: #1b61d6;
}
@@ -94,7 +94,7 @@ div.related a {
div.related ul {
padding-left: calc({{ theme_sidebarwidth|todim }} + 10px);
}
-
+
div.sphinxsidebar {
font-size: 0.75em;
line-height: 1.5em;
@@ -103,7 +103,7 @@ div.sphinxsidebar {
div.sphinxsidebarwrapper{
padding: 10px 0;
}
-
+
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: "Neuton", sans-serif;
@@ -118,30 +118,30 @@ div.sphinxsidebar h4 {
div.sphinxsidebar h4{
font-size: 1.3em;
}
-
+
div.sphinxsidebar h3 a {
color: #000000;
}
-
-
+
+
div.sphinxsidebar p {
color: #888;
padding: 5px 20px;
}
-
+
div.sphinxsidebar p.topless {
}
-
+
div.sphinxsidebar ul {
margin: 10px 20px;
padding: 0;
color: #373839;
}
-
+
div.sphinxsidebar a {
color: #444;
}
-
+
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: sans-serif;
@@ -171,16 +171,16 @@ p.sidebar-title {
}
/* -- body styles ----------------------------------------------------------- */
-
+
a, a .pre {
color: #1b61d6;
text-decoration: none;
}
-
+
a:hover, a:hover .pre {
text-decoration: underline;
}
-
+
div.body h1,
div.body h2,
div.body h3,
@@ -194,29 +194,29 @@ div.body h6 {
margin: 30px 0px 10px 0px;
padding: 5px 0;
}
-
+
div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; }
div.body h2 { font-size: 150%; background-color: #ffffff; }
div.body h3 { font-size: 120%; background-color: #ffffff; }
div.body h4 { font-size: 110%; background-color: #ffffff; }
div.body h5 { font-size: 100%; background-color: #ffffff; }
div.body h6 { font-size: 100%; background-color: #ffffff; }
-
+
a.headerlink {
color: #1b61d6;
font-size: 0.8em;
padding: 0 4px 0 4px;
text-decoration: none;
}
-
+
a.headerlink:hover {
text-decoration: underline;
}
-
+
div.body p, div.body dd, div.body li {
line-height: 1.5em;
}
-
+
div.admonition p.admonition-title + p {
display: inline;
}
@@ -236,7 +236,7 @@ div.note {
padding: 10px 20px 10px 60px;
background: #e1ecfe url(dialog-note.png) no-repeat 10px 8px;
}
-
+
div.seealso {
background: #fff6bf url(dialog-seealso.png) no-repeat 10px 8px;
border: 2px solid #ffd324;
@@ -244,7 +244,7 @@ div.seealso {
border-right-style: none;
padding: 10px 20px 10px 60px;
}
-
+
div.topic {
background: #eeeeee;
border: 2px solid #C6C9CB;
@@ -252,7 +252,7 @@ div.topic {
border-right-style: none;
border-left-style: none;
}
-
+
div.warning {
background: #fbe3e4 url(dialog-warning.png) no-repeat 10px 8px;
border: 2px solid #fbc2c4;
@@ -268,18 +268,18 @@ div.admonition-todo {
border-left-style: none;
padding: 10px 20px 10px 60px;
}
-
+
div.note p.admonition-title,
div.warning p.admonition-title,
div.seealso p.admonition-title,
div.admonition-todo p.admonition-title {
display: none;
}
-
+
p.admonition-title:after {
content: ":";
}
-
+
pre {
padding: 10px;
line-height: 1.2em;
@@ -289,7 +289,7 @@ pre {
border-right-style: none;
border-left-style: none;
}
-
+
code {
background-color: transparent;
color: #222;
diff --git a/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t b/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t
index f9961ae36..8eca63278 100644
--- a/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t
+++ b/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t
@@ -138,7 +138,7 @@ div.footer a {
/* -- body styles ----------------------------------------------------------- */
-p {
+p {
margin: 0.8em 0 0.5em 0;
}
diff --git a/sphinx/transforms/post_transforms/images.py b/sphinx/transforms/post_transforms/images.py
index f2cacac3c..949c09dde 100644
--- a/sphinx/transforms/post_transforms/images.py
+++ b/sphinx/transforms/post_transforms/images.py
@@ -11,7 +11,7 @@
import os
import re
from math import ceil
-from typing import Any, Dict, List, Tuple
+from typing import Any, Dict, List, Optional, Tuple
from docutils import nodes
@@ -175,6 +175,13 @@ class ImageConverter(BaseImageConverter):
"""
default_priority = 200
+ #: The converter is available or not. Will be filled at the first call of
+ #: the build. The result is shared in the same process.
+ #:
+ #: .. todo:: This should be refactored not to store the state without class
+ #: variable.
+ available = None # type: Optional[bool]
+
#: A conversion rules the image converter supports.
#: It is represented as a list of pair of source image format (mimetype) and
#: destination one::
@@ -187,16 +194,14 @@ class ImageConverter(BaseImageConverter):
conversion_rules = [] # type: List[Tuple[str, str]]
def __init__(self, *args: Any, **kwargs: Any) -> None:
- self.available = None # type: bool
- # the converter is available or not.
- # Will be checked at first conversion
super().__init__(*args, **kwargs)
def match(self, node: nodes.image) -> bool:
if not self.app.builder.supported_image_types:
return False
elif self.available is None:
- self.available = self.is_available()
+ # store the value to the class variable to share it during the build
+ self.__class__.available = self.is_available()
if not self.available:
return False
diff --git a/sphinx/util/fileutil.py b/sphinx/util/fileutil.py
index d8e896d48..eec1ae463 100644
--- a/sphinx/util/fileutil.py
+++ b/sphinx/util/fileutil.py
@@ -10,7 +10,7 @@
import os
import posixpath
-from typing import Dict
+from typing import Callable, Dict
from docutils.utils import relative_path
@@ -56,7 +56,8 @@ def copy_asset_file(source: str, destination: str,
def copy_asset(source: str, destination: str, excluded: PathMatcher = lambda path: False,
- context: Dict = None, renderer: "BaseRenderer" = None) -> None:
+ context: Dict = None, renderer: "BaseRenderer" = None,
+ onerror: Callable[[str, Exception], None] = None) -> None:
"""Copy asset files to destination recursively.
On copying, it expands the template variables if context argument is given and
@@ -67,6 +68,7 @@ def copy_asset(source: str, destination: str, excluded: PathMatcher = lambda pat
:param excluded: The matcher to determine the given path should be copied or not
:param context: The template variables. If not given, template files are simply copied
:param renderer: The template engine. If not given, SphinxRenderer is used by default
+ :param onerror: The error handler.
"""
if not os.path.exists(source):
return
@@ -90,6 +92,12 @@ def copy_asset(source: str, destination: str, excluded: PathMatcher = lambda pat
for filename in files:
if not excluded(posixpath.join(reldir, filename)):
- copy_asset_file(posixpath.join(root, filename),
- posixpath.join(destination, reldir),
- context, renderer)
+ try:
+ copy_asset_file(posixpath.join(root, filename),
+ posixpath.join(destination, reldir),
+ context, renderer)
+ except Exception as exc:
+ if onerror:
+ onerror(posixpath.join(root, filename), exc)
+ else:
+ raise
diff --git a/sphinx/util/i18n.py b/sphinx/util/i18n.py
index 2177fc7b7..41407f4e1 100644
--- a/sphinx/util/i18n.py
+++ b/sphinx/util/i18n.py
@@ -14,7 +14,7 @@ import warnings
from collections import namedtuple
from datetime import datetime, timezone
from os import path
-from typing import Callable, Generator, List, Set, Tuple
+from typing import Callable, Generator, List, Set, Tuple, Union
import babel.dates
from babel.messages.mofile import write_mo
@@ -128,8 +128,10 @@ def find_catalog(docname: str, compaction: bool) -> str:
return ret
-def docname_to_domain(docname: str, compation: bool) -> str:
+def docname_to_domain(docname: str, compation: Union[bool, str]) -> str:
"""Convert docname to domain for catalogs."""
+ if isinstance(compation, str):
+ return compation
if compation:
return docname.split(SEP, 1)[0]
else:
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index a5c64f882..f2cd8070b 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -304,6 +304,11 @@ def iscoroutinefunction(obj: Any) -> bool:
def isproperty(obj: Any) -> bool:
"""Check if the object is property."""
+ if sys.version_info > (3, 8):
+ from functools import cached_property # cached_property is available since py3.8
+ if isinstance(obj, cached_property):
+ return True
+
return isinstance(obj, property)
@@ -434,8 +439,8 @@ def _should_unwrap(subject: Callable) -> bool:
return False
-def signature(subject: Callable, bound_method: bool = False, follow_wrapped: bool = False
- ) -> inspect.Signature:
+def signature(subject: Callable, bound_method: bool = False, follow_wrapped: bool = False,
+ type_aliases: Dict = {}) -> inspect.Signature:
"""Return a Signature object for the given *subject*.
:param bound_method: Specify *subject* is a bound method or not
@@ -465,7 +470,7 @@ def signature(subject: Callable, bound_method: bool = False, follow_wrapped: boo
try:
# Update unresolved annotations using ``get_type_hints()``.
- annotations = typing.get_type_hints(subject)
+ annotations = typing.get_type_hints(subject, None, type_aliases)
for i, param in enumerate(parameters):
if isinstance(param.annotation, str) and param.name in annotations:
parameters[i] = param.replace(annotation=annotations[param.name])
@@ -595,13 +600,14 @@ def stringify_signature(sig: inspect.Signature, show_annotation: bool = True,
def signature_from_str(signature: str) -> inspect.Signature:
"""Create a Signature object from string."""
- module = ast.parse('def func' + signature + ': pass')
+ code = 'def func' + signature + ': pass'
+ module = ast.parse(code)
function = cast(ast.FunctionDef, module.body[0]) # type: ignore
- return signature_from_ast(function)
+ return signature_from_ast(function, code)
-def signature_from_ast(node: ast.FunctionDef) -> inspect.Signature:
+def signature_from_ast(node: ast.FunctionDef, code: str = '') -> inspect.Signature:
"""Create a Signature object from AST *node*."""
args = node.args
defaults = list(args.defaults)
@@ -621,9 +627,9 @@ def signature_from_ast(node: ast.FunctionDef) -> inspect.Signature:
if defaults[i] is Parameter.empty:
default = Parameter.empty
else:
- default = ast_unparse(defaults[i])
+ default = ast_unparse(defaults[i], code)
- annotation = ast_unparse(arg.annotation) or Parameter.empty
+ annotation = ast_unparse(arg.annotation, code) or Parameter.empty
params.append(Parameter(arg.arg, Parameter.POSITIONAL_ONLY,
default=default, annotation=annotation))
@@ -631,29 +637,29 @@ def signature_from_ast(node: ast.FunctionDef) -> inspect.Signature:
if defaults[i + posonlyargs] is Parameter.empty:
default = Parameter.empty
else:
- default = ast_unparse(defaults[i + posonlyargs])
+ default = ast_unparse(defaults[i + posonlyargs], code)
- annotation = ast_unparse(arg.annotation) or Parameter.empty
+ annotation = ast_unparse(arg.annotation, code) or Parameter.empty
params.append(Parameter(arg.arg, Parameter.POSITIONAL_OR_KEYWORD,
default=default, annotation=annotation))
if args.vararg:
- annotation = ast_unparse(args.vararg.annotation) or Parameter.empty
+ annotation = ast_unparse(args.vararg.annotation, code) or Parameter.empty
params.append(Parameter(args.vararg.arg, Parameter.VAR_POSITIONAL,
annotation=annotation))
for i, arg in enumerate(args.kwonlyargs):
- default = ast_unparse(args.kw_defaults[i]) or Parameter.empty
- annotation = ast_unparse(arg.annotation) or Parameter.empty
+ default = ast_unparse(args.kw_defaults[i], code) or Parameter.empty
+ annotation = ast_unparse(arg.annotation, code) or Parameter.empty
params.append(Parameter(arg.arg, Parameter.KEYWORD_ONLY, default=default,
annotation=annotation))
if args.kwarg:
- annotation = ast_unparse(args.kwarg.annotation) or Parameter.empty
+ annotation = ast_unparse(args.kwarg.annotation, code) or Parameter.empty
params.append(Parameter(args.kwarg.arg, Parameter.VAR_KEYWORD,
annotation=annotation))
- return_annotation = ast_unparse(node.returns) or Parameter.empty
+ return_annotation = ast_unparse(node.returns, code) or Parameter.empty
return inspect.Signature(params, return_annotation=return_annotation)
diff --git a/sphinx/util/inventory.py b/sphinx/util/inventory.py
index 9b647ccac..1e3572323 100644
--- a/sphinx/util/inventory.py
+++ b/sphinx/util/inventory.py
@@ -122,11 +122,16 @@ class InventoryFile:
for line in stream.read_compressed_lines():
# be careful to handle names with embedded spaces correctly
- m = re.match(r'(?x)(.+?)\s+(\S*:\S*)\s+(-?\d+)\s+?(\S*)\s+(.*)',
+ m = re.match(r'(?x)(.+?)\s+(\S+)\s+(-?\d+)\s+?(\S*)\s+(.*)',
line.rstrip())
if not m:
continue
name, type, prio, location, dispname = m.groups()
+ if ':' not in type:
+ # wrong type value. type should be in the form of "{domain}:{objtype}"
+ #
+ # Note: To avoid the regex DoS, this is implemented in python (refs: #8175)
+ continue
if type == 'py:module' and type in invdata and name in invdata[type]:
# due to a bug in 1.1 and below,
# two inventory entries are created
diff --git a/sphinx/util/template.py b/sphinx/util/template.py
index 18047d687..8785928a9 100644
--- a/sphinx/util/template.py
+++ b/sphinx/util/template.py
@@ -84,6 +84,8 @@ class LaTeXRenderer(SphinxRenderer):
self.env.variable_end_string = '%>'
self.env.block_start_string = '<%'
self.env.block_end_string = '%>'
+ self.env.comment_start_string = '<#'
+ self.env.comment_end_string = '#>'
class ReSTRenderer(SphinxRenderer):
diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py
index 23812db96..76438889b 100644
--- a/sphinx/util/typing.py
+++ b/sphinx/util/typing.py
@@ -63,7 +63,11 @@ def is_system_TypeVar(typ: Any) -> bool:
def stringify(annotation: Any) -> str:
"""Stringify type annotation object."""
if isinstance(annotation, str):
- return annotation
+ if annotation.startswith("'") and annotation.endswith("'"):
+ # might be a double Forward-ref'ed type. Go unquoting.
+ return annotation[1:-2]
+ else:
+ return annotation
elif isinstance(annotation, TypeVar):
return annotation.__name__
elif not annotation:
@@ -105,7 +109,10 @@ def _stringify_py37(annotation: Any) -> str:
return repr(annotation)
if getattr(annotation, '__args__', None):
- if qualname == 'Union':
+ if not isinstance(annotation.__args__, (list, tuple)):
+ # broken __args__ found
+ pass
+ elif qualname == 'Union':
if len(annotation.__args__) > 1 and annotation.__args__[-1] is NoneType:
if len(annotation.__args__) > 2:
args = ', '.join(stringify(a) for a in annotation.__args__[:-1])
diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py
index 4375bf326..8813c2d12 100644
--- a/sphinx/writers/html.py
+++ b/sphinx/writers/html.py
@@ -450,7 +450,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
highlighted = self.highlighter.highlight_block(
node.rawsource, lang, opts=opts, linenos=linenos,
- location=(self.builder.current_docname, node.line), **highlight_args
+ location=node, **highlight_args
)
starttag = self.starttag(node, 'div', suffix='',
CLASS='highlight-%s notranslate' % lang)
diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py
index c1252f4d0..4ceeaafdf 100644
--- a/sphinx/writers/html5.py
+++ b/sphinx/writers/html5.py
@@ -402,7 +402,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
highlighted = self.highlighter.highlight_block(
node.rawsource, lang, opts=opts, linenos=linenos,
- location=(self.builder.current_docname, node.line), **highlight_args
+ location=node, **highlight_args
)
starttag = self.starttag(node, 'div', suffix='',
CLASS='highlight-%s notranslate' % lang)
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index 73a22a76b..b77202951 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -651,7 +651,7 @@ class LaTeXTranslator(SphinxTranslator):
if len(node.children) != 1 and not isinstance(node.children[0],
nodes.Text):
logger.warning(__('document title is not a single Text node'),
- location=(self.curfilestack[-1], node.line))
+ location=node)
if not self.elements['title']:
# text needs to be escaped since it is inserted into
# the output literally
@@ -684,7 +684,7 @@ class LaTeXTranslator(SphinxTranslator):
else:
logger.warning(__('encountered title node not in section, topic, table, '
'admonition or sidebar'),
- location=(self.curfilestack[-1], node.line or ''))
+ location=node)
self.body.append('\\sphinxstyleothertitle{')
self.context.append('}\n')
self.in_title = 1
@@ -1762,7 +1762,7 @@ class LaTeXTranslator(SphinxTranslator):
hlcode = self.highlighter.highlight_block(
node.rawsource, lang, opts=opts, linenos=linenos,
- location=(self.curfilestack[-1], node.line), **highlight_args
+ location=node, **highlight_args
)
if self.in_footnote:
self.body.append('\n\\sphinxSetupCodeBlockInFootnote')
diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py
index 8c52a0868..5ad2831dd 100644
--- a/sphinx/writers/texinfo.py
+++ b/sphinx/writers/texinfo.py
@@ -628,7 +628,7 @@ class TexinfoTranslator(SphinxTranslator):
elif not isinstance(parent, nodes.section):
logger.warning(__('encountered title node not in section, topic, table, '
'admonition or sidebar'),
- location=(self.curfilestack[-1], node.line))
+ location=node)
self.visit_rubric(node)
else:
try:
@@ -1186,7 +1186,7 @@ class TexinfoTranslator(SphinxTranslator):
self.body.append('\n@caption{')
else:
logger.warning(__('caption not inside a figure.'),
- location=(self.curfilestack[-1], node.line))
+ location=node)
def depart_caption(self, node: Element) -> None:
if (isinstance(node.parent, nodes.figure) or
@@ -1262,11 +1262,11 @@ class TexinfoTranslator(SphinxTranslator):
def unimplemented_visit(self, node: Element) -> None:
logger.warning(__("unimplemented node type: %r"), node,
- location=(self.curfilestack[-1], node.line))
+ location=node)
def unknown_visit(self, node: Node) -> None:
logger.warning(__("unknown node type: %r"), node,
- location=(self.curfilestack[-1], node.line))
+ location=node)
def unknown_departure(self, node: Node) -> None:
pass
diff --git a/tests/roots/test-circular/conf.py b/tests/roots/test-circular/conf.py
index 027d21cda..a45d22e28 100644
--- a/tests/roots/test-circular/conf.py
+++ b/tests/roots/test-circular/conf.py
@@ -1 +1 @@
-exclude_patterns = ['_build']
+exclude_patterns = ['_build']
diff --git a/tests/roots/test-ext-autodoc/target/annotations.py b/tests/roots/test-ext-autodoc/target/annotations.py
new file mode 100644
index 000000000..667149b26
--- /dev/null
+++ b/tests/roots/test-ext-autodoc/target/annotations.py
@@ -0,0 +1,25 @@
+from __future__ import annotations
+from typing import overload
+
+
+myint = int
+
+
+def sum(x: myint, y: myint) -> myint:
+ """docstring"""
+ return x + y
+
+
+@overload
+def mult(x: myint, y: myint) -> myint:
+ ...
+
+
+@overload
+def mult(x: float, y: float) -> float:
+ ...
+
+
+def mult(x, y):
+ """docstring"""
+ return x, y
diff --git a/tests/roots/test-ext-autodoc/target/cached_property.py b/tests/roots/test-ext-autodoc/target/cached_property.py
new file mode 100644
index 000000000..63ec09f8e
--- /dev/null
+++ b/tests/roots/test-ext-autodoc/target/cached_property.py
@@ -0,0 +1,7 @@
+from functools import cached_property
+
+
+class Foo:
+ @cached_property
+ def prop(self) -> int:
+ return 1
diff --git a/tests/roots/test-ext-autodoc/target/generic_class.py b/tests/roots/test-ext-autodoc/target/generic_class.py
new file mode 100644
index 000000000..cf4c5ed37
--- /dev/null
+++ b/tests/roots/test-ext-autodoc/target/generic_class.py
@@ -0,0 +1,12 @@
+from typing import TypeVar, Generic
+
+
+T = TypeVar('T')
+
+
+# Test that typing.Generic's __new__ method does not mask our class'
+# __init__ signature.
+class A(Generic[T]):
+ """docstring for A"""
+ def __init__(self, a, b=None):
+ pass
diff --git a/tests/roots/test-ext-autodoc/target/overload2.py b/tests/roots/test-ext-autodoc/target/overload2.py
new file mode 100644
index 000000000..e901f791b
--- /dev/null
+++ b/tests/roots/test-ext-autodoc/target/overload2.py
@@ -0,0 +1,5 @@
+from target.overload import Bar
+
+
+class Baz(Bar):
+ pass
diff --git a/tests/roots/test-intl/xx/LC_MESSAGES/toctree.po b/tests/roots/test-intl/xx/LC_MESSAGES/toctree.po
index 8ca1dc52c..62cccdfc1 100644
--- a/tests/roots/test-intl/xx/LC_MESSAGES/toctree.po
+++ b/tests/roots/test-intl/xx/LC_MESSAGES/toctree.po
@@ -1,5 +1,5 @@
# SOME DESCRIPTIVE TITLE.
-# Copyright (C)
+# Copyright (C)
# This file is distributed under the same license as the Sphinx intl <Tests> package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
diff --git a/tests/roots/test-linkcheck-localserver/conf.py b/tests/roots/test-linkcheck-localserver/conf.py
new file mode 100644
index 000000000..2ba1f85e8
--- /dev/null
+++ b/tests/roots/test-linkcheck-localserver/conf.py
@@ -0,0 +1,2 @@
+exclude_patterns = ['_build']
+linkcheck_anchors = True
diff --git a/tests/roots/test-linkcheck-localserver/index.rst b/tests/roots/test-linkcheck-localserver/index.rst
new file mode 100644
index 000000000..807fe964b
--- /dev/null
+++ b/tests/roots/test-linkcheck-localserver/index.rst
@@ -0,0 +1 @@
+`local server <http://localhost:7777/#anchor>`_
diff --git a/tests/roots/test-numbered-circular/conf.py b/tests/roots/test-numbered-circular/conf.py
index 027d21cda..a45d22e28 100644
--- a/tests/roots/test-numbered-circular/conf.py
+++ b/tests/roots/test-numbered-circular/conf.py
@@ -1 +1 @@
-exclude_patterns = ['_build']
+exclude_patterns = ['_build']
diff --git a/tests/roots/test-theming/MANIFEST.in b/tests/roots/test-theming/MANIFEST.in
index 0e977e756..0ace41cb7 100644
--- a/tests/roots/test-theming/MANIFEST.in
+++ b/tests/roots/test-theming/MANIFEST.in
@@ -1,2 +1,2 @@
-recursive-include test_theme *.conf
-
+recursive-include test_theme *.conf
+
diff --git a/tests/roots/test-theming/conf.py b/tests/roots/test-theming/conf.py
index 062b9cf83..0db7cf035 100644
--- a/tests/roots/test-theming/conf.py
+++ b/tests/roots/test-theming/conf.py
@@ -1,3 +1,3 @@
-html_theme = 'test-theme'
-html_theme_path = ['.', 'test_theme']
-exclude_patterns = ['_build']
+html_theme = 'test-theme'
+html_theme_path = ['.', 'test_theme']
+exclude_patterns = ['_build']
diff --git a/tests/roots/test-theming/index.rst b/tests/roots/test-theming/index.rst
index 18a9a4e2f..214dcd7eb 100644
--- a/tests/roots/test-theming/index.rst
+++ b/tests/roots/test-theming/index.rst
@@ -1,5 +1,5 @@
-=======
-Theming
-=======
-
-
+=======
+Theming
+=======
+
+
diff --git a/tests/roots/test-theming/setup.py b/tests/roots/test-theming/setup.py
index e62007f36..02ee259c0 100644
--- a/tests/roots/test-theming/setup.py
+++ b/tests/roots/test-theming/setup.py
@@ -1,11 +1,11 @@
-from setuptools import setup, find_packages
-
-setup(
- name='test-theme',
- packages=find_packages(),
- include_package_data=True,
- entry_points="""
- [sphinx_themes]
- path = test_theme:get_path
- """,
-)
+from setuptools import setup, find_packages
+
+setup(
+ name='test-theme',
+ packages=find_packages(),
+ include_package_data=True,
+ entry_points="""
+ [sphinx_themes]
+ path = test_theme:get_path
+ """,
+)
diff --git a/tests/roots/test-theming/test_theme/__init__.py b/tests/roots/test-theming/test_theme/__init__.py
index 398408c5d..13bdc4b2c 100644
--- a/tests/roots/test-theming/test_theme/__init__.py
+++ b/tests/roots/test-theming/test_theme/__init__.py
@@ -1,5 +1,5 @@
-import os
-
-
-def get_path():
- return os.path.dirname(os.path.abspath(__file__))
+import os
+
+
+def get_path():
+ return os.path.dirname(os.path.abspath(__file__))
diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py
index 1c86b8daa..074602ea9 100644
--- a/tests/test_build_gettext.py
+++ b/tests/test_build_gettext.py
@@ -174,3 +174,21 @@ def test_gettext_template_msgid_order_in_sphinxpot(app):
'msgid "This is Template 2\\.".*'),
result,
flags=re.S)
+
+
+@pytest.mark.sphinx(
+ 'gettext', srcdir='root-gettext',
+ confoverrides={'gettext_compact': 'documentation'})
+def test_build_single_pot(app):
+ app.builder.build_all()
+
+ assert (app.outdir / 'documentation.pot').isfile()
+
+ result = (app.outdir / 'documentation.pot').read_text()
+ assert re.search(
+ ('msgid "Todo".*'
+ 'msgid "Like footnotes.".*'
+ 'msgid "The minute.".*'
+ 'msgid "Generated section".*'),
+ result,
+ flags=re.S)
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 1efc6c14a..8cd541481 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -421,6 +421,11 @@ def test_html5_output(app, cached_etree_parse, fname, expect):
check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
+@pytest.mark.sphinx('html', parallel=2)
+def test_html_parallel(app):
+ app.build()
+
+
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('html')
diff --git a/tests/test_build_linkcheck.py b/tests/test_build_linkcheck.py
index 7d85f10c5..a78587668 100644
--- a/tests/test_build_linkcheck.py
+++ b/tests/test_build_linkcheck.py
@@ -8,8 +8,10 @@
:license: BSD, see LICENSE for details.
"""
+import http.server
import json
import re
+import threading
from unittest import mock
import pytest
@@ -106,6 +108,21 @@ def test_anchors_ignored(app, status, warning):
# expect all ok when excluding #top
assert not content
+@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver', freshenv=True)
+def test_raises_for_invalid_status(app, status, warning):
+ server_thread = HttpServerThread(InternalServerErrorHandler, daemon=True)
+ server_thread.start()
+ try:
+ app.builder.build_all()
+ finally:
+ server_thread.terminate()
+ content = (app.outdir / 'output.txt').read_text()
+ assert content == (
+ "index.rst:1: [broken] http://localhost:7777/#anchor: "
+ "500 Server Error: Internal Server Error "
+ "for url: http://localhost:7777/\n"
+ )
+
@pytest.mark.sphinx(
'linkcheck', testroot='linkcheck', freshenv=True,
@@ -160,3 +177,22 @@ def test_linkcheck_request_headers(app, status, warning):
assert headers["X-Secret"] == "open sesami"
else:
assert headers["Accept"] == "text/html,application/xhtml+xml;q=0.9,*/*;q=0.8"
+
+
+class HttpServerThread(threading.Thread):
+ def __init__(self, handler, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.server = http.server.HTTPServer(("localhost", 7777), handler)
+
+ def run(self):
+ self.server.serve_forever(poll_interval=0.01)
+
+ def terminate(self):
+ self.server.shutdown()
+ self.server.server_close()
+ self.join()
+
+
+class InternalServerErrorHandler(http.server.BaseHTTPRequestHandler):
+ def do_GET(self):
+ self.send_error(500, "Internal Server Error")
diff --git a/tests/test_build_manpage.py b/tests/test_build_manpage.py
index c3c41a2ae..d4b1a320e 100644
--- a/tests/test_build_manpage.py
+++ b/tests/test_build_manpage.py
@@ -30,6 +30,13 @@ def test_all(app, status, warning):
assert 'Footnotes' not in content
+@pytest.mark.sphinx('man', testroot='basic',
+ confoverrides={'man_make_section_directory': True})
+def test_man_make_section_directory(app, status, warning):
+ app.build()
+ assert (app.outdir / '1' / 'python.1').exists()
+
+
@pytest.mark.sphinx('man', testroot='directive-code')
def test_captioned_code_block(app, status, warning):
app.builder.build_all()
diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py
index b98f37912..8040af9cc 100644
--- a/tests/test_domain_py.py
+++ b/tests/test_domain_py.py
@@ -386,6 +386,19 @@ def test_pyfunction_signature_full_py38(app):
[desc_parameter, desc_sig_operator, "/"])])
+@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
+def test_pyfunction_with_number_literals(app):
+ text = ".. py:function:: hello(age=0x10, height=1_6_0)"
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree[1][0][1],
+ [desc_parameterlist, ([desc_parameter, ([desc_sig_name, "age"],
+ [desc_sig_operator, "="],
+ [nodes.inline, "0x10"])],
+ [desc_parameter, ([desc_sig_name, "height"],
+ [desc_sig_operator, "="],
+ [nodes.inline, "1_6_0"])])])
+
+
def test_optional_pyfunction_signature(app):
text = ".. py:function:: compile(source [, filename [, symbol]]) -> ast object"
doctree = restructuredtext.parse(app, text)
diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py
index 90a2ec95a..9cb54de5b 100644
--- a/tests/test_ext_autodoc.py
+++ b/tests/test_ext_autodoc.py
@@ -290,6 +290,21 @@ def test_format_signature(app):
'(b, c=42, *d, **e)'
+@pytest.mark.skipif(sys.version_info < (3, 5), reason='typing is available since python3.5.')
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_autodoc_process_signature_typing_generic(app):
+ actual = do_autodoc(app, 'class', 'target.generic_class.A', {})
+
+ assert list(actual) == [
+ '',
+ '.. py:class:: A(a, b=None)',
+ ' :module: target.generic_class',
+ '',
+ ' docstring for A',
+ '',
+ ]
+
+
def test_autodoc_process_signature_typehints(app):
captured = []
@@ -881,6 +896,26 @@ def test_autodoc_descriptor(app):
]
+@pytest.mark.skipif(sys.version_info < (3, 8),
+ reason='cached_property is available since python3.8.')
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_autodoc_cached_property(app):
+ options = {"members": None,
+ "undoc-members": True}
+ actual = do_autodoc(app, 'class', 'target.cached_property.Foo', options)
+ assert list(actual) == [
+ '',
+ '.. py:class:: Foo()',
+ ' :module: target.cached_property',
+ '',
+ '',
+ ' .. py:method:: Foo.prop',
+ ' :module: target.cached_property',
+ ' :property:',
+ '',
+ ]
+
+
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_member_order(app):
# case member-order='bysource'
@@ -1968,6 +2003,22 @@ def test_overload(app):
@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_overload2(app):
+ options = {"members": None}
+ actual = do_autodoc(app, 'module', 'target.overload2', options)
+ assert list(actual) == [
+ '',
+ '.. py:module:: target.overload2',
+ '',
+ '',
+ '.. py:class:: Baz(x: int, y: int)',
+ ' Baz(x: str, y: str)',
+ ' :module: target.overload2',
+ '',
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_pymodule_for_ModuleLevelDocumenter(app):
app.env.ref_context['py:module'] = 'target.classes'
actual = do_autodoc(app, 'class', 'Foo')
diff --git a/tests/test_ext_autodoc_configs.py b/tests/test_ext_autodoc_configs.py
index 3d0bfd395..7d51b7f0e 100644
--- a/tests/test_ext_autodoc_configs.py
+++ b/tests/test_ext_autodoc_configs.py
@@ -642,6 +642,54 @@ def test_autodoc_typehints_description_for_invalid_node(app):
restructuredtext.parse(app, text) # raises no error
+@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.')
+@pytest.mark.sphinx('text', testroot='ext-autodoc')
+def test_autodoc_type_aliases(app):
+ # default
+ options = {"members": None}
+ actual = do_autodoc(app, 'module', 'target.annotations', options)
+ assert list(actual) == [
+ '',
+ '.. py:module:: target.annotations',
+ '',
+ '',
+ '.. py:function:: mult(x: int, y: int) -> int',
+ ' mult(x: float, y: float) -> float',
+ ' :module: target.annotations',
+ '',
+ ' docstring',
+ '',
+ '',
+ '.. py:function:: sum(x: int, y: int) -> int',
+ ' :module: target.annotations',
+ '',
+ ' docstring',
+ '',
+ ]
+
+ # define aliases
+ app.config.autodoc_type_aliases = {'myint': 'myint'}
+ actual = do_autodoc(app, 'module', 'target.annotations', options)
+ assert list(actual) == [
+ '',
+ '.. py:module:: target.annotations',
+ '',
+ '',
+ '.. py:function:: mult(x: myint, y: myint) -> myint',
+ ' mult(x: float, y: float) -> float',
+ ' :module: target.annotations',
+ '',
+ ' docstring',
+ '',
+ '',
+ '.. py:function:: sum(x: myint, y: myint) -> myint',
+ ' :module: target.annotations',
+ '',
+ ' docstring',
+ '',
+ ]
+
+
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_default_options(app):
# no settings
diff --git a/tests/test_ext_autodoc_events.py b/tests/test_ext_autodoc_events.py
index 4e8348abc..7ddc952ab 100644
--- a/tests/test_ext_autodoc_events.py
+++ b/tests/test_ext_autodoc_events.py
@@ -28,7 +28,8 @@ def test_process_docstring(app):
'.. py:function:: func()',
' :module: target.process_docstring',
'',
- ' my docstring'
+ ' my docstring',
+ '',
]
diff --git a/tests/test_intl.py b/tests/test_intl.py
index 1d1282baa..c0b87d5ce 100644
--- a/tests/test_intl.py
+++ b/tests/test_intl.py
@@ -14,8 +14,10 @@ import re
import pytest
from babel.messages import pofile, mofile
+from babel.messages.catalog import Catalog
from docutils import nodes
+from sphinx import locale
from sphinx.testing.util import (
path, etree_parse, strip_escseq,
assert_re_search, assert_not_re_search, assert_startswith, assert_node
@@ -1289,3 +1291,30 @@ def test_image_glob_intl_using_figure_language_filename(app):
def getwarning(warnings):
return strip_escseq(warnings.getvalue().replace(os.sep, '/'))
+
+
+@pytest.mark.sphinx('html', testroot='basic', confoverrides={'language': 'de'})
+def test_customize_system_message(make_app, app_params, sphinx_test_tempdir):
+ try:
+ # clear translators cache
+ locale.translators.clear()
+
+ # prepare message catalog (.po)
+ locale_dir = sphinx_test_tempdir / 'basic' / 'locales' / 'de' / 'LC_MESSAGES'
+ locale_dir.makedirs()
+ with (locale_dir / 'sphinx.po').open('wb') as f:
+ catalog = Catalog()
+ catalog.add('Quick search', 'QUICK SEARCH')
+ pofile.write_po(f, catalog)
+
+ # construct application and convert po file to .mo
+ args, kwargs = app_params
+ app = make_app(*args, **kwargs)
+ assert (locale_dir / 'sphinx.mo').exists()
+ assert app.translator.gettext('Quick search') == 'QUICK SEARCH'
+
+ app.build()
+ content = (app.outdir / 'index.html').read_text()
+ assert 'QUICK SEARCH' in content
+ finally:
+ locale.translators.clear()
diff --git a/tests/test_pycode.py b/tests/test_pycode.py
index 458e813f6..20f01f17d 100644
--- a/tests/test_pycode.py
+++ b/tests/test_pycode.py
@@ -26,7 +26,7 @@ def test_ModuleAnalyzer_get_module_source():
ModuleAnalyzer.get_module_source('builtins')
with pytest.raises(PycodeError):
ModuleAnalyzer.get_module_source('itertools')
-
+
def test_ModuleAnalyzer_for_string():
analyzer = ModuleAnalyzer.for_string('print("Hello world")', 'module_name')
diff --git a/tests/test_pycode_ast.py b/tests/test_pycode_ast.py
index 9b12d24d5..bbff64dd0 100644
--- a/tests/test_pycode_ast.py
+++ b/tests/test_pycode_ast.py
@@ -53,12 +53,12 @@ from sphinx.pycode import ast
("+ a", "+ a"), # UAdd
("- 1", "- 1"), # UnaryOp
("- a", "- a"), # USub
- ("(1, 2, 3)", "1, 2, 3"), # Tuple
+ ("(1, 2, 3)", "(1, 2, 3)"), # Tuple
("()", "()"), # Tuple (empty)
])
def test_unparse(source, expected):
module = ast.parse(source)
- assert ast.unparse(module.body[0].value) == expected
+ assert ast.unparse(module.body[0].value, source) == expected
def test_unparse_None():
@@ -66,8 +66,12 @@ def test_unparse_None():
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
-def test_unparse_py38():
- source = "lambda x=0, /, y=1, *args, z, **kwargs: x + y + z"
- expected = "lambda x=0, /, y=1, *args, z, **kwargs: ..."
+@pytest.mark.parametrize('source,expected', [
+ ("lambda x=0, /, y=1, *args, z, **kwargs: x + y + z",
+ "lambda x=0, /, y=1, *args, z, **kwargs: ..."), # posonlyargs
+ ("0x1234", "0x1234"), # Constant
+ ("1_000_000", "1_000_000"), # Constant
+])
+def test_unparse_py38(source, expected):
module = ast.parse(source)
- assert ast.unparse(module.body[0].value) == expected
+ assert ast.unparse(module.body[0].value, source) == expected
diff --git a/tests/test_util_typing.py b/tests/test_util_typing.py
index 932fdbfc0..3f8ddbb37 100644
--- a/tests/test_util_typing.py
+++ b/tests/test_util_typing.py
@@ -32,6 +32,10 @@ class MyList(List[T]):
pass
+class BrokenType:
+ __args__ = int
+
+
def test_stringify():
assert stringify(int) == "int"
assert stringify(str) == "str"
@@ -113,3 +117,7 @@ def test_stringify_type_hints_alias():
MyTuple = Tuple[str, str]
assert stringify(MyStr) == "str"
assert stringify(MyTuple) == "Tuple[str, str]" # type: ignore
+
+
+def test_stringify_broken_type_hints():
+ assert stringify(BrokenType) == 'test_util_typing.BrokenType'
diff --git a/tox.ini b/tox.ini
index bddd822a6..a61299979 100644
--- a/tox.ini
+++ b/tox.ini
@@ -26,6 +26,7 @@ extras =
test
setenv =
PYTHONWARNINGS = all,ignore::ImportWarning:importlib._bootstrap_external,ignore::DeprecationWarning:site,ignore::DeprecationWarning:distutils
+ PYTEST_ADDOPTS = --color yes
commands=
pytest --durations 25 {posargs}
@@ -40,16 +41,6 @@ extras =
commands =
flake8 {posargs}
-[testenv:pylint]
-basepython = python3
-description =
- Run source code analyzer.
-deps =
- pylint
- {[testenv]deps}
-commands =
- pylint --rcfile utils/pylintrc sphinx
-
[testenv:coverage]
basepython = python3
description =
diff --git a/utils/pylintrc b/utils/pylintrc
deleted file mode 100644
index c2b338b79..000000000
--- a/utils/pylintrc
+++ /dev/null
@@ -1,301 +0,0 @@
-# lint Python modules using external checkers.
-#
-# This is the main checker controlling the other ones and the reports
-# generation. It is itself both a raw checker and an astng checker in order
-# to:
-# * handle message activation / deactivation at the module level
-# * handle some basic but necessary stats'data (number of classes, methods...)
-#
-[MASTER]
-
-# Specify a configuration file.
-#rcfile=
-
-# Profiled execution.
-profile=no
-
-# Add <file or directory> to the black list. It should be a base name, not a
-# path. You may set this option multiple times.
-ignore=.svn
-
-# Pickle collected data for later comparisons.
-persistent=yes
-
-# Set the cache size for astng objects.
-cache-size=500
-
-# List of plugins (as comma separated values of python modules names) to load,
-# usually to register additional checkers.
-load-plugins=
-
-
-[MESSAGES CONTROL]
-
-# Enable only checker(s) with the given id(s). This option conflict with the
-# disable-checker option
-#enable-checker=
-
-# Enable all checker(s) except those with the given id(s). This option conflict
-# with the disable-checker option
-#disable-checker=
-
-# Enable all messages in the listed categories.
-#enable-msg-cat=
-
-# Disable all messages in the listed categories.
-#disable-msg-cat=
-
-# Enable the message(s) with the given id(s).
-#enable-msg=
-
-# Disable the message(s) with the given id(s).
-disable-msg=C0323,W0142,C0301,C0103,C0111,E0213,C0302,C0203,W0703,R0201,W0613,W0612,W0622
-
-
-[REPORTS]
-
-# set the output format. Available formats are text, parseable, colorized and
-# html
-output-format=colorized
-
-# Include message's id in output
-include-ids=yes
-
-# Put messages in a separate file for each module / package specified on the
-# command line instead of printing them on stdout. Reports (if any) will be
-# written in a file name "pylint_global.[txt|html]".
-files-output=no
-
-# Tells wether to display a full report or only the messages
-reports=yes
-
-# Python expression which should return a note less than 10 (10 is the highest
-# note).You have access to the variables errors warning, statement which
-# respectively contain the number of errors / warnings messages and the total
-# number of statements analyzed. This is used by the global evaluation report
-# (R0004).
-evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
-
-# Add a comment according to your evaluation note. This is used by the global
-# evaluation report (R0004).
-comment=no
-
-# Enable the report(s) with the given id(s).
-#enable-report=
-
-# Disable the report(s) with the given id(s).
-#disable-report=
-
-
-# checks for
-# * unused variables / imports
-# * undefined variables
-# * redefinition of variable from builtins or from an outer scope
-# * use of variable before assigment
-#
-[VARIABLES]
-
-# Tells wether we should check for unused import in __init__ files.
-init-import=no
-
-# A regular expression matching names used for dummy variables (i.e. not used).
-dummy-variables-rgx=_|dummy
-
-# List of additional names supposed to be defined in builtins. Remember that
-# you should avoid to define new builtins when possible.
-additional-builtins=
-
-
-# try to find bugs in the code using type inference
-#
-[TYPECHECK]
-
-# Tells wether missing members accessed in mixin class should be ignored. A
-# mixin class is detected if its name ends with "mixin" (case insensitive).
-ignore-mixin-members=yes
-
-# When zope mode is activated, consider the acquired-members option to ignore
-# access to some undefined attributes.
-zope=no
-
-# List of members which are usually get through zope's acquisition mecanism and
-# so shouldn't trigger E0201 when accessed (need zope=yes to be considered).
-acquired-members=REQUEST,acl_users,aq_parent
-
-
-# checks for :
-# * doc strings
-# * modules / classes / functions / methods / arguments / variables name
-# * number of arguments, local variables, branchs, returns and statements in
-# functions, methods
-# * required module attributes
-# * dangerous default values as arguments
-# * redefinition of function / method / class
-# * uses of the global statement
-#
-[BASIC]
-
-# Required attributes for module, separated by a comma
-required-attributes=
-
-# Regular expression which should only match functions or classes name which do
-# not require a docstring
-no-docstring-rgx=__.*__
-
-# Regular expression which should only match correct module names
-module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
-
-# Regular expression which should only match correct module level names
-const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))$
-
-# Regular expression which should only match correct class names
-class-rgx=[A-Z_][a-zA-Z0-9]+$
-
-# Regular expression which should only match correct function names
-function-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct method names
-method-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct instance attribute names
-attr-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct argument names
-argument-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct variable names
-variable-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct list comprehension /
-# generator expression variable names
-inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
-
-# Good variable names which should always be accepted, separated by a comma
-good-names=i,j,k,ex,Run,_
-
-# Bad variable names which should always be refused, separated by a comma
-bad-names=foo,bar,baz,toto,tutu,tata
-
-# List of builtins function names that should not be used, separated by a comma
-bad-functions=apply,input
-
-
-# checks for sign of poor/misdesign:
-# * number of methods, attributes, local variables...
-# * size, complexity of functions, methods
-#
-[DESIGN]
-
-# Maximum number of arguments for function / method
-max-args=12
-
-# Maximum number of locals for function / method body
-max-locals=30
-
-# Maximum number of return / yield for function / method body
-max-returns=12
-
-# Maximum number of branch for function / method body
-max-branchs=30
-
-# Maximum number of statements in function / method body
-max-statements=60
-
-# Maximum number of parents for a class (see R0901).
-max-parents=7
-
-# Maximum number of attributes for a class (see R0902).
-max-attributes=20
-
-# Minimum number of public methods for a class (see R0903).
-min-public-methods=0
-
-# Maximum number of public methods for a class (see R0904).
-max-public-methods=20
-
-
-# checks for
-# * external modules dependencies
-# * relative / wildcard imports
-# * cyclic imports
-# * uses of deprecated modules
-#
-[IMPORTS]
-
-# Deprecated modules which should not be used, separated by a comma
-deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
-
-# Create a graph of every (i.e. internal and external) dependencies in the
-# given file (report R0402 must not be disabled)
-import-graph=
-
-# Create a graph of external dependencies in the given file (report R0402 must
-# not be disabled)
-ext-import-graph=
-
-# Create a graph of internal dependencies in the given file (report R0402 must
-# not be disabled)
-int-import-graph=
-
-
-# checks for :
-# * methods without self as first argument
-# * overridden methods signature
-# * access only to existant members via self
-# * attributes not defined in the __init__ method
-# * supported interfaces implementation
-# * unreachable code
-#
-[CLASSES]
-
-# List of interface methods to ignore, separated by a comma. This is used for
-# instance to not check methods defines in Zope's Interface base class.
-ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
-
-# List of method names used to declare (i.e. assign) instance attributes.
-defining-attr-methods=__init__,__new__,setUp
-
-
-# checks for similarities and duplicated code. This computation may be
-# memory / CPU intensive, so you should disable it if you experiments some
-# problems.
-#
-[SIMILARITIES]
-
-# Minimum lines number of a similarity.
-min-similarity-lines=10
-
-# Ignore comments when computing similarities.
-ignore-comments=yes
-
-# Ignore docstrings when computing similarities.
-ignore-docstrings=yes
-
-
-# checks for:
-# * warning notes in the code
-# * PEP 263: source code with non ascii character but no encoding declaration
-#
-[MISCELLANEOUS]
-
-# List of note tags to take in consideration, separated by a comma.
-notes=FIXME,XXX,TODO
-
-
-# checks for :
-# * unauthorized constructions
-# * strict indentation
-# * line length
-# * use of <> instead of !=
-#
-[FORMAT]
-
-# Maximum number of characters on a single line.
-max-line-length=90
-
-# Maximum number of lines in a module
-max-module-lines=1000
-
-# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
-# tab).
-indent-string=' '