4

I'm starting to document my first asyncio-based project with Sphinx. I noticed some projects have this "coroutine" prefix before some methods and I'd like to do the same in my project's documentation, but I can't figure out how.

For example, aiohttp's HTTP client reference shows this:

class aiohttp.ClientSession(...):

coroutine request(...)

That project seems to use a coroutinemethod directive to achieve this, but I document all my functions and classes inline using docstrings and this directive only works if you write the docs in a reStructuredText document.

Does anybody know how to achieve this result with autodoc?

Edit: I will also accept answers that explain how to make a Sphinx extension to do this if Sphinx doesn't support it. Bonus points if someone can point my to some kind of way that automatically detects whether the method is a coroutine or not using inspect.iscoroutinefunction().

Edit: I'm looking at the "pyspecific" Sphinx extension in the CPython project for inspiration. However, I need to change autodoc's behavior, not add new directives. After a bit of research, it looks like autodoc has an autodoc-process-signature event which can be used to customize the function signature, but it doesn't seem to have the object used by the "pyspecific" extension.

0

1 Answer 1

3

This functionality is not already built-in to Sphinx. However, pull request #1826 adds support for generators, coroutine functions, coroutine methods with built-in detection of coroutines in autodoc's autofunction and automethod directives.

Here is a patch that I applied locally to enable this in Sphinx 1.4 (requires disabling sphinx-build's -W "turn warnings into errors" option):

# Recipe stolen from open PR (https://github.com/sphinx-doc/sphinx/pull/1826).


from inspect import iscoroutinefunction

from sphinx import addnodes
from sphinx.domains.python import (
    PyClassmember,
    PyModulelevel,
)
from sphinx.ext.autodoc import FunctionDocumenter as _FunctionDocumenter
from sphinx.ext.autodoc import MethodDocumenter as _MethodDocumenter


class PyCoroutineMixin(object):
    """Helper for coroutine-related Sphinx custom directives."""

    def handle_signature(self, sig, signode):
        ret = super(PyCoroutineMixin, self).handle_signature(sig, signode)
        signode.insert(0, addnodes.desc_annotation('coroutine ', 'coroutine '))
        return ret


class PyCoroutineFunction(PyCoroutineMixin, PyModulelevel):
    """Sphinx directive for coroutine functions."""

    def run(self):
        self.name = 'py:function'
        return PyModulelevel.run(self)


class PyCoroutineMethod(PyCoroutineMixin, PyClassmember):
    """Sphinx directive for coroutine methods."""

    def run(self):
        self.name = 'py:method'
        return PyClassmember.run(self)


class FunctionDocumenter(_FunctionDocumenter):
    """Automatically detect coroutine functions."""

    def import_object(self):
        ret = _FunctionDocumenter.import_object(self)
        if not ret:
            return ret

        obj = self.parent.__dict__.get(self.object_name)
        if iscoroutinefunction(obj):
            self.directivetype = 'coroutine'
            self.member_order = _FunctionDocumenter.member_order + 2
        return ret


class MethodDocumenter(_MethodDocumenter):
    """Automatically detect coroutine methods."""

    def import_object(self):
        ret = _MethodDocumenter.import_object(self)
        if not ret:
            return ret

        obj = self.parent.__dict__.get(self.object_name)
        if iscoroutinefunction(obj):
            self.directivetype = 'coroutinemethod'
            self.member_order = _MethodDocumenter.member_order + 2
        return ret


def setup(app):
    """Sphinx extension entry point."""

    # Add new directives.
    app.add_directive_to_domain('py', 'coroutine', PyCoroutineFunction)
    app.add_directive_to_domain('py', 'coroutinemethod', PyCoroutineMethod)

    # Customize annotations for anything that looks like a coroutine.
    app.add_autodocumenter(FunctionDocumenter)
    app.add_autodocumenter(MethodDocumenter)

    # Return extension meta data.
    return {
        'version': '1.0',
        'parallel_read_safe': True,
    }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.