Skip to content

Commit

Permalink
Merge pull request #9640 from tk0miya/9639_support_asyncgenfunction
Browse files Browse the repository at this point in the history
Close #9639: autodoc: Support asynchronous generator functions
  • Loading branch information
tk0miya committed Sep 17, 2021
2 parents bebd6bd + ccfca45 commit 506590d
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 56 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Expand Up @@ -13,6 +13,8 @@ Deprecated
Features added
--------------

* #9639: autodoc: Support asynchronous generator functions

Bugs fixed
----------

Expand Down
4 changes: 2 additions & 2 deletions sphinx/ext/autodoc/__init__.py
Expand Up @@ -1318,7 +1318,7 @@ def add_directive_header(self, sig: str) -> None:
sourcename = self.get_sourcename()
super().add_directive_header(sig)

if inspect.iscoroutinefunction(self.object):
if inspect.iscoroutinefunction(self.object) or inspect.isasyncgenfunction(self.object):
self.add_line(' :async:', sourcename)

def format_signature(self, **kwargs: Any) -> str:
Expand Down Expand Up @@ -2137,7 +2137,7 @@ def add_directive_header(self, sig: str) -> None:
obj = self.parent.__dict__.get(self.object_name, self.object)
if inspect.isabstractmethod(obj):
self.add_line(' :abstractmethod:', sourcename)
if inspect.iscoroutinefunction(obj):
if inspect.iscoroutinefunction(obj) or inspect.isasyncgenfunction(obj):
self.add_line(' :async:', sourcename)
if inspect.isclassmethod(obj):
self.add_line(' :classmethod:', sourcename)
Expand Down
3 changes: 2 additions & 1 deletion sphinx/util/inspect.py
Expand Up @@ -19,7 +19,8 @@
import warnings
from functools import partial, partialmethod
from importlib import import_module
from inspect import Parameter, isclass, ismethod, ismethoddescriptor, ismodule # NOQA
from inspect import (Parameter, isasyncgenfunction, isclass, ismethod, # NOQA
ismethoddescriptor, ismodule)
from io import StringIO
from types import ModuleType
from typing import Any, Callable, Dict, Mapping, Optional, Sequence, Tuple, Type, cast
Expand Down
4 changes: 4 additions & 0 deletions tests/roots/test-ext-autodoc/target/coroutine.py
Expand Up @@ -17,6 +17,10 @@ async def do_coroutine3():
"""A documented coroutine staticmethod"""
pass

async def do_asyncgen(self):
"""A documented async generator"""
yield


async def _other_coro_func():
return "run"
Expand Down
4 changes: 4 additions & 0 deletions tests/roots/test-ext-autodoc/target/functions.py
Expand Up @@ -8,6 +8,10 @@ def func():
async def coroutinefunc():
pass


async def asyncgenerator():
yield

partial_func = partial(func)
partial_coroutinefunc = partial(coroutinefunc)

Expand Down
53 changes: 0 additions & 53 deletions tests/test_ext_autodoc.py
Expand Up @@ -1619,59 +1619,6 @@ def test_bound_method(app):
]


@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_coroutine(app):
actual = do_autodoc(app, 'function', 'target.functions.coroutinefunc')
assert list(actual) == [
'',
'.. py:function:: coroutinefunc()',
' :module: target.functions',
' :async:',
'',
]

options = {"members": None}
actual = do_autodoc(app, 'class', 'target.coroutine.AsyncClass', options)
assert list(actual) == [
'',
'.. py:class:: AsyncClass()',
' :module: target.coroutine',
'',
'',
' .. py:method:: AsyncClass.do_coroutine()',
' :module: target.coroutine',
' :async:',
'',
' A documented coroutine function',
'',
'',
' .. py:method:: AsyncClass.do_coroutine2()',
' :module: target.coroutine',
' :async:',
' :classmethod:',
'',
' A documented coroutine classmethod',
'',
'',
' .. py:method:: AsyncClass.do_coroutine3()',
' :module: target.coroutine',
' :async:',
' :staticmethod:',
'',
' A documented coroutine staticmethod',
'',
]

# force-synchronized wrapper
actual = do_autodoc(app, 'function', 'target.coroutine.sync_func')
assert list(actual) == [
'',
'.. py:function:: sync_func()',
' :module: target.coroutine',
'',
]


@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_partialmethod(app):
expected = [
Expand Down
42 changes: 42 additions & 0 deletions tests/test_ext_autodoc_autoclass.py
Expand Up @@ -389,3 +389,45 @@ def test_class_alias_having_doccomment(app):
' docstring',
'',
]


@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_coroutine(app):
options = {"members": None}
actual = do_autodoc(app, 'class', 'target.coroutine.AsyncClass', options)
assert list(actual) == [
'',
'.. py:class:: AsyncClass()',
' :module: target.coroutine',
'',
'',
' .. py:method:: AsyncClass.do_asyncgen()',
' :module: target.coroutine',
' :async:',
'',
' A documented async generator',
'',
'',
' .. py:method:: AsyncClass.do_coroutine()',
' :module: target.coroutine',
' :async:',
'',
' A documented coroutine function',
'',
'',
' .. py:method:: AsyncClass.do_coroutine2()',
' :module: target.coroutine',
' :async:',
' :classmethod:',
'',
' A documented coroutine classmethod',
'',
'',
' .. py:method:: AsyncClass.do_coroutine3()',
' :module: target.coroutine',
' :async:',
' :staticmethod:',
'',
' A documented coroutine staticmethod',
'',
]
35 changes: 35 additions & 0 deletions tests/test_ext_autodoc_autofunction.py
Expand Up @@ -168,3 +168,38 @@ def test_wrapped_function_contextmanager(app):
" You'll feel better in this context!",
'',
]


@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_coroutine(app):
actual = do_autodoc(app, 'function', 'target.functions.coroutinefunc')
assert list(actual) == [
'',
'.. py:function:: coroutinefunc()',
' :module: target.functions',
' :async:',
'',
]


@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_synchronized_coroutine(app):
actual = do_autodoc(app, 'function', 'target.coroutine.sync_func')
assert list(actual) == [
'',
'.. py:function:: sync_func()',
' :module: target.coroutine',
'',
]


@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_async_generator(app):
actual = do_autodoc(app, 'function', 'target.functions.asyncgenerator')
assert list(actual) == [
'',
'.. py:function:: asyncgenerator()',
' :module: target.functions',
' :async:',
'',
]

0 comments on commit 506590d

Please sign in to comment.