diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 97815c09eb9..12a505fdab8 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -2234,6 +2234,12 @@ def dummy(): return None def get_doc(self, ignore: int = None) -> Optional[List[List[str]]]: + if self._new_docstrings is not None: + # docstring already returned previously, then modified by + # `DocstringSignatureMixin`. Just return the previously-computed + # result, so that we don't lose the processing done by + # `DocstringSignatureMixin`. + return self._new_docstrings if self.objpath[-1] == '__init__': docstring = getdoc(self.object, self.get_attr, self.config.autodoc_inherit_docstrings, @@ -2248,15 +2254,13 @@ def get_doc(self, ignore: int = None) -> Optional[List[List[str]]]: else: return [] elif self.objpath[-1] == '__new__': - __new__ = self.get_attr(self.object, '__new__', None) - if __new__: - docstring = getdoc(__new__, self.get_attr, - self.config.autodoc_inherit_docstrings, - self.parent, self.object_name) - if (docstring is not None and - (docstring == object.__new__.__doc__ or # for pypy - docstring.strip() == object.__new__.__doc__)): # for !pypy - docstring = None + docstring = getdoc(self.object, self.get_attr, + self.config.autodoc_inherit_docstrings, + self.parent, self.object_name) + if (docstring is not None and + (docstring == object.__new__.__doc__ or # for pypy + docstring.strip() == object.__new__.__doc__)): # for !pypy + docstring = None if docstring: tab_width = self.directive.state.document.settings.tab_width return [prepare_docstring(docstring, tabsize=tab_width)] diff --git a/tests/roots/test-ext-autodoc/target/__init__.py b/tests/roots/test-ext-autodoc/target/__init__.py index e49f9a14104..54f52bbffbd 100644 --- a/tests/roots/test-ext-autodoc/target/__init__.py +++ b/tests/roots/test-ext-autodoc/target/__init__.py @@ -114,6 +114,20 @@ class InnerChild(Outer.Inner): class DocstringSig(object): + def __new__(cls, *new_args, **new_kwargs): + """__new__(cls, d, e=1) -> DocstringSig +First line of docstring + + rest of docstring + """ + + def __init__(self, *init_args, **init_kwargs): + """__init__(self, a, b=1) -> None +First line of docstring + + rest of docstring + """ + def meth(self): """meth(FOO, BAR=1) -> BAZ First line of docstring diff --git a/tests/test_ext_autodoc_configs.py b/tests/test_ext_autodoc_configs.py index 0f058040437..6e0159d1448 100644 --- a/tests/test_ext_autodoc_configs.py +++ b/tests/test_ext_autodoc_configs.py @@ -287,14 +287,34 @@ def test_autodoc_inherit_docstrings(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_docstring_signature(app): - options = {"members": None} + options = {"members": None, "special-members": "__init__, __new__"} actual = do_autodoc(app, 'class', 'target.DocstringSig', options) assert list(actual) == [ '', - '.. py:class:: DocstringSig()', + # FIXME: Ideally this would instead be: `DocstringSig(d, e=1)` but + # currently `ClassDocumenter` does not apply the docstring signature + # logic when extracting a signature from a __new__ or __init__ method. + '.. py:class:: DocstringSig(*new_args, **new_kwargs)', ' :module: target', '', '', + ' .. py:method:: DocstringSig.__init__(self, a, b=1) -> None', + ' :module: target', + '', + ' First line of docstring', + '', + ' rest of docstring', + '', + '', + ' .. py:method:: DocstringSig.__new__(cls, d, e=1) -> DocstringSig', + ' :module: target', + ' :staticmethod:', + '', + ' First line of docstring', + '', + ' rest of docstring', + '', + '', ' .. py:method:: DocstringSig.meth(FOO, BAR=1) -> BAZ', ' :module: target', '', @@ -331,10 +351,31 @@ def test_autodoc_docstring_signature(app): actual = do_autodoc(app, 'class', 'target.DocstringSig', options) assert list(actual) == [ '', - '.. py:class:: DocstringSig()', + '.. py:class:: DocstringSig(*new_args, **new_kwargs)', ' :module: target', '', '', + ' .. py:method:: DocstringSig.__init__(*init_args, **init_kwargs)', + ' :module: target', + '', + ' __init__(self, a, b=1) -> None', + ' First line of docstring', + '', + ' rest of docstring', + '', + '', + '', + ' .. py:method:: DocstringSig.__new__(cls, *new_args, **new_kwargs)', + ' :module: target', + ' :staticmethod:', + '', + ' __new__(cls, d, e=1) -> DocstringSig', + ' First line of docstring', + '', + ' rest of docstring', + '', + '', + '', ' .. py:method:: DocstringSig.meth()', ' :module: target', '',