Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Close #4257: autodoc: Add autodoc_class_signature #9171

Merged
merged 1 commit into from May 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES
Expand Up @@ -28,6 +28,8 @@ Features added
* #9195: autodoc: The arguments of ``typing.Literal`` are wrongly rendered
* #9185: autodoc: :confval:`autodoc_typehints` allows ``'both'`` setting to
allow typehints to be included both in the signature and description
* #4257: autodoc: Add :confval:`autodoc_class_signature` to separate the class
entry and the definition of ``__init__()`` method
* #3257: autosummary: Support instance attributes for classes
* #9129: html search: Show search summaries when html_copy_source = False
* #9120: html theme: Eliminate prompt characters of code-block from copyable
Expand Down
14 changes: 14 additions & 0 deletions doc/usage/extensions/autodoc.rst
Expand Up @@ -463,6 +463,20 @@ There are also config values that you can set:

.. versionadded:: 1.4

.. confval:: autodoc_class_signature

This value selects how the signautre will be displayed for the class defined
by :rst:dir:`autoclass` directive. The possible values are:

``"mixed"``
Display the signature with the class name.
``"separated"``
Display the signature as a method.

The default is ``"mixed"``.

.. versionadded:: 4.1

.. confval:: autodoc_member_order

This value selects if automatically documented members are sorted
Expand Down
48 changes: 48 additions & 0 deletions sphinx/ext/autodoc/__init__.py
Expand Up @@ -70,6 +70,9 @@ class _All:
def __contains__(self, item: Any) -> bool:
return True

def append(self, item: Any) -> None:
pass # nothing


class _Empty:
"""A special value for :exclude-members: that never matches to any member."""
Expand Down Expand Up @@ -1440,6 +1443,15 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:

def __init__(self, *args: Any) -> None:
super().__init__(*args)

if self.config.autodoc_class_signature == 'separated':
# show __init__() method
tk0miya marked this conversation as resolved.
Show resolved Hide resolved
if self.options.special_members is None:
self.options['special-members'] = {'__new__', '__init__'}
else:
self.options.special_members.append('__new__')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean .add instead of .append here and in class _All? I tried to build my project documentation with the new autodoc_class_signature option (sphinx installed from github master) and got

# ... snip ...
  File "/home/ruro/projects/personal/aside/.venv/lib/python3.6/site-packages/sphinx/ext/autodoc/directive.py", line 162, in run
    documenter.generate(more_content=self.content)
  File "/home/ruro/projects/personal/aside/.venv/lib/python3.6/site-packages/sphinx/ext/autodoc/__init__.py", line 976, in generate
    self.document_members(all_members)
  File "/home/ruro/projects/personal/aside/.venv/lib/python3.6/site-packages/sphinx/ext/autodoc/__init__.py", line 850, in document_members
    documenter = classes[-1](self.directive, full_mname, self.indent)
  File "/home/ruro/projects/personal/aside/.venv/lib/python3.6/site-packages/sphinx/ext/autodoc/__init__.py", line 1452, in __init__
    self.options.special_members.append('__new__')
AttributeError: 'set' object has no attribute 'append'

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops... you're right. Could you file an issue or pull request, please?

self.options.special_members.append('__init__')

merge_members_option(self.options)

@classmethod
Expand Down Expand Up @@ -1556,6 +1568,9 @@ def format_args(self, **kwargs: Any) -> str:
def format_signature(self, **kwargs: Any) -> str:
if self.doc_as_attr:
return ''
if self.config.autodoc_class_signature == 'separated':
# do not show signatures
return ''

sig = super().format_signature()
sigs = []
Expand Down Expand Up @@ -2193,6 +2208,38 @@ def dummy():
else:
return None

def get_doc(self, ignore: int = None) -> Optional[List[List[str]]]:
if self.objpath[-1] == '__init__':
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.__init__.__doc__ or # for pypy
docstring.strip() == object.__init__.__doc__)): # for !pypy
docstring = None
if docstring:
tab_width = self.directive.state.document.settings.tab_width
return [prepare_docstring(docstring, tabsize=tab_width)]
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
if docstring:
tab_width = self.directive.state.document.settings.tab_width
return [prepare_docstring(docstring, tabsize=tab_width)]
else:
return []
else:
return super().get_doc()


class NonDataDescriptorMixin(DataDocumenterMixinBase):
"""
Expand Down Expand Up @@ -2662,6 +2709,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('autoclass_content', 'class', True, ENUM('both', 'class', 'init'))
app.add_config_value('autodoc_member_order', 'alphabetical', True,
ENUM('alphabetic', 'alphabetical', 'bysource', 'groupwise'))
app.add_config_value('autodoc_class_signature', 'mixed', True, ENUM('mixed', 'separated'))
app.add_config_value('autodoc_default_options', {}, True)
app.add_config_value('autodoc_docstring_signature', True, True)
app.add_config_value('autodoc_mock_imports', [], True)
Expand Down
1 change: 1 addition & 0 deletions sphinx/ext/autosummary/__init__.py
Expand Up @@ -174,6 +174,7 @@ def __init__(self) -> None:
document = Struct(settings=settings)
env = BuildEnvironment()
env.config = Config()
env.config.add('autodoc_class_signature', 'mixed', True, None)
state = Struct(document=document)
super().__init__(env, None, Options(), 0, state)

Expand Down
51 changes: 51 additions & 0 deletions tests/test_ext_autodoc_configs.py
Expand Up @@ -140,6 +140,57 @@ def test_autoclass_content_init(app):
]


@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_class_signature_mixed(app):
app.config.autodoc_class_signature = 'mixed'
options = {"members": None,
"undoc-members": None}
actual = do_autodoc(app, 'class', 'target.classes.Bar', options)
assert list(actual) == [
'',
'.. py:class:: Bar(x, y)',
' :module: target.classes',
'',
]


@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_class_signature_separated_init(app):
app.config.autodoc_class_signature = 'separated'
options = {"members": None,
"undoc-members": None}
actual = do_autodoc(app, 'class', 'target.classes.Bar', options)
assert list(actual) == [
'',
'.. py:class:: Bar',
' :module: target.classes',
'',
'',
' .. py:method:: Bar.__init__(x, y)',
' :module: target.classes',
'',
]


@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_class_signature_separated_new(app):
app.config.autodoc_class_signature = 'separated'
options = {"members": None,
"undoc-members": None}
actual = do_autodoc(app, 'class', 'target.classes.Baz', options)
assert list(actual) == [
'',
'.. py:class:: Baz',
' :module: target.classes',
'',
'',
' .. py:method:: Baz.__new__(cls, x, y)',
' :module: target.classes',
' :staticmethod:',
'',
]


@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autoclass_content_both(app):
app.config.autoclass_content = 'both'
Expand Down