Skip to content

Commit

Permalink
Fix instance attributes not being documented by inherited-members
Browse files Browse the repository at this point in the history
Fixes #477
  • Loading branch information
AWhetter committed Aug 29, 2024
1 parent eaac3e0 commit 1d1aa29
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 35 deletions.
72 changes: 37 additions & 35 deletions autoapi/_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,20 +152,20 @@ def parse_classdef(self, node, data=None):
continue

for child in base.get_children():
name = getattr(child, "name", None)
if isinstance(child, (astroid.Assign, astroid.AnnAssign)):
assign_value = _astroid_utils.get_assign_value(child)
if not assign_value:
children_data = self.parse(child)
for child_data in children_data:
name = child_data["name"]

existing_child = children.get(name)
if existing_child and not existing_child["doc"]:
existing_child["doc"] = child_data["doc"]

if name in overridden:
continue
name = assign_value[0]

if not name or name in overridden:
continue
seen.add(name)
child_data = self.parse(child)
base_children.extend(
_parse_child(node, child_data, overloads, base, name)
)
seen.add(name)
if _parse_child(node, child_data, overloads, base):
base_children.append(child_data)

overridden.update(seen)

Expand Down Expand Up @@ -297,11 +297,13 @@ def parse_module(self, node):
top_name = node.name.split(".", 1)[0]
for child in node.get_children():
if _astroid_utils.is_local_import_from(child, top_name):
child_data = self._parse_local_import_from(child)
children_data = self._parse_local_import_from(child)
else:
child_data = self.parse(child)
children_data = self.parse(child)

data["children"].extend(_parse_child(node, child_data, overloads))
for child_data in children_data:
if _parse_child(node, child_data, overloads):
data["children"].append(child_data)

return data

Expand Down Expand Up @@ -346,28 +348,28 @@ def parse(self, node):
data = self.parse(child)
if data:
break

return data


def _parse_child(node, child_data, overloads, base=None, name=None):
result = []
for single_data in child_data:
if single_data["type"] in ("function", "method", "property"):
if name is None:
name = single_data["name"]
if name in overloads:
grouped = overloads[name]
grouped["doc"] = single_data["doc"]
if single_data["is_overload"]:
grouped["overloads"].append(
(single_data["args"], single_data["return_annotation"])
)
continue
if single_data["is_overload"] and name not in overloads:
overloads[name] = single_data
def _parse_child(node, child_data, overloads, base=None) -> bool:
if child_data["type"] in ("function", "method", "property"):
name = child_data["name"]
if name in overloads:
grouped = overloads[name]
grouped["doc"] = child_data["doc"]

if child_data["is_overload"]:
grouped["overloads"].append(
(child_data["args"], child_data["return_annotation"])
)

return False

if child_data["is_overload"] and name not in overloads:
overloads[name] = child_data

if base:
single_data["inherited"] = base is not node
result.append(single_data)
if base:
child_data["inherited"] = base is not node

return result
return True
1 change: 1 addition & 0 deletions docs/changes/477.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix instance attributes not being documented by inherited-members
27 changes: 27 additions & 0 deletions tests/python/py3example/example/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,30 @@ def __getitem__(self, i):

def __len__(self):
return 3


class InheritBaseError(Exception):
"""The base exception."""
def __init__(self):
self.my_message = "one"
"""My message."""
super().__init__(self.my_message)


class InheritError(InheritBaseError):
"""The middle exception."""
def __init__(self):
self.my_other_message = "two"
"""My other message."""
super().__init__()


class SubInheritError(InheritError):
"""The last exception."""


class DuplicateInheritError(InheritBaseError):
"""Not the base exception."""
def __init__(self):
self.my_message = "three"
super().__init__()
30 changes: 30 additions & 0 deletions tests/python/test_pyintegration.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,9 @@ def test_integration(self, parse):
assert "Initialize self" not in example_file
assert "a new type" not in example_file

def test_inheritance(self, parse):
example_file = parse("_build/html/autoapi/example/index.html")

# Test that members are not inherited from standard library classes.
assert example_file.find(id="example.MyException")
assert not example_file.find(id="example.MyException.args")
Expand All @@ -285,6 +288,33 @@ def test_integration(self, parse):
assert example_file.find(id="example.My123.__contains__")
assert example_file.find(id="example.My123.index")

# Test that classes inherit instance attributes
exc = example_file.find(id="example.InheritError")
assert exc
message = example_file.find(id="example.InheritError.my_message")
assert message
message_docstring = message.parent.find("dd").text.strip()
assert message_docstring == "My message."

# Test that classes inherit from the whole mro
exc = example_file.find(id="example.SubInheritError")
assert exc
message = example_file.find(id="example.SubInheritError.my_message")
message_docstring = message.parent.find("dd").text.strip()
assert message_docstring == "My message."
message = example_file.find(id="example.SubInheritError.my_other_message")
assert message
message_docstring = message.parent.find("dd").text.strip()
assert message_docstring == "My other message."

# Test that inherited instance attributes include the docstring
exc = example_file.find(id="example.DuplicateInheritError")
assert exc
message = example_file.find(id="example.DuplicateInheritError.my_message")
assert message
message_docstring = message.parent.find("dd").text.strip()
assert message_docstring == "My message."

def test_annotations(self, parse):
example_file = parse("_build/html/autoapi/example/index.html")

Expand Down

0 comments on commit 1d1aa29

Please sign in to comment.