diff options
author | Takeshi KOMIYA <i.tkomiya@gmail.com> | 2020-09-21 00:49:23 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-21 00:49:23 +0900 |
commit | b454d4e4b0801105ad1517289c972ca2030cedd7 (patch) | |
tree | cb715a3a29d8c7cf0ebf11f2b70abb4b5a4eeb80 | |
parent | fd3d654c17874d4cbffbd8e9379728f83a31b30b (diff) | |
parent | 3c017dcdee6b9f4b7f5e46b3b0ba2cebced4d4dc (diff) | |
download | sphinx-git-b454d4e4b0801105ad1517289c972ca2030cedd7.tar.gz |
Merge branch '3.x' into 8190_autodoc-process-docstring-without_ending_blankline
-rw-r--r-- | CHANGES | 12 | ||||
-rw-r--r-- | doc/usage/restructuredtext/domains.rst | 12 | ||||
-rw-r--r-- | package-lock.json | 22 | ||||
-rw-r--r-- | sphinx/builders/html/__init__.py | 13 | ||||
-rw-r--r-- | sphinx/builders/latex/__init__.py | 2 | ||||
-rw-r--r-- | sphinx/directives/code.py | 2 | ||||
-rw-r--r-- | sphinx/domains/c.py | 76 | ||||
-rw-r--r-- | sphinx/domains/cpp.py | 2 | ||||
-rw-r--r-- | sphinx/ext/inheritance_diagram.py | 5 | ||||
-rw-r--r-- | sphinx/ext/napoleon/docstring.py | 2 | ||||
-rw-r--r-- | sphinx/util/fileutil.py | 18 | ||||
-rw-r--r-- | sphinx/util/inspect.py | 5 | ||||
-rw-r--r-- | tests/roots/test-ext-autodoc/target/cached_property.py | 7 | ||||
-rw-r--r-- | tests/test_build_html.py | 8 | ||||
-rw-r--r-- | tests/test_ext_autodoc.py | 20 |
15 files changed, 163 insertions, 43 deletions
@@ -13,14 +13,22 @@ Deprecated 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. + Bugs fixed ---------- * #8085: i18n: Add support for having single text domain * #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 +* #8192: napoleon: description is disappeared when it contains inline literals +* #8169: LaTeX: pxjahyper loaded even when latex_engine is not platex * #8093: The highlight warning has wrong location in some builders (LaTeX, singlehtml and so on) @@ -45,6 +53,10 @@ Features added Bugs fixed ---------- +* #8188: C, add missing items to internal object types dictionary, + e.g., preventing intersphinx from resolving them. + + Testing -------- 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/builders/html/__init__.py b/sphinx/builders/html/__init__.py index 923212a99..c30aa9cfd 100644 --- a/sphinx/builders/html/__init__.py +++ b/sphinx/builders/html/__init__.py @@ -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: diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py index 88c471675..ffb17d2eb 100644 --- a/sphinx/builders/latex/__init__.py +++ b/sphinx/builders/latex/__init__.py @@ -505,7 +505,7 @@ def validate_latex_theme_options(app: Sphinx, config: Config) -> None: def install_pakcages_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) 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 d8ccc2e3d..304c871ea 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -136,8 +136,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: @@ -1563,6 +1563,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 @@ -1792,7 +1797,7 @@ class Symbol: if not declaration: if Symbol.debug_lookup: - Symbol.debug_print("no delcaration") + Symbol.debug_print("no declaration") Symbol.debug_indent -= 2 # good, just a scope creation # TODO: what if we have more than one symbol? @@ -3408,10 +3413,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'] @@ -3428,6 +3436,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 @@ -3468,17 +3507,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: @@ -3494,16 +3532,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] @@ -3607,6 +3639,10 @@ class CDomain(Domain): 'macro': ObjType(_('macro'), 'macro'), 'type': ObjType(_('type'), 'type'), 'var': ObjType(_('variable'), 'data'), + 'enum': ObjType(_('enum'), 'enum'), + 'enumerator': ObjType(_('enumerator'), 'enumerator'), + 'struct': ObjType(_('struct'), 'struct'), + 'union': ObjType(_('union'), 'union'), } directives = { diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 92d578427..7b10f8166 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -4292,7 +4292,7 @@ class Symbol: if not declaration: if Symbol.debug_lookup: - Symbol.debug_print("no delcaration") + Symbol.debug_print("no declaration") Symbol.debug_indent -= 2 # good, just a scope creation # TODO: what if we have more than one symbol? 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..97eaa0fd6 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -36,7 +36,7 @@ _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]+:)?`.+?`)' ) 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/inspect.py b/sphinx/util/inspect.py index a5c64f882..37997e6b2 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) 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/test_build_html.py b/tests/test_build_html.py index e949f1157..1efc6c14a 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -10,8 +10,10 @@ import os import re +from distutils.version import LooseVersion from itertools import cycle, chain +import pygments import pytest from html5lib import HTMLParser @@ -1591,4 +1593,8 @@ def test_html_codeblock_linenos_style_inline(app): app.build() content = (app.outdir / 'index.html').read_text() - assert '<span class="lineno">1 </span>' in content + pygments_version = tuple(LooseVersion(pygments.__version__).version) + if pygments_version > (2, 7): + assert '<span class="linenos">1</span>' in content + else: + assert '<span class="lineno">1 </span>' in content diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index 90a2ec95a..1ba64a0a7 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -881,6 +881,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' |