Search code examples
pythoninheritancepython-sphinxrestructuredtext

Sphinx - insert argument documentation from parent method


I have some classes that inherit from each other. All classes contain the same method (let us call it mymethod), whereby the children overwrite the base class method. I want to generate a documentation for mymethod in all classes using sphinx.

Suppose mymethod takes an argument myargument. This argument has the same type and meaning for both the base method as well as the inherited method. To minimize redundancies, I would like to write the documentation for myargument only for the base class and insert the documentation in the child method's documentation. That is, I do not want to only put a simple reference to the base class but rather dynamically insert the text when I generate the documentation.

Can this be done? How?

Below please find some code illustrating the problem.

class BaseClass
    def mymethod(myargument):
        """This does something

        Params
        ------
        myargument : int
            Description of the argument

        """
        [...]


class MyClass1(BaseClass):
    def mymethod(myargument):
        """This does something

        Params
        ------
        [here I would like to insert in the description of ``myargument`` from ``BaseClass.mymethod``]
        """

        BaseClass.mymethod(myargument)
        [...]

class MyClass2(BaseClass):
    def mymethod(myargument, argument2):
        """This does something

        Params
        ------
        [here I would like to insert in the description of ``myargument`` in ``BaseClass.mymethod``]
        argument2 : int
            Description of the additional argument

        """

        BaseClass.mymethod(argument)
        [...]



Solution

  • Probably not ideal, but maybe you could use a decorator to extend the docstring. For example:

    class extend_docstring:
        def __init__(self, method):
            self.doc = method.__doc__
    
        def __call__(self, function):
            if self.doc is not None:
                doc = function.__doc__
                function.__doc__ = self.doc
                if doc is not None:
                    function.__doc__ += doc
            return function
    
    
    class BaseClass:
        def mymethod(myargument):
            """This does something
    
            Params
            ------
            myargument : int
                Description of the argument
            """
            [...]
    
    
    class MyClass1(BaseClass):
        @extend_docstring(BaseClass.mymethod)
        def mymethod(myargument):
            BaseClass.mymethod(myargument)
            [...]
    
    class MyClass2(BaseClass):
        @extend_docstring(MyClass1.mymethod)
        def mymethod(myargument, argument2):
            """argument2 : int
                Description of the additional argument
            """
    
            BaseClass.mymethod(argument)
            [...]
    
    
    print('---BaseClass.mymethod---')
    print(BaseClass.mymethod.__doc__)
    print('---MyClass1.mymethod---')
    print(MyClass1.mymethod.__doc__)
    print('---MyClass2.mymethod---')
    print(MyClass2.mymethod.__doc__)
    

    Result:

    ---BaseClass.mymethod---
    This does something
    
            Params
            ------
            myargument : int
                Description of the argument
    
    ---MyClass1.mymethod---
    This does something
    
            Params
            ------
            myargument : int
                Description of the argument
    
    ---MyClass2.mymethod---
    This does something
    
            Params
            ------
            myargument : int
                Description of the argument
            argument2 : int
                Description of the additional argument
    

    The override method could be resolved dynamically if you make the decorator a descriptor and search for it into __get__ but that means the decorator is no longer stackable as it doesn't return the real function.