numpydoc icon indicating copy to clipboard operation
numpydoc copied to clipboard

Overly verbose output for namedtuples

Open anntzer opened this issue 5 years ago • 1 comments

numpydoc generates rather verbose outputs for namedtuples, especially when combined with autosummary:

$ sphinx-quickstart -q -m --sep -pfoo -afoo --extensions=sphinx.ext.autosummary,numpydoc

then edit source/index.rst to contain

.. autosummary::
   foo.Foo

.. automodule:: foo
   :members:

and foo.py to contain

from collections import namedtuple

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

then build with

PYTHONPATH=. make html

This results in bad

Compare with the result using either not numpydoc at all, or sphinx.ext.napoleon instead: good

I guess whether "count" and "index" should be listed as methods can be argued over, but having "bar" and "baz" being listed twice seems clearly too much, and the "Attributes" entry in the autosummary table is not so great either.

anntzer avatar Apr 11 '20 18:04 anntzer

Also, if one tries to actually document the fields, e.g.:


_Foo = namedtuple("_Foo", "bar")

class Foo(_Foo):
    """
    A Foo.

    Attributes
    ----------
    bar : str
        The bar contents of Foo.
    """
    __slots__ = ()

then both the documented description ("The bar contents of Foo") and the docstring autogenerated by namedtuple ("Alias for field number 0") are output by numpydoc.

pitrou avatar Dec 29 '21 14:12 pitrou

I think this is fairly easy to fix. A quick and dirty modification in numpydoc/docscrape.py::ClassDoc does it

Change ClassDoc.properties to

    @property
    def properties(self):
        if self._cls is None:
            return []
        return [
            name
            for name, func in inspect.getmembers(self._cls)
            if (
                not name.startswith("_")
                and not self._should_skip_member(name, self._cls) #this added 
                and (
                    func is None
                    or isinstance(func, (property, cached_property))
                    or inspect.isdatadescriptor(func)
                )
                and self._is_show_member(name)
            )
        ]

and add ClassDoc._should_skip_member:

    @staticmethod
    def _should_skip_member(name, klass):
        if (issubclass(klass, tuple) and hasattr(klass, '_asdict')
                and hasattr(klass, '_fields') and name in klass._fields):
            return True 
        return False 

I think this should be the default behavior.

I tested it with a namedtuple having code like this:

class SomeClass(NamedTuple):
    """A docstring.
    """
    
    foo: str = 'text'
    """This is a docstring of foo 
    """
    

and it still preserves the docstring of foo in the output:

image

would you like to have a PR?

fohrloop avatar Feb 14 '24 22:02 fohrloop

That looks like a clear improvement to me; a PR would be welcome!

stefanv avatar Feb 14 '24 22:02 stefanv

There you are :)

fohrloop avatar Feb 14 '24 22:02 fohrloop