From 46f532a824639a97479039fc122533915cdfa10f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20F=C3=B6hr?= Date: Tue, 20 Feb 2024 07:26:06 +0200 Subject: [PATCH] fix 'Alias for field number X' problem with NamedTuples (#527) * fix 'Alias for field number X' problem with NamedTuples * TST: Add test for duplicated attrs w/ namedtuples. * TST: Add test of explicitly documented namedtuple params. --------- Co-authored-by: Ross Barnowski --- numpydoc/docscrape.py | 14 ++++++++ numpydoc/tests/test_docscrape.py | 57 ++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/numpydoc/docscrape.py b/numpydoc/docscrape.py index 4fbbe63c..fb3a0b63 100644 --- a/numpydoc/docscrape.py +++ b/numpydoc/docscrape.py @@ -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)) @@ -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" + 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 diff --git a/numpydoc/tests/test_docscrape.py b/numpydoc/tests/test_docscrape.py index 5077c425..8bbcdc53 100644 --- a/numpydoc/tests/test_docscrape.py +++ b/numpydoc/tests/test_docscrape.py @@ -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