mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '2.x' into 3.0.x
This commit is contained in:
commit
231d75b0d0
3
CHANGES
3
CHANGES
@ -199,6 +199,8 @@ Dependencies
|
|||||||
Incompatible changes
|
Incompatible changes
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
* #7222: ``sphinx.util.inspect.unwrap()`` is renamed to ``unwrap_all()``
|
||||||
|
|
||||||
Deprecated
|
Deprecated
|
||||||
----------
|
----------
|
||||||
|
|
||||||
@ -209,6 +211,7 @@ Bugs fixed
|
|||||||
----------
|
----------
|
||||||
|
|
||||||
* #7343: Sphinx builds has been slower since 2.4.0 on debug mode
|
* #7343: Sphinx builds has been slower since 2.4.0 on debug mode
|
||||||
|
* #7222: autodoc: ``__wrapped__`` functions are not documented correctly
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
--------
|
--------
|
||||||
|
@ -1011,41 +1011,42 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
|||||||
if self.env.config.autodoc_typehints in ('none', 'description'):
|
if self.env.config.autodoc_typehints in ('none', 'description'):
|
||||||
kwargs.setdefault('show_annotation', False)
|
kwargs.setdefault('show_annotation', False)
|
||||||
|
|
||||||
if ((inspect.isbuiltin(self.object) or inspect.ismethoddescriptor(self.object)) and
|
unwrapped = inspect.unwrap(self.object)
|
||||||
not inspect.is_cython_function_or_method(self.object)):
|
if ((inspect.isbuiltin(unwrapped) or inspect.ismethoddescriptor(unwrapped)) and
|
||||||
|
not inspect.is_cython_function_or_method(unwrapped)):
|
||||||
# cannot introspect arguments of a C function or method
|
# cannot introspect arguments of a C function or method
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
if (not inspect.isfunction(self.object) and
|
if (not inspect.isfunction(unwrapped) and
|
||||||
not inspect.ismethod(self.object) and
|
not inspect.ismethod(unwrapped) and
|
||||||
not inspect.isbuiltin(self.object) and
|
not inspect.isbuiltin(unwrapped) and
|
||||||
not inspect.is_cython_function_or_method(self.object) and
|
not inspect.is_cython_function_or_method(unwrapped) and
|
||||||
not inspect.isclass(self.object) and
|
not inspect.isclass(unwrapped) and
|
||||||
hasattr(self.object, '__call__')):
|
hasattr(unwrapped, '__call__')):
|
||||||
self.env.app.emit('autodoc-before-process-signature',
|
self.env.app.emit('autodoc-before-process-signature',
|
||||||
self.object.__call__, False)
|
unwrapped.__call__, False)
|
||||||
sig = inspect.signature(self.object.__call__)
|
sig = inspect.signature(unwrapped.__call__)
|
||||||
else:
|
else:
|
||||||
self.env.app.emit('autodoc-before-process-signature', self.object, False)
|
self.env.app.emit('autodoc-before-process-signature', unwrapped, False)
|
||||||
sig = inspect.signature(self.object)
|
sig = inspect.signature(unwrapped)
|
||||||
args = stringify_signature(sig, **kwargs)
|
args = stringify_signature(sig, **kwargs)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
if (inspect.is_builtin_class_method(self.object, '__new__') and
|
if (inspect.is_builtin_class_method(unwrapped, '__new__') and
|
||||||
inspect.is_builtin_class_method(self.object, '__init__')):
|
inspect.is_builtin_class_method(unwrapped, '__init__')):
|
||||||
raise TypeError('%r is a builtin class' % self.object)
|
raise TypeError('%r is a builtin class' % unwrapped)
|
||||||
|
|
||||||
# if a class should be documented as function (yay duck
|
# if a class should be documented as function (yay duck
|
||||||
# typing) we try to use the constructor signature as function
|
# typing) we try to use the constructor signature as function
|
||||||
# signature without the first argument.
|
# signature without the first argument.
|
||||||
try:
|
try:
|
||||||
self.env.app.emit('autodoc-before-process-signature',
|
self.env.app.emit('autodoc-before-process-signature',
|
||||||
self.object.__new__, True)
|
unwrapped.__new__, True)
|
||||||
sig = inspect.signature(self.object.__new__, bound_method=True)
|
sig = inspect.signature(unwrapped.__new__, bound_method=True)
|
||||||
args = stringify_signature(sig, show_return_annotation=False, **kwargs)
|
args = stringify_signature(sig, show_return_annotation=False, **kwargs)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.env.app.emit('autodoc-before-process-signature',
|
self.env.app.emit('autodoc-before-process-signature',
|
||||||
self.object.__init__, True)
|
unwrapped.__init__, True)
|
||||||
sig = inspect.signature(self.object.__init__, bound_method=True)
|
sig = inspect.signature(unwrapped.__init__, bound_method=True)
|
||||||
args = stringify_signature(sig, show_return_annotation=False, **kwargs)
|
args = stringify_signature(sig, show_return_annotation=False, **kwargs)
|
||||||
|
|
||||||
if self.env.config.strip_signature_backslash:
|
if self.env.config.strip_signature_backslash:
|
||||||
@ -1432,16 +1433,17 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
|||||||
if self.env.config.autodoc_typehints == 'none':
|
if self.env.config.autodoc_typehints == 'none':
|
||||||
kwargs.setdefault('show_annotation', False)
|
kwargs.setdefault('show_annotation', False)
|
||||||
|
|
||||||
if ((inspect.isbuiltin(self.object) or inspect.ismethoddescriptor(self.object)) and
|
unwrapped = inspect.unwrap(self.object)
|
||||||
not inspect.is_cython_function_or_method(self.object)):
|
if ((inspect.isbuiltin(unwrapped) or inspect.ismethoddescriptor(unwrapped)) and
|
||||||
|
not inspect.is_cython_function_or_method(unwrapped)):
|
||||||
# can never get arguments of a C function or method
|
# can never get arguments of a C function or method
|
||||||
return None
|
return None
|
||||||
if inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name):
|
if inspect.isstaticmethod(unwrapped, cls=self.parent, name=self.object_name):
|
||||||
self.env.app.emit('autodoc-before-process-signature', self.object, False)
|
self.env.app.emit('autodoc-before-process-signature', unwrapped, False)
|
||||||
sig = inspect.signature(self.object, bound_method=False)
|
sig = inspect.signature(unwrapped, bound_method=False)
|
||||||
else:
|
else:
|
||||||
self.env.app.emit('autodoc-before-process-signature', self.object, True)
|
self.env.app.emit('autodoc-before-process-signature', unwrapped, True)
|
||||||
sig = inspect.signature(self.object, bound_method=True)
|
sig = inspect.signature(unwrapped, bound_method=True)
|
||||||
args = stringify_signature(sig, **kwargs)
|
args = stringify_signature(sig, **kwargs)
|
||||||
|
|
||||||
if self.env.config.strip_signature_backslash:
|
if self.env.config.strip_signature_backslash:
|
||||||
|
@ -17,7 +17,7 @@ import typing
|
|||||||
import warnings
|
import warnings
|
||||||
from functools import partial, partialmethod
|
from functools import partial, partialmethod
|
||||||
from inspect import ( # NOQA
|
from inspect import ( # NOQA
|
||||||
Parameter, isclass, ismethod, ismethoddescriptor, isroutine
|
Parameter, isclass, ismethod, ismethoddescriptor, isroutine, unwrap
|
||||||
)
|
)
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from typing import Any, Callable, Mapping, List, Tuple
|
from typing import Any, Callable, Mapping, List, Tuple
|
||||||
@ -116,11 +116,16 @@ def getargspec(func: Callable) -> Any:
|
|||||||
kwonlyargs, kwdefaults, annotations)
|
kwonlyargs, kwdefaults, annotations)
|
||||||
|
|
||||||
|
|
||||||
def unwrap(obj: Any) -> Any:
|
def unwrap_all(obj: Any) -> Any:
|
||||||
"""Get an original object from wrapped object."""
|
"""
|
||||||
|
Get an original object from wrapped object (unwrapping partials, wrapped
|
||||||
|
functions, and other decorators).
|
||||||
|
"""
|
||||||
while True:
|
while True:
|
||||||
if ispartial(obj):
|
if ispartial(obj):
|
||||||
obj = unpartial(obj)
|
obj = obj.func
|
||||||
|
elif inspect.isroutine(obj) and hasattr(obj, '__wrapped__'):
|
||||||
|
obj = obj.__wrapped__
|
||||||
elif isclassmethod(obj):
|
elif isclassmethod(obj):
|
||||||
obj = obj.__func__
|
obj = obj.__func__
|
||||||
elif isstaticmethod(obj):
|
elif isstaticmethod(obj):
|
||||||
@ -207,26 +212,27 @@ def is_cython_function_or_method(obj: Any) -> bool:
|
|||||||
|
|
||||||
def isattributedescriptor(obj: Any) -> bool:
|
def isattributedescriptor(obj: Any) -> bool:
|
||||||
"""Check if the object is an attribute like descriptor."""
|
"""Check if the object is an attribute like descriptor."""
|
||||||
if inspect.isdatadescriptor(object):
|
if inspect.isdatadescriptor(obj):
|
||||||
# data descriptor is kind of attribute
|
# data descriptor is kind of attribute
|
||||||
return True
|
return True
|
||||||
elif isdescriptor(obj):
|
elif isdescriptor(obj):
|
||||||
# non data descriptor
|
# non data descriptor
|
||||||
if isfunction(obj) or isbuiltin(obj) or inspect.ismethod(obj):
|
unwrapped = inspect.unwrap(obj)
|
||||||
|
if isfunction(unwrapped) or isbuiltin(unwrapped) or inspect.ismethod(unwrapped):
|
||||||
# attribute must not be either function, builtin and method
|
# attribute must not be either function, builtin and method
|
||||||
return False
|
return False
|
||||||
elif is_cython_function_or_method(obj):
|
elif is_cython_function_or_method(unwrapped):
|
||||||
# attribute must not be either function and method (for cython)
|
# attribute must not be either function and method (for cython)
|
||||||
return False
|
return False
|
||||||
elif inspect.isclass(obj):
|
elif inspect.isclass(unwrapped):
|
||||||
# attribute must not be a class
|
# attribute must not be a class
|
||||||
return False
|
return False
|
||||||
elif isinstance(obj, (ClassMethodDescriptorType,
|
elif isinstance(unwrapped, (ClassMethodDescriptorType,
|
||||||
MethodDescriptorType,
|
MethodDescriptorType,
|
||||||
WrapperDescriptorType)):
|
WrapperDescriptorType)):
|
||||||
# attribute must not be a method descriptor
|
# attribute must not be a method descriptor
|
||||||
return False
|
return False
|
||||||
elif type(obj).__name__ == "instancemethod":
|
elif type(unwrapped).__name__ == "instancemethod":
|
||||||
# attribute must not be an instancemethod (C-API)
|
# attribute must not be an instancemethod (C-API)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
@ -257,17 +263,22 @@ def is_singledispatch_method(obj: Any) -> bool:
|
|||||||
|
|
||||||
def isfunction(obj: Any) -> bool:
|
def isfunction(obj: Any) -> bool:
|
||||||
"""Check if the object is function."""
|
"""Check if the object is function."""
|
||||||
return inspect.isfunction(unwrap(obj))
|
return inspect.isfunction(unwrap_all(obj))
|
||||||
|
|
||||||
|
|
||||||
def isbuiltin(obj: Any) -> bool:
|
def isbuiltin(obj: Any) -> bool:
|
||||||
"""Check if the object is builtin."""
|
"""Check if the object is builtin."""
|
||||||
return inspect.isbuiltin(unwrap(obj))
|
return inspect.isbuiltin(unwrap_all(obj))
|
||||||
|
|
||||||
|
|
||||||
|
def isroutine(obj: Any) -> bool:
|
||||||
|
"""Check is any kind of function or method."""
|
||||||
|
return inspect.isroutine(unwrap_all(obj))
|
||||||
|
|
||||||
|
|
||||||
def iscoroutinefunction(obj: Any) -> bool:
|
def iscoroutinefunction(obj: Any) -> bool:
|
||||||
"""Check if the object is coroutine-function."""
|
"""Check if the object is coroutine-function."""
|
||||||
obj = unwrap(obj)
|
obj = unwrap_all(obj)
|
||||||
if hasattr(obj, '__code__') and inspect.iscoroutinefunction(obj):
|
if hasattr(obj, '__code__') and inspect.iscoroutinefunction(obj):
|
||||||
# check obj.__code__ because iscoroutinefunction() crashes for custom method-like
|
# check obj.__code__ because iscoroutinefunction() crashes for custom method-like
|
||||||
# objects (see https://github.com/sphinx-doc/sphinx/issues/6605)
|
# objects (see https://github.com/sphinx-doc/sphinx/issues/6605)
|
||||||
|
8
tests/roots/test-ext-autodoc/target/wrappedfunction.py
Normal file
8
tests/roots/test-ext-autodoc/target/wrappedfunction.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# for py32 or above
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=None)
|
||||||
|
def slow_function(message, timeout):
|
||||||
|
"""This function is slow."""
|
||||||
|
print(message)
|
@ -1413,6 +1413,19 @@ def test_partialmethod(app):
|
|||||||
assert list(actual) == expected
|
assert list(actual) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_wrappedfunction(app):
|
||||||
|
actual = do_autodoc(app, 'function', 'target.wrappedfunction.slow_function')
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:function:: slow_function(message, timeout)',
|
||||||
|
' :module: target.wrappedfunction',
|
||||||
|
'',
|
||||||
|
' This function is slow.',
|
||||||
|
' ',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
def test_partialmethod_undoc_members(app):
|
def test_partialmethod_undoc_members(app):
|
||||||
expected = [
|
expected = [
|
||||||
|
Loading…
Reference in New Issue
Block a user