13

I am trying to document a Python project with Sphinx, but I'm having trouble combining the autodoc extension with a namedtuple generated class.

In one document, gammatone.rst, I have:

:mod:`gammatone` -- gammatone filterbank toolkit
================================================

.. automodule:: gammatone
   :members:
.. automodule:: gammatone.coeffs
   :members:

In my gammatone/coeffs.py, I have:

from collections import namedtuple

ERBFilterCoeffs = namedtuple(
    'ERBFilterCoeffs', # Most parameters omitted
    [
        'A0',
        'gain',
    ])

The code generated by namedtuple includes very generic docstrings that Sphinx's autodoc module picks up and includes. I'd rather document the class properly myself, without forgoing autodoc for the rest of the module.

I've tried putting something like this just before the class:

"""
.. class:: ERBFilterCoeffs(A0, gain)
:param A0: A0 coefficient
:param gain: Gain coefficient

Magic coefficients.
"""

...but it doesn't show up in the generated docs. Putting it after the class results in it being nested underneath the generic class documentation, rather than replacing it.

How do I simply tell Sphinx (and the autodoc extension) to use my documentation for the ERBFilterCoeffs class instead of that generated by namedtuple?

bad_coder
  • 11,289
  • 20
  • 44
  • 72
detly
  • 29,332
  • 18
  • 93
  • 152

3 Answers3

13

You don't actually need to extend the namedtuple at all. You can put the docstring after the namedtuple. This actually works for constants and attributes as well.

ERBFilterCoeffs = namedtuple('ERBFilterCoeffs', ['A0', 'gain', ])
""" Magic coefficients.

.. py:attribute:: A0

    The A0 attribute is something

.. py:attribute:: gain

    The gain attribute is blah blah

"""
dnephin
  • 25,944
  • 9
  • 55
  • 45
  • 4
    In my case, "putting it after the class results in it being nested underneath the generic class documentation, rather than replacing it." – detly Dec 13 '13 at 23:30
  • This won't work. Here you create a namedtuple assigned to variable `ERBFilterCoeffs` and then a define a multi-line string in vacuum. – Alex Conrad Apr 16 '15 at 17:49
  • 2
    @aconrad this is how sphinx documents constants, it works for sphinx, but maybe not for `ERBFilterCoeffs.__doc__` – dnephin Apr 17 '15 at 18:51
  • You can leave out `:members:` in the `autoclass` directive. The only problem is that you can't rely on `:members:` to `automodule` in that case, and you have to list the module contents manually. – Mad Physicist Dec 28 '17 at 21:35
  • I tried this method, and it almost works for me, but it prefixes the attribute names with the name of the module. I can't use the global setting `add_module_names = False`, because I want them elsewhere. And I can't seem to annotate the `some_name` in `:py:attribute:: some_name` with tilde `~` or similar: the annotations are just included literally. Is there a way around this? – Dan Halbert Jan 28 '20 at 17:16
8

How about after defining ERBFilterCoeffs with the namedtuple, try assigning that doc string to ERBFilterCoeffs.__doc__?

EDIT: Ok, how about this then:

class ERBFilterCoeffs(namedtuple('ERBFilterCoeffs','a b c')):
    """
    this is the doc string for ERBFilterCoeffs
    """
PaulMcG
  • 62,419
  • 16
  • 94
  • 130
  • I get: `WARNING: autodoc can't import/find module 'gammatone.erb', it reported error: "attribute '__doc__' of 'type' objects is not writable", please check your spelling and sys.path` — so presumably I can't overwrite the docstring of the `namedtuple` generated type :/ – detly Dec 09 '12 at 06:19
  • As a side-effect, this will show itself as ``base`` which is a bit paradoxical... Other than that, the docs turn out fine :) – exhuma Sep 12 '14 at 13:29
-1

In general, I prefer to have finer control over what is generated than adding a :members: directive to automodule gives. As such, I would recommend documenting ERBFilterCoeffs explicitly using .. autoclass:: ERBFilterCoeffs. I would not add a :members: directive here, since that will include the very generic default docs a namedtuple creates for each field. Instead, I would use .. py:attribute:: ... elements in your docstring, which you can put before the class definition using the special #: comments:

#: Magic coefficients.
#:
#: .. py:attirbute:: A0
#:
#:    A0 coefficient
#:
#: .. py:attribute:: gain
#:
#:    Gain coefficient
ERBFilterCoeffs = namedtuple(
    'ERBFilterCoeffs', [# Most parameters omitted
        'A0',
        'gain',
    ]
)
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264