Python Sphinx autodoc and decorated members

Viewed 12814

I am attempting to use Sphinx to document my Python class. I do so using autodoc:

.. autoclass:: Bus
   :members:

While it correctly fetches the docstrings for my methods, those that are decorated:

    @checkStale
    def open(self):
        """
        Some docs.
        """
        # Code

with @checkStale being

def checkStale(f):
    @wraps(f)
    def newf(self, *args, **kwargs):
        if self._stale:
            raise Exception
        return f(self, *args, **kwargs)
    return newf

have an incorrect prototype, such as open(*args, **kwargs).

How can I fix this? I was under the impression that using @wraps would fix up this kind of thing.

8 Answers

Add '.__ doc __':

def checkStale(f):
    @wraps(f)
    def newf(self, *args, **kwargs):
       if self._stale:
          raise Exception
       return f(self, *args, **kwargs)
    newf.__doc__ = f.__doc__
    return newf

And on decorated function add:

@checkStale
def open(self):
    """
    open()
    Some docs.
    """
    # Code

the answer to this is quite simple, but none of the threads I've seen have mentioned it. Have a look at functools.update_wrapper()

import functools

def schema_in(orig_func):
    schema = Schema() 
    def validate_args(*args, **kwargs):
        clean_kwargs = schema.load(**kwargs)
        return orig_func(**clean_kwargs)

    functools.update_wrapper(validate_args, orig_func)
    return validate_args
    

I'm not sure this will run, but it illustrates the concept. If your wrapper is injecting validated_args between the caller and the callee, the example shows how to update the wrapper (validated_args) method with the metadata of orig_method. Ultimately, this will allow Sphinx and other type analysis tools such as mypy (I'm assuming!) to see the data needed to behave as expected. I have just finished testing this and can confirm it works as described, Sphinx autodoc is behaving as desired.

I just found an easy solution which works for me, but don't ask me why. If you know why add it in the comments.

from functools import wraps 

def a_decorator(f):
    """A decorator 

    Args:
        f (function): the function to wrap
    """
    @wraps(f) # use this annotation on the wrapper works like a charm
    def wrapper(*args, **kwargs):
        some code
        return ret

return wrapper

The doc of the decorated function and of the decorator are both kept

Related