Support singledispatch in autofunction/methodThis makes autofunction and automethod work standalone withsingledispatch functions and methods. Previously only automodule wouldsupport these

This commit is contained in:
Tom Cobb 2020-04-24 15:15:22 +01:00
parent 2fac698e76
commit 22fafb9773
2 changed files with 86 additions and 43 deletions

View File

@ -1062,30 +1062,15 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
def add_directive_header(self, sig: str) -> None:
sourcename = self.get_sourcename()
super().add_directive_header(sig)
if inspect.is_singledispatch_function(self.object):
self.add_singledispatch_directive_header(sig)
else:
super().add_directive_header(sig)
if inspect.iscoroutinefunction(self.object):
self.add_line(' :async:', sourcename)
class SingledispatchFunctionDocumenter(FunctionDocumenter):
"""
Specialized Documenter subclass for singledispatch'ed functions.
"""
objtype = 'singledispatch_function'
directivetype = 'function'
member_order = 30
# before FunctionDocumenter
priority = FunctionDocumenter.priority + 1
@classmethod
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
) -> bool:
return (super().can_document_member(member, membername, isattr, parent) and
inspect.is_singledispatch_function(member))
def add_directive_header(self, sig: str) -> None:
def add_singledispatch_directive_header(self, sig: str) -> None:
sourcename = self.get_sourcename()
# intercept generated directive headers
@ -1125,6 +1110,26 @@ class SingledispatchFunctionDocumenter(FunctionDocumenter):
func.__signature__ = sig.replace(parameters=params) # type: ignore
class SingledispatchFunctionDocumenter(FunctionDocumenter):
"""
Specialized Documenter subclass for singledispatch'ed functions.
Retained for backwards compatibility, does the same as the FunctionDocumenter
"""
objtype = 'singledispatch_function'
directivetype = 'function'
member_order = 30
# before FunctionDocumenter
priority = FunctionDocumenter.priority + 1
@classmethod
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
) -> bool:
return (super().can_document_member(member, membername, isattr, parent) and
inspect.is_singledispatch_function(member))
class DecoratorDocumenter(FunctionDocumenter):
"""
Specialized Documenter subclass for decorator functions.
@ -1455,7 +1460,11 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
return args
def add_directive_header(self, sig: str) -> None:
super().add_directive_header(sig)
meth = self.parent.__dict__.get(self.objpath[-1])
if inspect.is_singledispatch_method(meth):
self.add_singledispatch_directive_header(sig)
else:
super().add_directive_header(sig)
sourcename = self.get_sourcename()
obj = self.parent.__dict__.get(self.object_name, self.object)
@ -1471,28 +1480,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
def document_members(self, all_members: bool = False) -> None:
pass
class SingledispatchMethodDocumenter(MethodDocumenter):
"""
Specialized Documenter subclass for singledispatch'ed methods.
"""
objtype = 'singledispatch_method'
directivetype = 'method'
member_order = 50
# before MethodDocumenter
priority = MethodDocumenter.priority + 1
@classmethod
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
) -> bool:
if super().can_document_member(member, membername, isattr, parent) and parent.object:
meth = parent.object.__dict__.get(membername)
return inspect.is_singledispatch_method(meth)
else:
return False
def add_directive_header(self, sig: str) -> None:
def add_singledispatch_directive_header(self, sig: str) -> None:
sourcename = self.get_sourcename()
# intercept generated directive headers
@ -1533,6 +1521,29 @@ class SingledispatchMethodDocumenter(MethodDocumenter):
func.__signature__ = sig.replace(parameters=params) # type: ignore
class SingledispatchMethodDocumenter(MethodDocumenter):
"""
Specialized Documenter subclass for singledispatch'ed methods.
Retained for backwards compatibility, does the same as the MethodDocumenter
"""
objtype = 'singledispatch_method'
directivetype = 'method'
member_order = 50
# before MethodDocumenter
priority = MethodDocumenter.priority + 1
@classmethod
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
) -> bool:
if super().can_document_member(member, membername, isattr, parent) and parent.object:
meth = parent.object.__dict__.get(membername)
return inspect.is_singledispatch_method(meth)
else:
return False
class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # type: ignore
"""
Specialized Documenter subclass for attributes.

View File

@ -1604,6 +1604,21 @@ def test_singledispatch():
]
@pytest.mark.usefixtures('setup_test')
def test_singledispatch_autofunction():
options = {}
actual = do_autodoc(app, 'function', 'target.singledispatch.func', options)
assert list(actual) == [
'',
'.. py:function:: func(arg, kwarg=None)',
' func(arg: int, kwarg=None)',
' func(arg: str, kwarg=None)',
' :module: target.singledispatch',
'',
' A function for general use.',
'',
]
@pytest.mark.skipif(sys.version_info < (3, 8),
reason='singledispatchmethod is available since python3.8')
@pytest.mark.usefixtures('setup_test')
@ -1631,6 +1646,23 @@ def test_singledispatchmethod():
]
@pytest.mark.skipif(sys.version_info < (3, 8),
reason='singledispatchmethod is available since python3.8')
@pytest.mark.usefixtures('setup_test')
def test_singledispatchmethod_automethod():
options = {}
actual = do_autodoc(app, 'method', 'target.singledispatchmethod.Foo.meth', options)
assert list(actual) == [
'',
'.. py:method:: Foo.meth(arg, kwarg=None)',
' Foo.meth(arg: int, kwarg=None)',
' Foo.meth(arg: str, kwarg=None)',
' :module: target.singledispatchmethod',
'',
' A method for general use.',
'',
]
@pytest.mark.usefixtures('setup_test')
@pytest.mark.skipif(pyximport is None, reason='cython is not installed')
def test_cython():