Skip to content

Commit 1d1aa29

Browse files
committed
Fix instance attributes not being documented by inherited-members
Fixes #477
1 parent eaac3e0 commit 1d1aa29

File tree

4 files changed

+95
-35
lines changed

4 files changed

+95
-35
lines changed

autoapi/_parser.py

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -152,20 +152,20 @@ def parse_classdef(self, node, data=None):
152152
continue
153153

154154
for child in base.get_children():
155-
name = getattr(child, "name", None)
156-
if isinstance(child, (astroid.Assign, astroid.AnnAssign)):
157-
assign_value = _astroid_utils.get_assign_value(child)
158-
if not assign_value:
155+
children_data = self.parse(child)
156+
for child_data in children_data:
157+
name = child_data["name"]
158+
159+
existing_child = children.get(name)
160+
if existing_child and not existing_child["doc"]:
161+
existing_child["doc"] = child_data["doc"]
162+
163+
if name in overridden:
159164
continue
160-
name = assign_value[0]
161165

162-
if not name or name in overridden:
163-
continue
164-
seen.add(name)
165-
child_data = self.parse(child)
166-
base_children.extend(
167-
_parse_child(node, child_data, overloads, base, name)
168-
)
166+
seen.add(name)
167+
if _parse_child(node, child_data, overloads, base):
168+
base_children.append(child_data)
169169

170170
overridden.update(seen)
171171

@@ -297,11 +297,13 @@ def parse_module(self, node):
297297
top_name = node.name.split(".", 1)[0]
298298
for child in node.get_children():
299299
if _astroid_utils.is_local_import_from(child, top_name):
300-
child_data = self._parse_local_import_from(child)
300+
children_data = self._parse_local_import_from(child)
301301
else:
302-
child_data = self.parse(child)
302+
children_data = self.parse(child)
303303

304-
data["children"].extend(_parse_child(node, child_data, overloads))
304+
for child_data in children_data:
305+
if _parse_child(node, child_data, overloads):
306+
data["children"].append(child_data)
305307

306308
return data
307309

@@ -346,28 +348,28 @@ def parse(self, node):
346348
data = self.parse(child)
347349
if data:
348350
break
351+
349352
return data
350353

351354

352-
def _parse_child(node, child_data, overloads, base=None, name=None):
353-
result = []
354-
for single_data in child_data:
355-
if single_data["type"] in ("function", "method", "property"):
356-
if name is None:
357-
name = single_data["name"]
358-
if name in overloads:
359-
grouped = overloads[name]
360-
grouped["doc"] = single_data["doc"]
361-
if single_data["is_overload"]:
362-
grouped["overloads"].append(
363-
(single_data["args"], single_data["return_annotation"])
364-
)
365-
continue
366-
if single_data["is_overload"] and name not in overloads:
367-
overloads[name] = single_data
355+
def _parse_child(node, child_data, overloads, base=None) -> bool:
356+
if child_data["type"] in ("function", "method", "property"):
357+
name = child_data["name"]
358+
if name in overloads:
359+
grouped = overloads[name]
360+
grouped["doc"] = child_data["doc"]
361+
362+
if child_data["is_overload"]:
363+
grouped["overloads"].append(
364+
(child_data["args"], child_data["return_annotation"])
365+
)
366+
367+
return False
368+
369+
if child_data["is_overload"] and name not in overloads:
370+
overloads[name] = child_data
368371

369-
if base:
370-
single_data["inherited"] = base is not node
371-
result.append(single_data)
372+
if base:
373+
child_data["inherited"] = base is not node
372374

373-
return result
375+
return True

docs/changes/477.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix instance attributes not being documented by inherited-members

tests/python/py3example/example/example.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,30 @@ def __getitem__(self, i):
185185

186186
def __len__(self):
187187
return 3
188+
189+
190+
class InheritBaseError(Exception):
191+
"""The base exception."""
192+
def __init__(self):
193+
self.my_message = "one"
194+
"""My message."""
195+
super().__init__(self.my_message)
196+
197+
198+
class InheritError(InheritBaseError):
199+
"""The middle exception."""
200+
def __init__(self):
201+
self.my_other_message = "two"
202+
"""My other message."""
203+
super().__init__()
204+
205+
206+
class SubInheritError(InheritError):
207+
"""The last exception."""
208+
209+
210+
class DuplicateInheritError(InheritBaseError):
211+
"""Not the base exception."""
212+
def __init__(self):
213+
self.my_message = "three"
214+
super().__init__()

tests/python/test_pyintegration.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,9 @@ def test_integration(self, parse):
277277
assert "Initialize self" not in example_file
278278
assert "a new type" not in example_file
279279

280+
def test_inheritance(self, parse):
281+
example_file = parse("_build/html/autoapi/example/index.html")
282+
280283
# Test that members are not inherited from standard library classes.
281284
assert example_file.find(id="example.MyException")
282285
assert not example_file.find(id="example.MyException.args")
@@ -285,6 +288,33 @@ def test_integration(self, parse):
285288
assert example_file.find(id="example.My123.__contains__")
286289
assert example_file.find(id="example.My123.index")
287290

291+
# Test that classes inherit instance attributes
292+
exc = example_file.find(id="example.InheritError")
293+
assert exc
294+
message = example_file.find(id="example.InheritError.my_message")
295+
assert message
296+
message_docstring = message.parent.find("dd").text.strip()
297+
assert message_docstring == "My message."
298+
299+
# Test that classes inherit from the whole mro
300+
exc = example_file.find(id="example.SubInheritError")
301+
assert exc
302+
message = example_file.find(id="example.SubInheritError.my_message")
303+
message_docstring = message.parent.find("dd").text.strip()
304+
assert message_docstring == "My message."
305+
message = example_file.find(id="example.SubInheritError.my_other_message")
306+
assert message
307+
message_docstring = message.parent.find("dd").text.strip()
308+
assert message_docstring == "My other message."
309+
310+
# Test that inherited instance attributes include the docstring
311+
exc = example_file.find(id="example.DuplicateInheritError")
312+
assert exc
313+
message = example_file.find(id="example.DuplicateInheritError.my_message")
314+
assert message
315+
message_docstring = message.parent.find("dd").text.strip()
316+
assert message_docstring == "My message."
317+
288318
def test_annotations(self, parse):
289319
example_file = parse("_build/html/autoapi/example/index.html")
290320

0 commit comments

Comments
 (0)