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
|
||||
super class is not displayed
|
||||
* #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
|
||||
multibyte characters
|
||||
* #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)
|
||||
|
||||
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 self.options.members:
|
||||
return False, [] # type: ignore
|
||||
|
@ -14,7 +14,7 @@ import warnings
|
||||
from typing import Any, Callable, Dict, List, Mapping, NamedTuple, Optional, Tuple
|
||||
|
||||
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.inspect import (getannotations, getmro, getslots, isclass, isenumclass,
|
||||
safe_getattr)
|
||||
@ -251,8 +251,8 @@ class ClassAttribute:
|
||||
self.docstring = docstring
|
||||
|
||||
|
||||
def get_class_members(subject: Any, objpath: List[str], attrgetter: Callable,
|
||||
analyzer: ModuleAnalyzer = None) -> Dict[str, ClassAttribute]:
|
||||
def get_class_members(subject: Any, objpath: List[str], attrgetter: Callable
|
||||
) -> Dict[str, ClassAttribute]:
|
||||
"""Get members and attributes of target class."""
|
||||
from sphinx.ext.autodoc import INSTANCEATTR
|
||||
|
||||
@ -297,8 +297,9 @@ def get_class_members(subject: Any, objpath: List[str], attrgetter: Callable,
|
||||
except AttributeError:
|
||||
continue
|
||||
|
||||
# annotation only member (ex. attr: int)
|
||||
try:
|
||||
for cls in getmro(subject):
|
||||
# annotation only member (ex. attr: int)
|
||||
try:
|
||||
for name in getannotations(cls):
|
||||
name = unmangle(cls, name)
|
||||
@ -307,13 +308,20 @@ def get_class_members(subject: Any, objpath: List[str], attrgetter: Callable,
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if analyzer:
|
||||
# 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():
|
||||
if namespace == ns and name not in members:
|
||||
members[name] = ClassAttribute(subject, name, INSTANCEATTR,
|
||||
if ns == qualname and name not in members:
|
||||
members[name] = ClassAttribute(cls, name, INSTANCEATTR,
|
||||
'\n'.join(docstring))
|
||||
except (AttributeError, PycodeError):
|
||||
pass
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
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):
|
||||
actual = do_autodoc(app, 'class', 'target.decorator.Baz')
|
||||
assert list(actual) == [
|
||||
|
Loading…
Reference in New Issue
Block a user