summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2020-10-04 22:55:42 +0900
committerGitHub <noreply@github.com>2020-10-04 22:55:42 +0900
commitd8cdad919bc379efdf765e0313c96dd360db847d (patch)
tree3a40a2f51592e2c662e040e863f36059f408ba0f
parenta555e3db8a2e38dc7f79c3dd8ac6cd406f70c9e1 (diff)
parent38bb3774643d779b708970f941f2b16d1ab81b89 (diff)
downloadsphinx-git-d8cdad919bc379efdf765e0313c96dd360db847d.tar.gz
Merge branch '3.x' into 8200_typealias_break_type_annotation
-rw-r--r--CHANGES12
-rw-r--r--doc/extdev/deprecated.rst10
-rw-r--r--doc/usage/configuration.rst6
-rw-r--r--sphinx/application.py7
-rw-r--r--sphinx/builders/html/__init__.py12
-rw-r--r--sphinx/builders/linkcheck.py7
-rw-r--r--sphinx/builders/manpage.py10
-rw-r--r--sphinx/environment/collectors/toctree.py4
-rw-r--r--sphinx/ext/autodoc/__init__.py10
-rw-r--r--sphinx/locale/__init__.py2
-rw-r--r--sphinx/pycode/ast.py18
-rw-r--r--sphinx/transforms/post_transforms/images.py15
-rw-r--r--sphinx/util/typing.py5
-rw-r--r--tests/test_build_manpage.py7
-rw-r--r--tests/test_intl.py29
-rw-r--r--tests/test_pycode_ast.py2
-rw-r--r--tests/test_util_typing.py8
17 files changed, 141 insertions, 23 deletions
diff --git a/CHANGES b/CHANGES
index 5637f66d6..600efc466 100644
--- a/CHANGES
+++ b/CHANGES
@@ -12,6 +12,8 @@ Deprecated
* ``sphinx.builders.latex.LaTeXBuilder.usepackages``
* ``sphinx.builders.latex.LaTeXBuilder.usepackages_afger_hyperref``
+* ``sphinx.ext.autodoc.SingledispatchFunctionDocumenter``
+* ``sphinx.ext.autodoc.SingledispatchMethodDocumenter``
Features added
--------------
@@ -22,11 +24,14 @@ Features added
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
@@ -34,15 +39,22 @@ Bugs fixed
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
* #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
* #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
--------
diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst
index d7ad21fff..2bb8aebfd 100644
--- a/doc/extdev/deprecated.rst
+++ b/doc/extdev/deprecated.rst
@@ -36,6 +36,16 @@ The following is a list of deprecated interfaces.
- 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/usage/configuration.rst b/doc/usage/configuration.rst
index 3fc3a5306..270fcbf33 100644
--- a/doc/usage/configuration.rst
+++ b/doc/usage/configuration.rst
@@ -2245,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/sphinx/application.py b/sphinx/application.py
index 385b74d8a..f91027bf7 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -18,7 +18,7 @@ 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
@@ -293,7 +293,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
diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py
index c30aa9cfd..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:
@@ -785,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/linkcheck.py b/sphinx/builders/linkcheck.py
index 1083e82ec..a9e6b05b0 100644
--- a/sphinx/builders/linkcheck.py
+++ b/sphinx/builders/linkcheck.py
@@ -211,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
@@ -220,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:
@@ -257,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/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 83c44c8df..434fc40e9 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -1302,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):
"""
@@ -1938,6 +1943,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
"""
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/pycode/ast.py b/sphinx/pycode/ast.py
index 9bafff11c..2583448d5 100644
--- a/sphinx/pycode/ast.py
+++ b/sphinx/pycode/ast.py
@@ -166,14 +166,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 "()"
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/typing.py b/sphinx/util/typing.py
index 4dac3b695..8eca67220 100644
--- a/sphinx/util/typing.py
+++ b/sphinx/util/typing.py
@@ -109,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/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_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_ast.py b/tests/test_pycode_ast.py
index 9b12d24d5..32a784b74 100644
--- a/tests/test_pycode_ast.py
+++ b/tests/test_pycode_ast.py
@@ -53,7 +53,7 @@ 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):
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'