Merge pull request #9640 from tk0miya/9639_support_asyncgenfunction

Close #9639: autodoc: Support asynchronous generator functions
This commit is contained in:
Takeshi KOMIYA 2021-09-18 01:03:34 +09:00 committed by GitHub
commit 506590d4ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 91 additions and 56 deletions

View File

@ -13,6 +13,8 @@ Deprecated
Features added
--------------
* #9639: autodoc: Support asynchronous generator functions
Bugs fixed
----------

View File

@ -1318,7 +1318,7 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
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:
@ -2137,7 +2137,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
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)

View File

@ -19,7 +19,8 @@ import typing
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

View File

@ -17,6 +17,10 @@ class AsyncClass:
"""A documented coroutine staticmethod"""
pass
async def do_asyncgen(self):
"""A documented async generator"""
yield
async def _other_coro_func():
return "run"

View File

@ -8,6 +8,10 @@ def func():
async def coroutinefunc():
pass
async def asyncgenerator():
yield
partial_func = partial(func)
partial_coroutinefunc = partial(coroutinefunc)

View File

@ -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 = [

View File

@ -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',
'',
]

View File

@ -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:',
'',
]