mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #8548 from tk0miya/741_inherited_ivar
Fix #741: autodoc: inherited-members doesn't support instance attributes on super class
This commit is contained in:
commit
57ed10c680
2
CHANGES
2
CHANGES
@ -80,6 +80,8 @@ Bugs fixed
|
|||||||
* #8067: autodoc: A typehint for the instance variable having type_comment on
|
* #8067: autodoc: A typehint for the instance variable having type_comment on
|
||||||
super class is not displayed
|
super class is not displayed
|
||||||
* #8545: autodoc: a __slots__ attribute is not documented even having docstring
|
* #8545: autodoc: a __slots__ attribute is not documented even having docstring
|
||||||
|
* #741: autodoc: inherited-members doesn't work for instance attributes on super
|
||||||
|
class
|
||||||
* #8477: autosummary: non utf-8 reST files are generated when template contains
|
* #8477: autosummary: non utf-8 reST files are generated when template contains
|
||||||
multibyte characters
|
multibyte characters
|
||||||
* #8501: autosummary: summary extraction splits text after "el at." unexpectedly
|
* #8501: autosummary: summary extraction splits text after "el at." unexpectedly
|
||||||
|
@ -1584,7 +1584,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
|||||||
self.add_line(' ' + _('Bases: %s') % ', '.join(bases), sourcename)
|
self.add_line(' ' + _('Bases: %s') % ', '.join(bases), sourcename)
|
||||||
|
|
||||||
def get_object_members(self, want_all: bool) -> Tuple[bool, ObjectMembers]:
|
def get_object_members(self, want_all: bool) -> Tuple[bool, ObjectMembers]:
|
||||||
members = get_class_members(self.object, self.objpath, self.get_attr, self.analyzer)
|
members = get_class_members(self.object, self.objpath, self.get_attr)
|
||||||
if not want_all:
|
if not want_all:
|
||||||
if not self.options.members:
|
if not self.options.members:
|
||||||
return False, [] # type: ignore
|
return False, [] # type: ignore
|
||||||
|
@ -14,7 +14,7 @@ import warnings
|
|||||||
from typing import Any, Callable, Dict, List, Mapping, NamedTuple, Optional, Tuple
|
from typing import Any, Callable, Dict, List, Mapping, NamedTuple, Optional, Tuple
|
||||||
|
|
||||||
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
|
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
|
||||||
from sphinx.pycode import ModuleAnalyzer
|
from sphinx.pycode import ModuleAnalyzer, PycodeError
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
from sphinx.util.inspect import (getannotations, getmro, getslots, isclass, isenumclass,
|
from sphinx.util.inspect import (getannotations, getmro, getslots, isclass, isenumclass,
|
||||||
safe_getattr)
|
safe_getattr)
|
||||||
@ -251,8 +251,8 @@ class ClassAttribute:
|
|||||||
self.docstring = docstring
|
self.docstring = docstring
|
||||||
|
|
||||||
|
|
||||||
def get_class_members(subject: Any, objpath: List[str], attrgetter: Callable,
|
def get_class_members(subject: Any, objpath: List[str], attrgetter: Callable
|
||||||
analyzer: ModuleAnalyzer = None) -> Dict[str, ClassAttribute]:
|
) -> Dict[str, ClassAttribute]:
|
||||||
"""Get members and attributes of target class."""
|
"""Get members and attributes of target class."""
|
||||||
from sphinx.ext.autodoc import INSTANCEATTR
|
from sphinx.ext.autodoc import INSTANCEATTR
|
||||||
|
|
||||||
@ -297,8 +297,9 @@ def get_class_members(subject: Any, objpath: List[str], attrgetter: Callable,
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# annotation only member (ex. attr: int)
|
try:
|
||||||
for cls in getmro(subject):
|
for cls in getmro(subject):
|
||||||
|
# annotation only member (ex. attr: int)
|
||||||
try:
|
try:
|
||||||
for name in getannotations(cls):
|
for name in getannotations(cls):
|
||||||
name = unmangle(cls, name)
|
name = unmangle(cls, name)
|
||||||
@ -307,13 +308,20 @@ def get_class_members(subject: Any, objpath: List[str], attrgetter: Callable,
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if analyzer:
|
|
||||||
# append instance attributes (cf. self.attr1) if analyzer knows
|
# append instance attributes (cf. self.attr1) if analyzer knows
|
||||||
namespace = '.'.join(objpath)
|
try:
|
||||||
|
modname = safe_getattr(cls, '__module__')
|
||||||
|
qualname = safe_getattr(cls, '__qualname__')
|
||||||
|
analyzer = ModuleAnalyzer.for_module(modname)
|
||||||
|
analyzer.analyze()
|
||||||
for (ns, name), docstring in analyzer.attr_docs.items():
|
for (ns, name), docstring in analyzer.attr_docs.items():
|
||||||
if namespace == ns and name not in members:
|
if ns == qualname and name not in members:
|
||||||
members[name] = ClassAttribute(subject, name, INSTANCEATTR,
|
members[name] = ClassAttribute(cls, name, INSTANCEATTR,
|
||||||
'\n'.join(docstring))
|
'\n'.join(docstring))
|
||||||
|
except (AttributeError, PycodeError):
|
||||||
|
pass
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
return members
|
return members
|
||||||
|
|
||||||
|
10
tests/roots/test-ext-autodoc/target/instance_variable.py
Normal file
10
tests/roots/test-ext-autodoc/target/instance_variable.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
class Foo:
|
||||||
|
def __init__(self):
|
||||||
|
self.attr1 = None #: docstring foo
|
||||||
|
self.attr2 = None #: docstring foo
|
||||||
|
|
||||||
|
|
||||||
|
class Bar(Foo):
|
||||||
|
def __init__(self):
|
||||||
|
self.attr2 = None #: docstring bar
|
||||||
|
self.attr3 = None #: docstring bar
|
@ -51,6 +51,61 @@ def test_classes(app):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_instance_variable(app):
|
||||||
|
options = {'members': True}
|
||||||
|
actual = do_autodoc(app, 'class', 'target.instance_variable.Bar', options)
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:class:: Bar()',
|
||||||
|
' :module: target.instance_variable',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:attribute:: Bar.attr2',
|
||||||
|
' :module: target.instance_variable',
|
||||||
|
'',
|
||||||
|
' docstring bar',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:attribute:: Bar.attr3',
|
||||||
|
' :module: target.instance_variable',
|
||||||
|
'',
|
||||||
|
' docstring bar',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_inherited_instance_variable(app):
|
||||||
|
options = {'members': True,
|
||||||
|
'inherited-members': True}
|
||||||
|
actual = do_autodoc(app, 'class', 'target.instance_variable.Bar', options)
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:class:: Bar()',
|
||||||
|
' :module: target.instance_variable',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:attribute:: Bar.attr1',
|
||||||
|
' :module: target.instance_variable',
|
||||||
|
'',
|
||||||
|
' docstring foo',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:attribute:: Bar.attr2',
|
||||||
|
' :module: target.instance_variable',
|
||||||
|
'',
|
||||||
|
' docstring bar',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:attribute:: Bar.attr3',
|
||||||
|
' :module: target.instance_variable',
|
||||||
|
'',
|
||||||
|
' docstring bar',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def test_decorators(app):
|
def test_decorators(app):
|
||||||
actual = do_autodoc(app, 'class', 'target.decorator.Baz')
|
actual = do_autodoc(app, 'class', 'target.decorator.Baz')
|
||||||
assert list(actual) == [
|
assert list(actual) == [
|
||||||
|
Loading…
Reference in New Issue
Block a user