Skip to content

fix 'Alias for field number X' problem with NamedTuples #527

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions numpydoc/docscrape.py
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,7 @@ def properties(self):
for name, func in inspect.getmembers(self._cls)
if (
not name.startswith("_")
and not self._should_skip_member(name, self._cls)
and (
func is None
or isinstance(func, (property, cached_property))
Expand All @@ -715,6 +716,19 @@ def properties(self):
)
]

@staticmethod
def _should_skip_member(name, klass):
if (
# Namedtuples should skip everything in their ._fields as the
# docstrings for each of the members is: "Alias for field number X"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it work properly with #257 (comment) ?

Copy link
Contributor Author

@fohrloop fohrloop Feb 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested with code from this comment, using

.. autoclass:: mypkg.Foo
    :members:

that outputs:

image

does that answer to the question?

Edit: Added also the methods to the screenshot.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does, thank you!

issubclass(klass, tuple)
and hasattr(klass, "_asdict")
and hasattr(klass, "_fields")
and name in klass._fields
):
return True
return False

def _is_show_member(self, name):
if self.show_inherited_members:
return True # show all class members
Expand Down
57 changes: 57 additions & 0 deletions numpydoc/tests/test_docscrape.py
Original file line number Diff line number Diff line change
Expand Up @@ -1641,6 +1641,63 @@ def val(self):
assert class_docstring["Attributes"][0].name == "val"


def test_namedtuple_no_duplicate_attributes():
"""
Ensure that attributes of namedtuples are not duplicated in the docstring.

See gh-257
"""
from collections import namedtuple

foo = namedtuple("Foo", ("bar", "baz"))

# Create the SphinxClassDoc object via get_doc_object
sds = get_doc_object(foo)
assert sds["Attributes"] == []


def test_namedtuple_class_docstring():
"""Ensure that class docstring is preserved when inheriting from namedtuple.

See gh-257
"""
from collections import namedtuple

foo = namedtuple("Foo", ("bar", "baz"))

class MyFoo(foo):
"""MyFoo's class docstring"""

# Create the SphinxClassDoc object via get_doc_object
sds = get_doc_object(MyFoo)
assert sds["Summary"] == ["MyFoo's class docstring"]

# Example dataclass where constructor params are documented explicit.
# Parameter names/descriptions should be included in the docstring, but
# should not result in a duplicated `Attributes` section
class MyFooWithParams(foo):
"""
MyFoo's class docstring

Parameters
----------
bar : str
The bar attribute
baz : str
The baz attribute
"""

bar: str
baz: str

sds = get_doc_object(MyFooWithParams)
assert "MyFoo's class docstring" in sds["Summary"]
assert len(sds["Attributes"]) == 0
assert len(sds["Parameters"]) == 2
assert sds["Parameters"][0].desc[0] == "The bar attribute"
assert sds["Parameters"][1].desc[0] == "The baz attribute"


if __name__ == "__main__":
import pytest

Expand Down