mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '3.x' into 8190_autodoc-process-docstring-without_ending_blankline
This commit is contained in:
12
CHANGES
12
CHANGES
@@ -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
|
||||
--------
|
||||
|
||||
|
||||
@@ -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::
|
||||
|
||||
|
||||
|
||||
22
package-lock.json
generated
22
package-lock.json
generated
@@ -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": {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
@@ -1562,6 +1562,11 @@ class Symbol:
|
||||
for s in sChild.get_all_symbols():
|
||||
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:
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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]+:)?`.+?`)'
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
7
tests/roots/test-ext-autodoc/target/cached_property.py
Normal file
7
tests/roots/test-ext-autodoc/target/cached_property.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from functools import cached_property
|
||||
|
||||
|
||||
class Foo:
|
||||
@cached_property
|
||||
def prop(self) -> int:
|
||||
return 1
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user