Skip to content

Commit

Permalink
Fix #8105: autodoc: the signature of decorated class is incorrect
Browse files Browse the repository at this point in the history
In #7651, autodoc stops to undecorate the functions on getting the
signature from the callables.  But some kinds of decorators conceals
the correct signature because they pass through their arguments via
`(*args, **kwargs)`.

This restarts to undecorate the functions again as before #7651.
  • Loading branch information
tk0miya committed Nov 4, 2020
1 parent 0cf1632 commit 6d05b1a
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 5 deletions.
7 changes: 7 additions & 0 deletions CHANGES
Expand Up @@ -7,9 +7,14 @@ Dependencies
Incompatible changes
--------------------

* #8105: autodoc: the signature of class constructor will be shown for decorated
classes, not a signature of decorator

Deprecated
----------

* The ``follow_wrapped`` argument of ``sphinx.util.inspect.signature()``

Features added
--------------

Expand All @@ -21,6 +26,8 @@ Bugs fixed
----------

* #7613: autodoc: autodoc does not respect __signature__ of the class
* #8105: autodoc: the signature of class constructor is incorrect if the class
is decorated

Testing
--------
Expand Down
6 changes: 6 additions & 0 deletions doc/extdev/deprecated.rst
Expand Up @@ -26,6 +26,12 @@ The following is a list of deprecated interfaces.
- (will be) Removed
- Alternatives


* - The ``follow_wrapped`` argument of ``sphinx.util.inspect.signature()``
- 3.4
- 5.0
- N/A

* - ``sphinx.builders.latex.LaTeXBuilder.usepackages``
- 3.3
- 5.0
Expand Down
3 changes: 1 addition & 2 deletions sphinx/ext/autodoc/__init__.py
Expand Up @@ -1213,7 +1213,7 @@ def format_args(self, **kwargs: Any) -> str:

try:
self.env.app.emit('autodoc-before-process-signature', self.object, False)
sig = inspect.signature(self.object, follow_wrapped=True,
sig = inspect.signature(self.object,
type_aliases=self.env.config.autodoc_type_aliases)
args = stringify_signature(sig, **kwargs)
except TypeError as exc:
Expand Down Expand Up @@ -1853,7 +1853,6 @@ def format_args(self, **kwargs: Any) -> str:
else:
self.env.app.emit('autodoc-before-process-signature', self.object, True)
sig = inspect.signature(self.object, bound_method=True,
follow_wrapped=True,
type_aliases=self.env.config.autodoc_type_aliases)
args = stringify_signature(sig, **kwargs)
except TypeError as exc:
Expand Down
10 changes: 8 additions & 2 deletions sphinx/util/inspect.py
Expand Up @@ -439,14 +439,20 @@ def _should_unwrap(subject: Callable) -> bool:
return False


def signature(subject: Callable, bound_method: bool = False, follow_wrapped: bool = False,
def signature(subject: Callable, bound_method: bool = False, follow_wrapped: bool = None,
type_aliases: Dict = {}) -> inspect.Signature:
"""Return a Signature object for the given *subject*.
:param bound_method: Specify *subject* is a bound method or not
:param follow_wrapped: Same as ``inspect.signature()``.
Defaults to ``False`` (get a signature of *subject*).
"""

if follow_wrapped is None:
follow_wrapped = True
else:
warnings.warn('The follow_wrapped argument of sphinx.util.inspect.signature() is '
'deprecated', RemovedInSphinx50Warning, stacklevel=2)

try:
try:
if _should_unwrap(subject):
Expand Down
22 changes: 22 additions & 0 deletions tests/roots/test-ext-autodoc/target/decorator.py
Expand Up @@ -29,3 +29,25 @@ class Bar:
@deco1
def meth(self, name=None, age=None):
pass


class Baz:
@deco1
def __init__(self, name=None, age=None):
pass


class Qux:
@deco1
def __new__(self, name=None, age=None):
pass


class _Metaclass(type):
@deco1
def __call__(self, name=None, age=None):
pass


class Quux(metaclass=_Metaclass):
pass
25 changes: 25 additions & 0 deletions tests/test_ext_autodoc_autoclass.py
Expand Up @@ -48,3 +48,28 @@ def test_classes(app):
'',
]


def test_decorators(app):
actual = do_autodoc(app, 'class', 'target.decorator.Baz')
assert list(actual) == [
'',
'.. py:class:: Baz(name=None, age=None)',
' :module: target.decorator',
'',
]

actual = do_autodoc(app, 'class', 'target.decorator.Qux')
assert list(actual) == [
'',
'.. py:class:: Qux(name=None, age=None)',
' :module: target.decorator',
'',
]

actual = do_autodoc(app, 'class', 'target.decorator.Quux')
assert list(actual) == [
'',
'.. py:class:: Quux(name=None, age=None)',
' :module: target.decorator',
'',
]
2 changes: 1 addition & 1 deletion tests/test_util_inspect.py
Expand Up @@ -100,7 +100,7 @@ def wrapped_bound_method(*args, **kwargs):

# wrapped bound method
sig = inspect.signature(wrapped_bound_method)
assert stringify_signature(sig) == '(*args, **kwargs)'
assert stringify_signature(sig) == '(arg1, **kwargs)'


def test_signature_partialmethod():
Expand Down

0 comments on commit 6d05b1a

Please sign in to comment.