Change docstring when inheriting but leave method the same
Asked Answered
P

2

6

I'm building an HTTP API and I factored out a lot of code into a superclass that handles requests to a collection of objects. In my subclass, I specify what database models the operation should work on and the superclass takes care of the rest.

This means that I don't need to re-implement the get, post, etc. methods from the superclass, however, I want to change their docstrings in the subclass so that I can have some documentation more specific to the actual model the endpoint is operating on.

What is the cleanest way to inherit the parent class's functionality but change the docstrings?

Example:

class CollectionApi(Resource):
    """Operate on a collection of something.
    """
    class Meta(object):
        model = None
        schema = None


    def get(self):
        """Return a list of collections.
        """
        # snip

    def post(self):
        """Create a new item in this collection.
        """
        # snip

class ActivityListApi(CollectionApi):
    """Operations on the collection of Activities.
    """
    class Meta(object):
        model = models.Activity
        schema = schemas.ActivitySchema

Specifically, I need ActivityListApi to have get and post run like in CollectionApi, but I want different docstrings (for automatic documentation's sake).

I can do this:

    def get(self):
        """More detailed docs
        """
        return super(ActivityListApi, self).get()

But this seems messy.

Purvis answered 16/8, 2016 at 1:12 Comment(8)
I don't understand it, if the methods do the same thing, why do they need different docstrings? Or, to put it differently, won't the base class users benefit from extended docstrings as well?Narcisanarcissism
Since this is an API, I thought it would be helpful for the child classes to have, e.g. example requests and responses. Since the specific request and response varies depending on exactly what kind of resource is being operated on, you can't have that kind of detail in the base class.Purvis
Add the detail to the class docstring.Transgress
That's an option, but there are two shortcomings. 1. This means I'm documenting as many as 4 methods in one docstring, which is not very user friendly. 2. I'm using a package that automatically documents http endpoints, so I won't be taking advantage of its capability if I stuff all the documentation into the class docstring.Purvis
How about a "private" _get that does the actual work, just on super? You then have 2 separate get that call it, but they each get their docstring. Sure, folks might wonder why this arrangement, but not not much more than a fancier solution.Geomancer
That's not a bad idea, marginally cleaner than my super. I'll see if anybody else has ideas.Purvis
@JLPeyret: if you post that as an answer, I'll be happy to accept itPurvis
@Purvis - cool, added.Geomancer
G
4
class CollectionApi(Resource):
    """Operate on a collection of something.
    """

    def _get(self):
        """actual work... lotsa techy doc here!

           the get methods only serve to have something to hang 
           their user docstrings onto
        """

        pass

    def get(self):
        """user-intended doc for CollectionApi"""
        return self._get()

class ActivityListApi(CollectionApi):

    def get(self):
        """user-intended doc for ActivityListApi"""
        return self._get()
Geomancer answered 18/8, 2016 at 3:33 Comment(0)
C
0

Better late than never. It is actually possible to change the doc while leaving the original method untouched by modifying __doc__ method attribute directly once the class has been declared.

With this approach, it avoids any overhead, and checks like CollectionApi.get is ActivityListApi.get will work as expected (ie return True unless inherited, even if the doc has been modified).

Here is what to do for your application:

class CollectionApi(Resource):
    """Operate on a collection of something.
    """
    class Meta(object):
        model = None
        schema = None


    def get(self):
        """Return a list of collections.
        """
        # snip

    def post(self):
        """Create a new item in this collection.
        """
        # snip

class ActivityListApi(CollectionApi):
    """Operations on the collection of Activities.
    """
    class Meta(object):
        model = models.Activity
        schema = schemas.ActivitySchema

ActivityListApi.get.__doc__ = \
    """user-intended doc for `get`.
    """

ActivityListApi.post.__doc__ = \
    """user-intended doc for `post`.
    """

At least in works nowadays in Python 3.6+. I did not check for Python 2.7 which has been deprecated long time ago.

Caveman answered 4/5 at 11:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.