Close #744: autodoc: Support abstractmethod

This commit is contained in:
Takeshi KOMIYA 2019-05-14 23:45:49 +09:00
parent 358e582490
commit e288999933
5 changed files with 94 additions and 4 deletions

View File

@ -81,6 +81,7 @@ Features added
* #6289: autodoc: :confval:`autodoc_default_options` now supports
``imported-members`` option
* #4777: autodoc: Support coroutine
* #744: autodoc: Support abstractmethod
* #6212 autosummary: Add :confval:`autosummary_imported_members` to display
imported members on autosummary
* #6271: ``make clean`` is catastrophically broken if building into '.'

View File

@ -1338,6 +1338,8 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
sourcename = self.get_sourcename()
obj = self.parent.__dict__.get(self.object_name, self.object)
if inspect.isabstractmethod(obj):
self.add_line(' :abstractmethod:', sourcename)
if inspect.iscoroutinefunction(obj):
self.add_line(' :async:', sourcename)
if inspect.isclassmethod(obj):
@ -1455,7 +1457,10 @@ class PropertyDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): #
def add_directive_header(self, sig):
# type: (str) -> None
super().add_directive_header(sig)
self.add_line(' :property:', self.get_sourcename())
sourcename = self.get_sourcename()
if inspect.isabstractmethod(self.object):
self.add_line(' :abstractmethod:', sourcename)
self.add_line(' :property:', sourcename)
class InstanceAttributeDocumenter(AttributeDocumenter):

View File

@ -172,6 +172,12 @@ def isdescriptor(x):
return False
def isabstractmethod(obj):
# type: (Any) -> bool
"""Check if the object is an abstractmethod."""
return safe_getattr(obj, '__isabstractmethod__', False) is True
def isattributedescriptor(obj):
# type: (Any) -> bool
"""Check if the object is an attribute like descriptor."""
@ -231,7 +237,7 @@ def isproperty(obj):
def safe_getattr(obj, name, *defargs):
# type: (Any, str, str) -> object
# type: (Any, str, Any) -> Any
"""A getattr() that turns all exceptions into AttributeErrors."""
try:
return getattr(obj, name, *defargs)
@ -319,9 +325,9 @@ def is_builtin_class_method(obj, attr_name):
classes = [c for c in inspect.getmro(obj) if attr_name in c.__dict__]
cls = classes[0] if classes else object
if not hasattr(builtins, safe_getattr(cls, '__name__', '')): # type: ignore
if not hasattr(builtins, safe_getattr(cls, '__name__', '')):
return False
return getattr(builtins, safe_getattr(cls, '__name__', '')) is cls # type: ignore
return getattr(builtins, safe_getattr(cls, '__name__', '')) is cls
class Parameter:

View File

@ -0,0 +1,29 @@
from abc import abstractmethod
class Base():
def meth(self):
pass
@abstractmethod
def abstractmeth(self):
pass
@staticmethod
@abstractmethod
def staticmeth():
pass
@classmethod
@abstractmethod
def classmeth(cls):
pass
@property
@abstractmethod
def prop(self):
pass
@abstractmethod
async def coroutinemeth(self):
pass

View File

@ -1485,6 +1485,55 @@ def test_mocked_module_imports(app, warning):
assert warning.getvalue() == ''
@pytest.mark.usefixtures('setup_test')
def test_abstractmethods():
options = {"members": None,
"undoc-members": None}
actual = do_autodoc(app, 'module', 'target.abstractmethods', options)
assert list(actual) == [
'',
'.. py:module:: target.abstractmethods',
'',
'',
'.. py:class:: Base',
' :module: target.abstractmethods',
'',
' ',
' .. py:method:: Base.abstractmeth()',
' :module: target.abstractmethods',
' :abstractmethod:',
' ',
' ',
' .. py:method:: Base.classmeth()',
' :module: target.abstractmethods',
' :abstractmethod:',
' :classmethod:',
' ',
' ',
' .. py:method:: Base.coroutinemeth()',
' :module: target.abstractmethods',
' :abstractmethod:',
' :async:',
' ',
' ',
' .. py:method:: Base.meth()',
' :module: target.abstractmethods',
' ',
' ',
' .. py:method:: Base.prop',
' :module: target.abstractmethods',
' :abstractmethod:',
' :property:',
' ',
' ',
' .. py:method:: Base.staticmeth()',
' :module: target.abstractmethods',
' :abstractmethod:',
' :staticmethod:',
' '
]
@pytest.mark.usefixtures('setup_test')
def test_partialfunction():
options = {"members": None}