From 17a5a29f1eeb89f35a430ac50597d0f82e8b45a9 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 10 Jul 2020 22:50:05 +0900 Subject: [PATCH] Fix #7935: autodoc: A default value inspect._empty conseals signatures A function signature is not shown when the function has a parameter having ``inspect._empty`` as its default value because Signature class validates function signatures on instantiation. --- CHANGES | 2 ++ sphinx/util/inspect.py | 8 +++++++- tests/test_util_inspect.py | 6 +++++- tests/typing_test_data.py | 4 ++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index f42d1431b..a91349e5d 100644 --- a/CHANGES +++ b/CHANGES @@ -27,6 +27,8 @@ Bugs fixed ---------- * #7886: autodoc: TypeError is raised on mocking generic-typed classes +* #7935: autodoc: function signature is not shown when the function has a + parameter having ``inspect._empty`` as its default value * #7839: autosummary: cannot handle umlauts in function names * #7865: autosummary: Failed to extract summary line when abbreviations found * #7866: autosummary: Failed to extract correct summary line when docstring diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index 441f850d1..fc4e1c004 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -484,7 +484,13 @@ def signature(subject: Callable, bound_method: bool = False, follow_wrapped: boo if len(parameters) > 0: parameters.pop(0) - return inspect.Signature(parameters, return_annotation=return_annotation) + # To allow to create signature object correctly for pure python functions, + # pass an internal parameter __validate_parameters__=False to Signature + # + # For example, this helps a function having a default value `inspect._empty`. + # refs: https://github.com/sphinx-doc/sphinx/issues/7935 + return inspect.Signature(parameters, return_annotation=return_annotation, # type: ignore + __validate_parameters__=False) def stringify_signature(sig: inspect.Signature, show_annotation: bool = True, diff --git a/tests/test_util_inspect.py b/tests/test_util_inspect.py index 6a14dc1ac..c21eaaa16 100644 --- a/tests/test_util_inspect.py +++ b/tests/test_util_inspect.py @@ -130,7 +130,7 @@ def test_signature_partialmethod(): def test_signature_annotations(): from typing_test_data import (f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, - f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, Node) + f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, f21, Node) # Class annotations sig = inspect.signature(f0) @@ -214,6 +214,10 @@ def test_signature_annotations(): sig = inspect.signature(f19) assert stringify_signature(sig) == '(*args: int, **kwargs: str)' + # default value is inspect.Signature.empty + sig = inspect.signature(f21) + assert stringify_signature(sig) == "(arg1='whatever', arg2)" + # type hints by string sig = inspect.signature(Node.children) if (3, 5, 0) <= sys.version_info < (3, 5, 3): diff --git a/tests/typing_test_data.py b/tests/typing_test_data.py index 70c3144a0..6c0357911 100644 --- a/tests/typing_test_data.py +++ b/tests/typing_test_data.py @@ -1,3 +1,4 @@ +from inspect import Signature from numbers import Integral from typing import Any, Dict, List, TypeVar, Union, Callable, Tuple, Optional @@ -100,6 +101,9 @@ def f20() -> Optional[Union[int, str]]: pass +def f21(arg1='whatever', arg2=Signature.empty): + pass + class Node: def __init__(self, parent: Optional['Node']) -> None: