Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sphinx-doc/sphinx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2020-05-31 20:08:57 +0300
committerTakeshi KOMIYA <i.tkomiya@gmail.com>2020-06-01 18:44:07 +0300
commit49de4ab1a16664ebb343d353cf5d54d7dbd32318 (patch)
treed3fcff215d6e4dcb88c9a5eff410bd7b2d9d53f9
parentfb2f777079506a6ed36077ae98c2b267d25b16b5 (diff)
Support overloaded constructors (__call__, __init__ and __new__)
-rw-r--r--sphinx/ext/autodoc/__init__.py46
-rw-r--r--tests/roots/test-ext-autodoc/target/overload.py49
-rw-r--r--tests/test_ext_autodoc.py21
3 files changed, 107 insertions, 9 deletions
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 4fb6a4ce9..31301e01e 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -1279,6 +1279,9 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
'private-members': bool_option, 'special-members': members_option,
} # type: Dict[str, Callable]
+ _signature_class = None # type: Any
+ _signature_method_name = None # type: str
+
def __init__(self, *args: Any) -> None:
super().__init__(*args)
merge_special_members_option(self.options)
@@ -1299,7 +1302,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
self.doc_as_attr = True
return ret
- def _get_signature(self) -> Optional[Signature]:
+ def _get_signature(self) -> Tuple[Optional[Any], Optional[str], Optional[Signature]]:
def get_user_defined_function_or_method(obj: Any, attr: str) -> Any:
""" Get the `attr` function or method from `obj`, if it is user-defined. """
if inspect.is_builtin_class_method(obj, attr):
@@ -1323,7 +1326,8 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
if call is not None:
self.env.app.emit('autodoc-before-process-signature', call, True)
try:
- return inspect.signature(call, bound_method=True)
+ sig = inspect.signature(call, bound_method=True)
+ return type(self.object), '__call__', sig
except ValueError:
pass
@@ -1332,7 +1336,8 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
if new is not None:
self.env.app.emit('autodoc-before-process-signature', new, True)
try:
- return inspect.signature(new, bound_method=True)
+ sig = inspect.signature(new, bound_method=True)
+ return self.object, '__new__', sig
except ValueError:
pass
@@ -1341,7 +1346,8 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
if init is not None:
self.env.app.emit('autodoc-before-process-signature', init, True)
try:
- return inspect.signature(init, bound_method=True)
+ sig = inspect.signature(init, bound_method=True)
+ return self.object, '__init__', sig
except ValueError:
pass
@@ -1351,20 +1357,21 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
# the signature from, so just pass the object itself to our hook.
self.env.app.emit('autodoc-before-process-signature', self.object, False)
try:
- return inspect.signature(self.object, bound_method=False)
+ sig = inspect.signature(self.object, bound_method=False)
+ return None, None, sig
except ValueError:
pass
# Still no signature: happens e.g. for old-style classes
# with __init__ in C and no `__text_signature__`.
- return None
+ return None, None, None
def format_args(self, **kwargs: Any) -> str:
if self.env.config.autodoc_typehints in ('none', 'description'):
kwargs.setdefault('show_annotation', False)
try:
- sig = self._get_signature()
+ self._signature_class, self._signature_method_name, sig = self._get_signature()
except TypeError as exc:
# __signature__ attribute contained junk
logger.warning(__("Failed to get a constructor signature for %s: %s"),
@@ -1380,7 +1387,30 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
if self.doc_as_attr:
return ''
- return super().format_signature(**kwargs)
+ sig = super().format_signature()
+
+ overloaded = False
+ qualname = None
+ # TODO: recreate analyzer for the module of class (To be clear, owner of the method)
+ if self._signature_class and self._signature_method_name and self.analyzer:
+ qualname = '.'.join([self._signature_class.__qualname__,
+ self._signature_method_name])
+ if qualname in self.analyzer.overloads:
+ overloaded = True
+
+ sigs = []
+ if overloaded:
+ # Use signatures for overloaded methods instead of the implementation method.
+ for overload in self.analyzer.overloads.get(qualname):
+ parameters = list(overload.parameters.values())
+ overload = overload.replace(parameters=parameters[1:],
+ return_annotation=Parameter.empty)
+ sig = stringify_signature(overload, **kwargs)
+ sigs.append(sig)
+ else:
+ sigs.append(sig)
+
+ return "\n".join(sigs)
def add_directive_header(self, sig: str) -> None:
sourcename = self.get_sourcename()
diff --git a/tests/roots/test-ext-autodoc/target/overload.py b/tests/roots/test-ext-autodoc/target/overload.py
index 87d1c8533..da43d32eb 100644
--- a/tests/roots/test-ext-autodoc/target/overload.py
+++ b/tests/roots/test-ext-autodoc/target/overload.py
@@ -1,4 +1,4 @@
-from typing import overload
+from typing import Any, overload
@overload
@@ -39,3 +39,50 @@ class Math:
def sum(self, x, y):
"""docstring"""
return x + y
+
+
+class Foo:
+ """docstring"""
+
+ @overload
+ def __new__(cls, x: int, y: int) -> "Foo":
+ ...
+
+ @overload
+ def __new__(cls, x: str, y: str) -> "Foo":
+ ...
+
+ def __new__(cls, x, y):
+ pass
+
+
+class Bar:
+ """docstring"""
+
+ @overload
+ def __init__(cls, x: int, y: int) -> None:
+ ...
+
+ @overload
+ def __init__(cls, x: str, y: str) -> None:
+ ...
+
+ def __init__(cls, x, y):
+ pass
+
+
+class Meta(type):
+ @overload
+ def __call__(cls, x: int, y: int) -> Any:
+ ...
+
+ @overload
+ def __call__(cls, x: str, y: str) -> Any:
+ ...
+
+ def __call__(cls, x, y):
+ pass
+
+
+class Baz(metaclass=Meta):
+ """docstring"""
diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py
index 0a651950b..e80e8b357 100644
--- a/tests/test_ext_autodoc.py
+++ b/tests/test_ext_autodoc.py
@@ -1796,6 +1796,27 @@ def test_overload(app):
'.. py:module:: target.overload',
'',
'',
+ '.. py:class:: Bar(x: int, y: int)',
+ ' Bar(x: str, y: str)',
+ ' :module: target.overload',
+ '',
+ ' docstring',
+ '',
+ '',
+ '.. py:class:: Baz(x: int, y: int)',
+ ' Baz(x: str, y: str)',
+ ' :module: target.overload',
+ '',
+ ' docstring',
+ '',
+ '',
+ '.. py:class:: Foo(x: int, y: int)',
+ ' Foo(x: str, y: str)',
+ ' :module: target.overload',
+ '',
+ ' docstring',
+ '',
+ '',
'.. py:class:: Math()',
' :module: target.overload',
'',