Alex Vorndran Alex Vorndran - 18 days ago 4
Python Question

Python-Sphinx: "inherit" method documentation from superclass

Edit:
As of now (Sphinx 1.4.9) there seems to be no way to tell Sphinx to do what I want (see issue on GitHub). The accepted answer from Brecht Machiels solves the problem in an other way, until Sphinx might be able to do so one day.

Description:
I am trying to document a Python project with sphinx-apidoc. The Sphinx config is almost default, I just included

'sphinx.ext.autodoc'
.

It works in general, but derived classes do not inherit method documentation of their superclasses as I would expect it.

Example:
Consider a very minimalistic Python package called
project
. Aside an empty
__init__.py
it only consists of one file (
base.py
, see below)

# -*- coding: utf-8 -*
import abc


class Superclass(object):
"""The one to rule them all"""

@abc.abstractmethod
def give(self, ring):
"""Give out a ring"""
pass


class Derived(Superclass):
"""Somebody has to do the work"""

def give(self, ring):
print("I pass the ring {} to you".format(ring))


Running sphinx-apidoc (
sphinx-apidoc -o apidoc project -f
) generates the following files:


  • apidoc/modules.rst


    project
    =======

    .. toctree::
    :maxdepth: 4

    project

  • apidoc/project.rst


    project package
    ===============

    Submodules
    ----------

    project.base module
    -------------------

    .. automodule:: project.base
    :members:
    :undoc-members:
    :show-inheritance:


    Module contents
    ---------------

    .. automodule:: project
    :members:
    :undoc-members:
    :show-inheritance:



Including
apidoc/modules.rst
in the default
index.rst
followed by
make html
generates a basic html documentation for both classes and their methods. Unfortunately, the docstring of
Derived.give
is empty.

Question:
Is there a way to tell Sphinx to take the parent's method documentation without decorator magic as described in this SO post for every single method?

Answer

You can manage docstrings automatically by employing a metaclass for the abstract base class. The following is a very basic implementation of such a metaclass. It needs to be extended to properly handle multiple base classes and corner cases.

# -*- coding: utf-8 -*
import abc


class SuperclassMeta(type):
    def __new__(mcls, classname, bases, cls_dict):
        cls = super().__new__(mcls, classname, bases, cls_dict)
        for name, member in cls_dict.items():
            if not getattr(member, '__doc__'):
                member.__doc__ = getattr(bases[-1], name).__doc__
        return cls


class Superclass(object, metaclass=SuperclassMeta):
    """The one to rule them all"""

    @abc.abstractmethod
    def give(self, ring):
        """Give out a ring"""
        pass


class Derived(Superclass):
    """Somebody has to do the work"""

    def give(self, ring):
        print("I pass the ring {} to you".format(ring))

This is even a better solution than having Sphinx do this, because this will also work when calling help() on the derived classes.

Comments