mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Fix #8800: autodoc: Uninitialized attributes in superclass are recognized as undocumented
Unintentionally, uninitialized attributes defined at superclasses are recognized as undocumented in the filtering step. Therefore, they are filtered if `:undoc-members:` option given.
This commit is contained in:
parent
7ca279e33a
commit
6d8c9183fa
2
CHANGES
2
CHANGES
@ -77,6 +77,8 @@ Bugs fixed
|
||||
contains invalid type comments
|
||||
* #8693: autodoc: Default values for overloaded functions are rendered as string
|
||||
* #8134: autodoc: crashes when mocked decorator takes arguments
|
||||
* #8800: autodoc: Uninitialized attributes in superclass are recognized as
|
||||
undocumented
|
||||
* #8306: autosummary: mocked modules are documented as empty page when using
|
||||
:recursive: option
|
||||
* #8232: graphviz: Image node is not rendered if graph file is in subdirectory
|
||||
|
@ -294,24 +294,35 @@ def get_class_members(subject: Any, objpath: List[str], attrgetter: Callable
|
||||
|
||||
try:
|
||||
for cls in getmro(subject):
|
||||
# annotation only member (ex. attr: int)
|
||||
for name in getannotations(cls):
|
||||
name = unmangle(cls, name)
|
||||
if name and name not in members:
|
||||
members[name] = ObjectMember(name, INSTANCEATTR, class_=cls)
|
||||
|
||||
# append instance attributes (cf. self.attr1) if analyzer knows
|
||||
try:
|
||||
modname = safe_getattr(cls, '__module__')
|
||||
qualname = safe_getattr(cls, '__qualname__')
|
||||
analyzer = ModuleAnalyzer.for_module(modname)
|
||||
analyzer.analyze()
|
||||
except AttributeError:
|
||||
qualname = None
|
||||
analyzer = None
|
||||
except PycodeError:
|
||||
analyzer = None
|
||||
|
||||
# annotation only member (ex. attr: int)
|
||||
for name in getannotations(cls):
|
||||
name = unmangle(cls, name)
|
||||
if name and name not in members:
|
||||
if analyzer and (qualname, name) in analyzer.attr_docs:
|
||||
docstring = '\n'.join(analyzer.attr_docs[qualname, name])
|
||||
else:
|
||||
docstring = None
|
||||
|
||||
members[name] = ObjectMember(name, INSTANCEATTR, class_=cls,
|
||||
docstring=docstring)
|
||||
|
||||
# append instance attributes (cf. self.attr1) if analyzer knows
|
||||
if analyzer:
|
||||
for (ns, name), docstring in analyzer.attr_docs.items():
|
||||
if ns == qualname and name not in members:
|
||||
members[name] = ObjectMember(name, INSTANCEATTR, class_=cls,
|
||||
docstring='\n'.join(docstring))
|
||||
except (AttributeError, PycodeError):
|
||||
pass
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
class Base:
|
||||
attr1: int #: docstring
|
||||
attr2: str
|
||||
|
||||
|
||||
class Derived(Base):
|
||||
attr3: int #: docstring
|
||||
attr4: str
|
@ -106,6 +106,73 @@ def test_inherited_instance_variable(app):
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 6), reason='py36+ is available since python3.6.')
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_uninitialized_attributes(app):
|
||||
options = {"members": None,
|
||||
"inherited-members": True}
|
||||
actual = do_autodoc(app, 'class', 'target.uninitialized_attributes.Derived', options)
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:class:: Derived()',
|
||||
' :module: target.uninitialized_attributes',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Derived.attr1',
|
||||
' :module: target.uninitialized_attributes',
|
||||
' :type: int',
|
||||
'',
|
||||
' docstring',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Derived.attr3',
|
||||
' :module: target.uninitialized_attributes',
|
||||
' :type: int',
|
||||
'',
|
||||
' docstring',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 6), reason='py36+ is available since python3.6.')
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_undocumented_uninitialized_attributes(app):
|
||||
options = {"members": None,
|
||||
"inherited-members": True,
|
||||
"undoc-members": True}
|
||||
actual = do_autodoc(app, 'class', 'target.uninitialized_attributes.Derived', options)
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:class:: Derived()',
|
||||
' :module: target.uninitialized_attributes',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Derived.attr1',
|
||||
' :module: target.uninitialized_attributes',
|
||||
' :type: int',
|
||||
'',
|
||||
' docstring',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Derived.attr2',
|
||||
' :module: target.uninitialized_attributes',
|
||||
' :type: str',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Derived.attr3',
|
||||
' :module: target.uninitialized_attributes',
|
||||
' :type: int',
|
||||
'',
|
||||
' docstring',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Derived.attr4',
|
||||
' :module: target.uninitialized_attributes',
|
||||
' :type: str',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
def test_decorators(app):
|
||||
actual = do_autodoc(app, 'class', 'target.decorator.Baz')
|
||||
assert list(actual) == [
|
||||
|
Loading…
Reference in New Issue
Block a user