Sphinx: Doc in decorated function not generated

Created on 22 May 2017  路  9Comments  路  Source: sphinx-doc/sphinx

Problem

  • Function below, doc isn't generated in with parameters HTML, only function signature is generated.

Procedure to reproduce the problem

@some_decorator
def some_func(param1, param2):
     """An awesome function
     :param param1: a param
     :param param2: another param
     """
     do_something

Error logs / results

some_func(param1, param2)

Expected results

some_func(param1, param2)
  An awesome function
  Parameters: param1 a param
              param2 another param

Environment info

  • OS: Mac
  • Python version: 2.7.13
  • Sphinx version: 1.6.1
autodoc question

Most helpful comment

It's a known side-effect that decorators 'mask' the docstrings of the wrapped functions. This interferes with Sphinx's ability to discover documentation there.

The first workaround to try is to use the functools.wraps decorator, one action of which is to 'replicate' the docstring of the decorated function into the final, wrapped object. I've used this approach in one of my projects; e.g., the function opan.utils.vector.ortho_basis is wrapped by my custom decorator opan.utils.decorate.arraysqueeze. I used functools.wraps on line 87 for exactly this purpose of docstring replication. As you can see, the docstring is parsed properly by Sphinx in the generated documentation.

One thing that wraps can't (or couldn't previously) do, though, is report the correct function signature to, e.g., .. autofunction::. I had to enter the function signature manually when I composed the module-level docstring.

All 9 comments

It's a known side-effect that decorators 'mask' the docstrings of the wrapped functions. This interferes with Sphinx's ability to discover documentation there.

The first workaround to try is to use the functools.wraps decorator, one action of which is to 'replicate' the docstring of the decorated function into the final, wrapped object. I've used this approach in one of my projects; e.g., the function opan.utils.vector.ortho_basis is wrapped by my custom decorator opan.utils.decorate.arraysqueeze. I used functools.wraps on line 87 for exactly this purpose of docstring replication. As you can see, the docstring is parsed properly by Sphinx in the generated documentation.

One thing that wraps can't (or couldn't previously) do, though, is report the correct function signature to, e.g., .. autofunction::. I had to enter the function signature manually when I composed the module-level docstring.

I see,Thanks @bskinn, it works.

autodoc module simply refers the docstrings of the targets. in this case, functools.wraps helps you.
I think this is not a bug. Can I close this?

Yes @tk0miya, please close it.

I know this issue has been closed, but I think it would be very useful for sphinx to be able to inspect numba-decorated functions. Would it be acceptable to add code (an extension?) which does something like: if an object obj has type numba.targets.registry.CPUDispatcher, then recognize it as a function (autodoc currently does not); and then instead document obj.py_func, which is the original undecorated python function?

PS Part of this can be implemented using the autodoc-process-signature trigger. Here is an example for what to add to conf.py so that numba-decorated functions have their correct signatures emitted: https://github.com/duetosymmetry/qnm/blob/d286cad616a4abe5ff3b4e05adbfb4b0e305583e/docs/conf.py#L71-L93 (please note that I should have named the function process_numba_signature instead of process_numba_docstring)

@duetosymmetry I think Sphinx should not be add special code for 3rd party libraries. So it should be better to handle in an extension. And it seems you've already handled it on autodoc-process-signature. Is there something needed else?
If so, please file a new issue. This is different topic from this issue.

@tk0miya I think the only thing missing is that autosummary does not recognize decorated functions as functions, since it sees them as objects. This makes autodoc less automatic鈥攎y workaround is I have to include a list of decorated functions in docs/_autosummary/modulename.rst. I could not find a trigger that allows me to assign what='function' for the relevant decorated functions. Perhaps this is the actual issue (and needs to be opened separately, if it is useful for others).

When 4 years we had met this problem we fixed with monkey-patching:
in conf.py:

def setup(app):
    """Customize function args retrieving to get args under decorator."""
    from functools import partial
    from sphinx.ext import autodoc
    from stepler.third_party.utils import get_unwrapped_func

    orig_getargspec = autodoc.getargspec

    def patched_getargspec(func):
        if type(func) is not partial:
            func = get_unwrapped_func(func)
        return orig_getargspec(func)

    autodoc.getargspec = patched_getargspec

where get_unwrapped_func:

def get_unwrapped_func(func):
    """Get original function under decorator.
    Decorator hides original function inside itself. But in some cases it's
    important to get access to original function, for ex: for documentation.
    Args:
        func (function): function that can be potentially a decorator which
            hides original function
    Returns:
        function: unwrapped function or the same function
    """
    if not inspect.isfunction(func) and not inspect.ismethod(func):
        return func

    if func.__name__ != six.get_function_code(func).co_name:
        for cell in six.get_function_closure(func):
            obj = cell.cell_contents
            if inspect.isfunction(obj):
                if func.__name__ == six.get_function_code(obj).co_name:
                    return obj
                else:
                    return get_unwrapped_func(obj)
    return func

Code is 4 years old, can't say is it working now. Original example is in https://github.com/Mirantis/stepler/blob/master/doc/source/conf.py#L450

```

Was this page helpful?
0 / 5 - 0 ratings