Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sphinx-doc/sphinx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2020-04-05 17:01:11 +0300
committerTakeshi KOMIYA <i.tkomiya@gmail.com>2020-04-05 17:01:11 +0300
commit223f92b5c1b41a4ebca92dc499b3351c0ebd7beb (patch)
treed85b8dccbea2caf5ee378d7e8e50e8b1b7bd71fe
parentc99db32a6c67e876a86f3f1ee5abd49fc92f0db4 (diff)
parent4ba1243ea2c31558fc8b49ca8397935d72d9f596 (diff)
Merge branch '2.4.x' into 2.x2.x
-rw-r--r--CHANGES3
-rw-r--r--sphinx/ext/autodoc/__init__.py48
-rw-r--r--sphinx/util/inspect.py39
-rw-r--r--tests/roots/test-ext-autodoc/target/wrappedfunction.py8
-rw-r--r--tests/test_autodoc.py13
5 files changed, 74 insertions, 37 deletions
diff --git a/CHANGES b/CHANGES
index 6778d000a..3210b8a53 100644
--- a/CHANGES
+++ b/CHANGES
@@ -7,6 +7,8 @@ Dependencies
Incompatible changes
--------------------
+* #7222: ``sphinx.util.inspect.unwrap()`` is renamed to ``unwrap_all()``
+
Deprecated
----------
@@ -17,6 +19,7 @@ Bugs fixed
----------
* #7343: Sphinx builds has been slower since 2.4.0 on debug mode
+* #7222: autodoc: ``__wrapped__`` functions are not documented correctly
Testing
--------
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 08e4e5301..e9c492413 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -976,39 +976,40 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
if self.env.config.autodoc_typehints == 'none':
kwargs.setdefault('show_annotation', False)
- if inspect.isbuiltin(self.object) or inspect.ismethoddescriptor(self.object):
+ unwrapped = inspect.unwrap(self.object)
+ if inspect.isbuiltin(unwrapped) or inspect.ismethoddescriptor(unwrapped):
# cannot introspect arguments of a C function or method
return None
try:
- if (not inspect.isfunction(self.object) and
- not inspect.ismethod(self.object) and
- not inspect.isbuiltin(self.object) and
- not inspect.isclass(self.object) and
- hasattr(self.object, '__call__')):
+ if (not inspect.isfunction(unwrapped) and
+ not inspect.ismethod(unwrapped) and
+ not inspect.isbuiltin(unwrapped) and
+ not inspect.isclass(unwrapped) and
+ hasattr(unwrapped, '__call__')):
self.env.app.emit('autodoc-before-process-signature',
- self.object.__call__, False)
- sig = inspect.signature(self.object.__call__)
+ unwrapped.__call__, False)
+ sig = inspect.signature(unwrapped.__call__)
else:
- self.env.app.emit('autodoc-before-process-signature', self.object, False)
- sig = inspect.signature(self.object)
+ self.env.app.emit('autodoc-before-process-signature', unwrapped, False)
+ sig = inspect.signature(unwrapped)
args = stringify_signature(sig, **kwargs)
except TypeError:
- if (inspect.is_builtin_class_method(self.object, '__new__') and
- inspect.is_builtin_class_method(self.object, '__init__')):
- raise TypeError('%r is a builtin class' % self.object)
+ if (inspect.is_builtin_class_method(unwrapped, '__new__') and
+ inspect.is_builtin_class_method(unwrapped, '__init__')):
+ raise TypeError('%r is a builtin class' % unwrapped)
# if a class should be documented as function (yay duck
# typing) we try to use the constructor signature as function
# signature without the first argument.
try:
self.env.app.emit('autodoc-before-process-signature',
- self.object.__new__, True)
- sig = inspect.signature(self.object.__new__, bound_method=True)
+ unwrapped.__new__, True)
+ sig = inspect.signature(unwrapped.__new__, bound_method=True)
args = stringify_signature(sig, show_return_annotation=False, **kwargs)
except TypeError:
self.env.app.emit('autodoc-before-process-signature',
- self.object.__init__, True)
- sig = inspect.signature(self.object.__init__, bound_method=True)
+ unwrapped.__init__, True)
+ sig = inspect.signature(unwrapped.__init__, bound_method=True)
args = stringify_signature(sig, show_return_annotation=False, **kwargs)
# escape backslashes for reST
@@ -1337,15 +1338,16 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
if self.env.config.autodoc_typehints == 'none':
kwargs.setdefault('show_annotation', False)
- if inspect.isbuiltin(self.object) or inspect.ismethoddescriptor(self.object):
+ unwrapped = inspect.unwrap(self.object)
+ if inspect.isbuiltin(unwrapped) or inspect.ismethoddescriptor(unwrapped):
# can never get arguments of a C function or method
return None
- if inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name):
- self.env.app.emit('autodoc-before-process-signature', self.object, False)
- sig = inspect.signature(self.object, bound_method=False)
+ if inspect.isstaticmethod(unwrapped, cls=self.parent, name=self.object_name):
+ self.env.app.emit('autodoc-before-process-signature', unwrapped, False)
+ sig = inspect.signature(unwrapped, bound_method=False)
else:
- self.env.app.emit('autodoc-before-process-signature', self.object, True)
- sig = inspect.signature(self.object, bound_method=True)
+ self.env.app.emit('autodoc-before-process-signature', unwrapped, True)
+ sig = inspect.signature(unwrapped, bound_method=True)
args = stringify_signature(sig, **kwargs)
# escape backslashes for reST
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index 2bcccdd60..bce5c33ea 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -17,7 +17,7 @@ import typing
import warnings
from functools import partial, partialmethod
from inspect import ( # NOQA
- isclass, ismethod, ismethoddescriptor, isroutine
+ isclass, ismethod, ismethoddescriptor, unwrap
)
from io import StringIO
from typing import Any, Callable, Mapping, List, Tuple
@@ -111,11 +111,16 @@ def getargspec(func):
kwonlyargs, kwdefaults, annotations)
-def unwrap(obj: Any) -> Any:
- """Get an original object from wrapped object."""
+def unwrap_all(obj: Any) -> Any:
+ """
+ Get an original object from wrapped object (unwrapping partials, wrapped
+ functions, and other decorators).
+ """
while True:
if ispartial(obj):
- obj = unpartial(obj)
+ obj = obj.func
+ elif inspect.isroutine(obj) and hasattr(obj, '__wrapped__'):
+ obj = obj.__wrapped__
elif isclassmethod(obj):
obj = obj.__func__
elif isstaticmethod(obj):
@@ -194,23 +199,24 @@ def isabstractmethod(obj: Any) -> bool:
def isattributedescriptor(obj: Any) -> bool:
"""Check if the object is an attribute like descriptor."""
- if inspect.isdatadescriptor(object):
+ if inspect.isdatadescriptor(obj):
# data descriptor is kind of attribute
return True
elif isdescriptor(obj):
# 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
return False
- elif inspect.isclass(obj):
+ elif inspect.isclass(unwrapped):
# attribute must not be a class
return False
- elif isinstance(obj, (ClassMethodDescriptorType,
- MethodDescriptorType,
- WrapperDescriptorType)):
+ elif isinstance(unwrapped, (ClassMethodDescriptorType,
+ MethodDescriptorType,
+ WrapperDescriptorType)):
# attribute must not be a method descriptor
return False
- elif type(obj).__name__ == "instancemethod":
+ elif type(unwrapped).__name__ == "instancemethod":
# attribute must not be an instancemethod (C-API)
return False
else:
@@ -221,17 +227,22 @@ def isattributedescriptor(obj: Any) -> bool:
def isfunction(obj: Any) -> bool:
"""Check if the object is function."""
- return inspect.isfunction(unwrap(obj))
+ return inspect.isfunction(unwrap_all(obj))
def isbuiltin(obj: Any) -> bool:
"""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:
"""Check if the object is coroutine-function."""
- obj = unwrap(obj)
+ obj = unwrap_all(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)
diff --git a/tests/roots/test-ext-autodoc/target/wrappedfunction.py b/tests/roots/test-ext-autodoc/target/wrappedfunction.py
new file mode 100644
index 000000000..ea872f086
--- /dev/null
+++ b/tests/roots/test-ext-autodoc/target/wrappedfunction.py
@@ -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)
diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py
index a86211f18..cb93b47e1 100644
--- a/tests/test_autodoc.py
+++ b/tests/test_autodoc.py
@@ -1370,6 +1370,19 @@ def test_partialmethod(app):
@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')
def test_partialmethod_undoc_members(app):
expected = [
'',