mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Fix #6311: autosummary: autosummary table gets confused by complex type hints
This commit is contained in:
1
CHANGES
1
CHANGES
@@ -95,6 +95,7 @@ Bugs fixed
|
||||
* commented term in glossary directive is wrongly recognized
|
||||
* #6299: rst domain: rst:directive directive generates waste space
|
||||
* #6331: man: invalid output when doctest follows rubric
|
||||
* #6311: autosummary: autosummary table gets confused by complex type hints
|
||||
|
||||
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')
|
||||
@@ -953,15 +959,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):
|
||||
@@ -969,8 +975,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
|
||||
@@ -980,7 +986,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
|
||||
@@ -997,8 +1003,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
|
||||
@@ -1008,9 +1014,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__')):
|
||||
@@ -1021,10 +1027,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('\\', '\\\\')
|
||||
@@ -1052,8 +1058,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:
|
||||
@@ -1096,8 +1102,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
|
||||
@@ -1107,18 +1113,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
|
||||
@@ -1307,15 +1314,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:
|
||||
|
||||
@@ -407,8 +407,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()):
|
||||
@@ -429,7 +429,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'
|
||||
|
||||
Reference in New Issue
Block a user