Fix #7189: autodoc: classmethod coroutines are not detected

This commit is contained in:
Takeshi KOMIYA 2020-02-22 00:28:35 +09:00
parent 130a0a7f38
commit 2fec37219f
5 changed files with 55 additions and 5 deletions

View File

@ -18,6 +18,7 @@ Bugs fixed
* #7184: autodoc: ``*args`` and ``**kwarg`` in type comments are not handled
properly
* #7189: autodoc: classmethod coroutines are not detected
Testing
--------

View File

@ -111,6 +111,19 @@ def getargspec(func):
kwonlyargs, kwdefaults, annotations)
def unwrap(obj: Any) -> Any:
"""Get an original object from wrapped object."""
while True:
if ispartial(obj):
obj = unpartial(obj)
elif isclassmethod(obj):
obj = obj.__func__
elif isstaticmethod(obj):
obj = obj.__func__
else:
return obj
def isenumclass(x: Any) -> bool:
"""Check if the object is subclass of enum."""
return inspect.isclass(x) and issubclass(x, enum.Enum)
@ -141,7 +154,7 @@ def isclassmethod(obj: Any) -> bool:
"""Check if the object is classmethod."""
if isinstance(obj, classmethod):
return True
elif inspect.ismethod(obj) and obj.__self__ is not None:
elif inspect.ismethod(obj) and obj.__self__ is not None and isclass(obj.__self__):
return True
return False
@ -208,17 +221,17 @@ def isattributedescriptor(obj: Any) -> bool:
def isfunction(obj: Any) -> bool:
"""Check if the object is function."""
return inspect.isfunction(unpartial(obj))
return inspect.isfunction(unwrap(obj))
def isbuiltin(obj: Any) -> bool:
"""Check if the object is builtin."""
return inspect.isbuiltin(unpartial(obj))
return inspect.isbuiltin(unwrap(obj))
def iscoroutinefunction(obj: Any) -> bool:
"""Check if the object is coroutine-function."""
obj = unpartial(obj)
obj = unwrap(obj)
if hasattr(obj, '__code__') and inspect.iscoroutinefunction(obj):
# check obj.__code__ because iscoroutinefunction() crashes for custom method-like
# objects (see https://github.com/sphinx-doc/sphinx/issues/6605)

View File

@ -3,6 +3,16 @@ class AsyncClass:
"""A documented coroutine function"""
attr_coro_result = await _other_coro_func() # NOQA
@classmethod
async def do_coroutine2(cls):
"""A documented coroutine classmethod"""
pass
@staticmethod
async def do_coroutine3():
"""A documented coroutine staticmethod"""
pass
async def _other_coro_func():
return "run"

View File

@ -1319,7 +1319,23 @@ def test_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

@ -419,6 +419,16 @@ def test_dict_customtype():
assert "<CustomType(2)>: 2" in description
@pytest.mark.sphinx(testroot='ext-autodoc')
def test_isclassmethod(app):
from target.methods import Base, Inherited
assert inspect.isclassmethod(Base.classmeth) is True
assert inspect.isclassmethod(Base.meth) is False
assert inspect.isclassmethod(Inherited.classmeth) is True
assert inspect.isclassmethod(Inherited.meth) is False
@pytest.mark.sphinx(testroot='ext-autodoc')
def test_isstaticmethod(app):
from target.methods import Base, Inherited