mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #6353 from tk0miya/6311_autosummary_confused_by_complex_typehints2
Fix #6311: autosummary: autosummary table gets confused by complex type hints
This commit is contained in:
commit
774c59821c
1
CHANGES
1
CHANGES
@ -108,6 +108,7 @@ Bugs fixed
|
||||
* #6351: "Hyperlink target is not referenced" message is shown even if
|
||||
referenced
|
||||
* #6165: autodoc: ``tab_width`` setting of docutils has been ignored
|
||||
* #6311: autosummary: autosummary table gets confused by complex type hints
|
||||
* Generated Makefiles lack a final EOL (refs: #6232)
|
||||
|
||||
Testing
|
||||
|
@ -365,8 +365,8 @@ class Documenter:
|
||||
return False
|
||||
return True
|
||||
|
||||
def format_args(self):
|
||||
# type: () -> str
|
||||
def format_args(self, **kwargs):
|
||||
# type: (Any) -> str
|
||||
"""Format the argument signature of *self.object*.
|
||||
|
||||
Should return None if the object does not have a signature.
|
||||
@ -385,8 +385,8 @@ class Documenter:
|
||||
# directives of course)
|
||||
return '.'.join(self.objpath) or self.modname
|
||||
|
||||
def format_signature(self):
|
||||
# type: () -> str
|
||||
def format_signature(self, **kwargs):
|
||||
# type: (Any) -> str
|
||||
"""Format the signature (arguments and return annotation) of the object.
|
||||
|
||||
Let the user process it via the ``autodoc-process-signature`` event.
|
||||
@ -397,7 +397,13 @@ class Documenter:
|
||||
else:
|
||||
# try to introspect the signature
|
||||
try:
|
||||
args = self.format_args()
|
||||
try:
|
||||
args = self.format_args(**kwargs)
|
||||
except TypeError:
|
||||
warnings.warn("Documenter.format_args() takes keyword arguments "
|
||||
"since Sphinx-2.1",
|
||||
RemovedInSphinx40Warning)
|
||||
args = self.format_args()
|
||||
except Exception as err:
|
||||
logger.warning(__('error while formatting arguments for %s: %s') %
|
||||
(self.fullname, err), type='autodoc')
|
||||
@ -956,15 +962,15 @@ class DocstringSignatureMixin:
|
||||
return lines
|
||||
return super().get_doc(None, ignore) # type: ignore
|
||||
|
||||
def format_signature(self):
|
||||
# type: () -> str
|
||||
def format_signature(self, **kwargs):
|
||||
# type: (Any) -> str
|
||||
if self.args is None and self.env.config.autodoc_docstring_signature: # type: ignore
|
||||
# only act if a signature is not explicitly given already, and if
|
||||
# the feature is enabled
|
||||
result = self._find_signature()
|
||||
if result is not None:
|
||||
self.args, self.retann = result
|
||||
return super().format_signature() # type: ignore
|
||||
return super().format_signature(**kwargs) # type: ignore
|
||||
|
||||
|
||||
class DocstringStripSignatureMixin(DocstringSignatureMixin):
|
||||
@ -972,8 +978,8 @@ class DocstringStripSignatureMixin(DocstringSignatureMixin):
|
||||
Mixin for AttributeDocumenter to provide the
|
||||
feature of stripping any function signature from the docstring.
|
||||
"""
|
||||
def format_signature(self):
|
||||
# type: () -> str
|
||||
def format_signature(self, **kwargs):
|
||||
# type: (Any) -> str
|
||||
if self.args is None and self.env.config.autodoc_docstring_signature: # type: ignore
|
||||
# only act if a signature is not explicitly given already, and if
|
||||
# the feature is enabled
|
||||
@ -983,7 +989,7 @@ class DocstringStripSignatureMixin(DocstringSignatureMixin):
|
||||
# DocstringSignatureMixin.format_signature.
|
||||
# Documenter.format_signature use self.args value to format.
|
||||
_args, self.retann = result
|
||||
return super().format_signature()
|
||||
return super().format_signature(**kwargs)
|
||||
|
||||
|
||||
class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: ignore
|
||||
@ -1000,8 +1006,8 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
||||
return (inspect.isfunction(member) or inspect.isbuiltin(member) or
|
||||
(inspect.isroutine(member) and isinstance(parent, ModuleDocumenter)))
|
||||
|
||||
def format_args(self):
|
||||
# type: () -> str
|
||||
def format_args(self, **kwargs):
|
||||
# type: (Any) -> str
|
||||
if inspect.isbuiltin(self.object) or inspect.ismethoddescriptor(self.object):
|
||||
# cannot introspect arguments of a C function or method
|
||||
return None
|
||||
@ -1011,9 +1017,9 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
||||
not inspect.isbuiltin(self.object) and
|
||||
not inspect.isclass(self.object) and
|
||||
hasattr(self.object, '__call__')):
|
||||
args = Signature(self.object.__call__).format_args()
|
||||
args = Signature(self.object.__call__).format_args(**kwargs)
|
||||
else:
|
||||
args = Signature(self.object).format_args()
|
||||
args = Signature(self.object).format_args(**kwargs)
|
||||
except TypeError:
|
||||
if (inspect.is_builtin_class_method(self.object, '__new__') and
|
||||
inspect.is_builtin_class_method(self.object, '__init__')):
|
||||
@ -1024,10 +1030,10 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
||||
# signature without the first argument.
|
||||
try:
|
||||
sig = Signature(self.object.__new__, bound_method=True, has_retval=False)
|
||||
args = sig.format_args()
|
||||
args = sig.format_args(**kwargs)
|
||||
except TypeError:
|
||||
sig = Signature(self.object.__init__, bound_method=True, has_retval=False)
|
||||
args = sig.format_args()
|
||||
args = sig.format_args(**kwargs)
|
||||
|
||||
# escape backslashes for reST
|
||||
args = args.replace('\\', '\\\\')
|
||||
@ -1055,8 +1061,8 @@ class DecoratorDocumenter(FunctionDocumenter):
|
||||
# must be lower than FunctionDocumenter
|
||||
priority = -1
|
||||
|
||||
def format_args(self):
|
||||
args = super().format_args()
|
||||
def format_args(self, **kwargs):
|
||||
args = super().format_args(**kwargs)
|
||||
if ',' in args:
|
||||
return args
|
||||
else:
|
||||
@ -1099,8 +1105,8 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
||||
self.doc_as_attr = True
|
||||
return ret
|
||||
|
||||
def format_args(self):
|
||||
# type: () -> str
|
||||
def format_args(self, **kwargs):
|
||||
# type: (Any) -> str
|
||||
# for classes, the relevant signature is the __init__ method's
|
||||
initmeth = self.get_attr(self.object, '__init__', None)
|
||||
# classes without __init__ method, default __init__ or
|
||||
@ -1110,18 +1116,19 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
||||
not(inspect.ismethod(initmeth) or inspect.isfunction(initmeth)):
|
||||
return None
|
||||
try:
|
||||
return Signature(initmeth, bound_method=True, has_retval=False).format_args()
|
||||
sig = Signature(initmeth, bound_method=True, has_retval=False)
|
||||
return sig.format_args(**kwargs)
|
||||
except TypeError:
|
||||
# still not possible: happens e.g. for old-style classes
|
||||
# with __init__ in C
|
||||
return None
|
||||
|
||||
def format_signature(self):
|
||||
# type: () -> str
|
||||
def format_signature(self, **kwargs):
|
||||
# type: (Any) -> str
|
||||
if self.doc_as_attr:
|
||||
return ''
|
||||
|
||||
return super().format_signature()
|
||||
return super().format_signature(**kwargs)
|
||||
|
||||
def add_directive_header(self, sig):
|
||||
# type: (str) -> None
|
||||
@ -1312,15 +1319,15 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
||||
|
||||
return ret
|
||||
|
||||
def format_args(self):
|
||||
# type: () -> str
|
||||
def format_args(self, **kwargs):
|
||||
# type: (Any) -> str
|
||||
if inspect.isbuiltin(self.object) or inspect.ismethoddescriptor(self.object):
|
||||
# can never get arguments of a C function or method
|
||||
return None
|
||||
if inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name):
|
||||
args = Signature(self.object, bound_method=False).format_args()
|
||||
args = Signature(self.object, bound_method=False).format_args(**kwargs)
|
||||
else:
|
||||
args = Signature(self.object, bound_method=True).format_args()
|
||||
args = Signature(self.object, bound_method=True).format_args(**kwargs)
|
||||
# escape backslashes for reST
|
||||
args = args.replace('\\', '\\\\')
|
||||
return args
|
||||
|
@ -329,7 +329,12 @@ class Autosummary(SphinxDirective):
|
||||
|
||||
# -- Grab the signature
|
||||
|
||||
sig = documenter.format_signature()
|
||||
try:
|
||||
sig = documenter.format_signature(show_annotation=False)
|
||||
except TypeError:
|
||||
# the documenter does not support ``show_annotation`` option
|
||||
sig = documenter.format_signature()
|
||||
|
||||
if not sig:
|
||||
sig = ''
|
||||
else:
|
||||
@ -445,7 +450,7 @@ def mangle_signature(sig, max_chars=30):
|
||||
args = [] # type: List[str]
|
||||
opts = [] # type: List[str]
|
||||
|
||||
opt_re = re.compile(r"^(.*, |)([a-zA-Z0-9_*]+)=")
|
||||
opt_re = re.compile(r"^(.*, |)([a-zA-Z0-9_*]+)\s*=\s*")
|
||||
while s:
|
||||
m = opt_re.search(s)
|
||||
if not m:
|
||||
|
@ -413,8 +413,8 @@ class Signature:
|
||||
else:
|
||||
return None
|
||||
|
||||
def format_args(self):
|
||||
# type: () -> str
|
||||
def format_args(self, show_annotation=True):
|
||||
# type: (bool) -> str
|
||||
args = []
|
||||
last_kind = None
|
||||
for i, param in enumerate(self.parameters.values()):
|
||||
@ -435,7 +435,7 @@ class Signature:
|
||||
param.POSITIONAL_OR_KEYWORD,
|
||||
param.KEYWORD_ONLY):
|
||||
arg.write(param.name)
|
||||
if param.annotation is not param.empty:
|
||||
if show_annotation and param.annotation is not param.empty:
|
||||
if isinstance(param.annotation, str) and param.name in self.annotations:
|
||||
arg.write(': ')
|
||||
arg.write(self.format_annotation(self.annotations[param.name]))
|
||||
|
@ -1,4 +1,5 @@
|
||||
from os import * # NOQA
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Foo:
|
||||
@ -11,3 +12,7 @@ class Foo:
|
||||
@property
|
||||
def baz(self):
|
||||
pass
|
||||
|
||||
|
||||
def bar(x: Union[int, str], y: int = 1):
|
||||
pass
|
||||
|
@ -8,4 +8,5 @@
|
||||
|
||||
autosummary_dummy_module
|
||||
autosummary_dummy_module.Foo
|
||||
autosummary_dummy_module.bar
|
||||
autosummary_importfail
|
||||
|
@ -13,9 +13,13 @@ from io import StringIO
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from docutils import nodes
|
||||
|
||||
from sphinx.ext.autosummary import mangle_signature, import_by_name, extract_summary
|
||||
from sphinx.testing.util import etree_parse
|
||||
from sphinx import addnodes
|
||||
from sphinx.ext.autosummary import (
|
||||
autosummary_table, autosummary_toc, mangle_signature, import_by_name, extract_summary
|
||||
)
|
||||
from sphinx.testing.util import assert_node, etree_parse
|
||||
from sphinx.util.docutils import new_document
|
||||
|
||||
html_warnfile = StringIO()
|
||||
@ -179,6 +183,24 @@ def test_escaping(app, status, warning):
|
||||
def test_autosummary_generate(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
doctree = app.env.get_doctree('index')
|
||||
assert_node(doctree, (nodes.paragraph,
|
||||
nodes.paragraph,
|
||||
addnodes.tabular_col_spec,
|
||||
autosummary_table,
|
||||
autosummary_toc))
|
||||
assert_node(doctree[3],
|
||||
[autosummary_table, nodes.table, nodes.tgroup, (nodes.colspec,
|
||||
nodes.colspec,
|
||||
[nodes.tbody, (nodes.row,
|
||||
nodes.row,
|
||||
nodes.row,
|
||||
nodes.row)])])
|
||||
assert doctree[3][0][0][2][0].astext() == 'autosummary_dummy_module\n\n'
|
||||
assert doctree[3][0][0][2][1].astext() == 'autosummary_dummy_module.Foo()\n\n'
|
||||
assert doctree[3][0][0][2][2].astext() == 'autosummary_dummy_module.bar(x[, y])\n\n'
|
||||
assert doctree[3][0][0][2][3].astext() == 'autosummary_importfail\n\n'
|
||||
|
||||
module = (app.srcdir / 'generated' / 'autosummary_dummy_module.rst').text()
|
||||
assert (' .. autosummary::\n'
|
||||
' \n'
|
||||
|
Loading…
Reference in New Issue
Block a user