mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Fix #8545: autodoc: a __slots__ attribute is not documented even having docstring
To avoid filtering __slots__ attributes having docstring at filter_members(), this passes docstring captured at get_class_members() to the filter_members() via ObjectMember.
This commit is contained in:
1
CHANGES
1
CHANGES
@@ -79,6 +79,7 @@ Bugs fixed
|
||||
* #8522: autodoc: ``__bool__`` method could be called
|
||||
* #8067: autodoc: A typehint for the instance variable having type_comment on
|
||||
super class is not displayed
|
||||
* #8545: autodoc: a __slots__ attribute is not documented even having docstring
|
||||
* #8477: autosummary: non utf-8 reST files are generated when template contains
|
||||
multibyte characters
|
||||
* #8501: autosummary: summary extraction splits text after "el at." unexpectedly
|
||||
|
||||
@@ -275,9 +275,11 @@ class ObjectMember(tuple):
|
||||
def __new__(cls, name: str, obj: Any, **kwargs: Any) -> Any:
|
||||
return super().__new__(cls, (name, obj)) # type: ignore
|
||||
|
||||
def __init__(self, name: str, obj: Any, skipped: bool = False) -> None:
|
||||
def __init__(self, name: str, obj: Any, docstring: Optional[str] = None,
|
||||
skipped: bool = False) -> None:
|
||||
self.__name__ = name
|
||||
self.object = obj
|
||||
self.docstring = docstring
|
||||
self.skipped = skipped
|
||||
|
||||
|
||||
@@ -709,6 +711,11 @@ class Documenter:
|
||||
cls_doc = self.get_attr(cls, '__doc__', None)
|
||||
if cls_doc == doc:
|
||||
doc = None
|
||||
|
||||
if isinstance(obj, ObjectMember) and obj.docstring:
|
||||
# hack for ClassDocumenter to inject docstring via ObjectMember
|
||||
doc = obj.docstring
|
||||
|
||||
has_doc = bool(doc)
|
||||
|
||||
metadata = extract_metadata(doc)
|
||||
@@ -1585,16 +1592,18 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
||||
selected = []
|
||||
for name in self.options.members: # type: str
|
||||
if name in members:
|
||||
selected.append((name, members[name].value))
|
||||
selected.append(ObjectMember(name, members[name].value,
|
||||
docstring=members[name].docstring))
|
||||
else:
|
||||
logger.warning(__('missing attribute %s in object %s') %
|
||||
(name, self.fullname), type='autodoc')
|
||||
return False, selected
|
||||
elif self.options.inherited_members:
|
||||
return False, [(m.name, m.value) for m in members.values()]
|
||||
return False, [ObjectMember(m.name, m.value, docstring=m.docstring)
|
||||
for m in members.values()]
|
||||
else:
|
||||
return False, [(m.name, m.value) for m in members.values()
|
||||
if m.class_ == self.object]
|
||||
return False, [ObjectMember(m.name, m.value, docstring=m.docstring)
|
||||
for m in members.values() if m.class_ == self.object]
|
||||
|
||||
def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]:
|
||||
if encoding is not None:
|
||||
|
||||
@@ -310,9 +310,10 @@ def get_class_members(subject: Any, objpath: List[str], attrgetter: Callable,
|
||||
if analyzer:
|
||||
# append instance attributes (cf. self.attr1) if analyzer knows
|
||||
namespace = '.'.join(objpath)
|
||||
for (ns, name) in analyzer.find_attr_docs():
|
||||
for (ns, name), docstring in analyzer.attr_docs.items():
|
||||
if namespace == ns and name not in members:
|
||||
members[name] = ClassAttribute(subject, name, INSTANCEATTR)
|
||||
members[name] = ClassAttribute(subject, name, INSTANCEATTR,
|
||||
'\n'.join(docstring))
|
||||
|
||||
return members
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
class Foo:
|
||||
"""docstring"""
|
||||
|
||||
__slots__ = ['attr']
|
||||
|
||||
|
||||
class Bar:
|
||||
"""docstring"""
|
||||
|
||||
__slots__ = {'attr1': 'docstring of attr1',
|
||||
'attr2': 'docstring of attr2',
|
||||
'attr3': None}
|
||||
@@ -12,4 +16,6 @@ class Bar:
|
||||
|
||||
|
||||
class Baz:
|
||||
"""docstring"""
|
||||
|
||||
__slots__ = 'attr'
|
||||
|
||||
@@ -1172,6 +1172,8 @@ def test_slots(app):
|
||||
'.. py:class:: Bar()',
|
||||
' :module: target.slots',
|
||||
'',
|
||||
' docstring',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Bar.attr1',
|
||||
' :module: target.slots',
|
||||
@@ -1192,6 +1194,8 @@ def test_slots(app):
|
||||
'.. py:class:: Baz()',
|
||||
' :module: target.slots',
|
||||
'',
|
||||
' docstring',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Baz.attr',
|
||||
' :module: target.slots',
|
||||
@@ -1200,6 +1204,8 @@ def test_slots(app):
|
||||
'.. py:class:: Foo()',
|
||||
' :module: target.slots',
|
||||
'',
|
||||
' docstring',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Foo.attr',
|
||||
' :module: target.slots',
|
||||
|
||||
@@ -77,6 +77,32 @@ def test_decorators(app):
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_slots_attribute(app):
|
||||
options = {"members": None}
|
||||
actual = do_autodoc(app, 'class', 'target.slots.Bar', options)
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:class:: Bar()',
|
||||
' :module: target.slots',
|
||||
'',
|
||||
' docstring',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Bar.attr1',
|
||||
' :module: target.slots',
|
||||
'',
|
||||
' docstring of attr1',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Bar.attr2',
|
||||
' :module: target.slots',
|
||||
'',
|
||||
' docstring of instance attr2',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.')
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_show_inheritance_for_subclass_of_generic_type(app):
|
||||
|
||||
Reference in New Issue
Block a user