Fix #7023: autodoc: nested partial functions are not listed

This commit is contained in:
Takeshi KOMIYA 2020-01-17 09:13:41 +09:00
parent ef811532c9
commit e908e43f67
5 changed files with 40 additions and 12 deletions

View File

@ -51,6 +51,7 @@ Bugs fixed
* #6559: Wrong node-ids are generated in glossary directive
* #6986: apidoc: misdetects module name for .so file inside module
* #6999: napoleon: fails to parse tilde in :exc: role
* #7023: autodoc: nested partial functions are not listed
Testing
--------

View File

@ -121,6 +121,17 @@ def isenumattribute(x: Any) -> bool:
return isinstance(x, enum.Enum)
def unpartial(obj: Any) -> Any:
"""Get an original object from partial object.
This returns given object itself if not partial.
"""
while ispartial(obj):
obj = obj.func
return obj
def ispartial(obj: Any) -> bool:
"""Check if the object is partial."""
return isinstance(obj, (partial, partialmethod))
@ -197,24 +208,21 @@ def isattributedescriptor(obj: Any) -> bool:
def isfunction(obj: Any) -> bool:
"""Check if the object is function."""
return inspect.isfunction(obj) or ispartial(obj) and inspect.isfunction(obj.func)
return inspect.isfunction(unpartial(obj))
def isbuiltin(obj: Any) -> bool:
"""Check if the object is builtin."""
return inspect.isbuiltin(obj) or ispartial(obj) and inspect.isbuiltin(obj.func)
return inspect.isbuiltin(unpartial(obj))
def iscoroutinefunction(obj: Any) -> bool:
"""Check if the object is coroutine-function."""
obj = unpartial(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)
return True
elif (ispartial(obj) and hasattr(obj.func, '__code__') and
inspect.iscoroutinefunction(obj.func)):
# partialed
return True
else:
return False

View File

@ -1,11 +1,12 @@
from functools import partial
def func1():
def func1(a, b, c):
"""docstring of func1"""
pass
func2 = partial(func1)
func3 = partial(func1)
func2 = partial(func1, 1)
func3 = partial(func2, 2)
func3.__doc__ = "docstring of func3"
func4 = partial(func3, 3)

View File

@ -1241,19 +1241,25 @@ def test_partialfunction():
'.. py:module:: target.partialfunction',
'',
'',
'.. py:function:: func1()',
'.. py:function:: func1(a, b, c)',
' :module: target.partialfunction',
'',
' docstring of func1',
' ',
'',
'.. py:function:: func2()',
'.. py:function:: func2(b, c)',
' :module: target.partialfunction',
'',
' docstring of func1',
' ',
'',
'.. py:function:: func3()',
'.. py:function:: func3(c)',
' :module: target.partialfunction',
'',
' docstring of func3',
' ',
'',
'.. py:function:: func4()',
' :module: target.partialfunction',
'',
' docstring of func3',

View File

@ -511,3 +511,15 @@ def test_isproperty(app):
assert inspect.isproperty(Base.meth) is False # method of class
assert inspect.isproperty(Base().meth) is False # method of instance
assert inspect.isproperty(func) is False # function
def test_unpartial():
def func1(a, b, c):
pass
func2 = functools.partial(func1, 1)
func2.__doc__ = "func2"
func3 = functools.partial(func2, 2) # nested partial object
assert inspect.unpartial(func2) is func1
assert inspect.unpartial(func3) is func1