From b3affa6949d3858312a28ff75a8161bfaf5db8ee Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 13 Jun 2020 23:53:30 +0900 Subject: [PATCH 1/2] Fix autodoc: Show detailed warning when type_comment is mismatched (refs: #7807) --- CHANGES | 3 +++ sphinx/ext/autodoc/type_comment.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/CHANGES b/CHANGES index 4198395c6..5f8d82d4a 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,9 @@ Deprecated Features added -------------- +* #7807: autodoc: Show detailed warning when type_comment is mismatched with its + signature + Bugs fixed ---------- diff --git a/sphinx/ext/autodoc/type_comment.py b/sphinx/ext/autodoc/type_comment.py index e6a77f24d..7f11e3d12 100644 --- a/sphinx/ext/autodoc/type_comment.py +++ b/sphinx/ext/autodoc/type_comment.py @@ -128,6 +128,9 @@ def update_annotations_using_type_comments(app: Sphinx, obj: Any, bound_method: if 'return' not in obj.__annotations__: obj.__annotations__['return'] = type_sig.return_annotation + except KeyError as exc: + logger.warning(__("Failed to update signature for %r: parameter not found: %s"), + obj, exc) except NotImplementedError as exc: # failed to ast.unparse() logger.warning(__("Failed to parse type_comment for %r: %s"), obj, exc) From d77622ba79a0ce8381b484ea4756d1f52b5f7803 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 14 Jun 2020 01:19:21 +0900 Subject: [PATCH 2/2] Fix #7807: autodoc: wrong signature is shown for the function using contextmanager --- CHANGES | 1 + sphinx/util/inspect.py | 17 ++++++++++++++++- .../test-ext-autodoc/target/wrappedfunction.py | 9 ++++++++- tests/test_ext_autodoc_autofunction.py | 13 +++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 5f8d82d4a..e4467d7f1 100644 --- a/CHANGES +++ b/CHANGES @@ -21,6 +21,7 @@ Bugs fixed * #7808: autodoc: Warnings raised on variable and attribute type annotations * #7802: autodoc: EOFError is raised on parallel build +* #7807: autodoc: wrong signature is shown for the function using contextmanager * #7812: autosummary: generates broken stub files if the target code contains an attribute and module that are same name * #7811: sphinx.util.inspect causes circular import problem diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index 206be1f9b..956dcd49e 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -9,6 +9,7 @@ """ import builtins +import contextlib import enum import inspect import re @@ -421,6 +422,17 @@ def is_builtin_class_method(obj: Any, attr_name: str) -> bool: return getattr(builtins, name, None) is cls +def _should_unwrap(subject: Callable) -> bool: + """Check the function should be unwrapped on getting signature.""" + if (safe_getattr(subject, '__globals__', None) and + subject.__globals__.get('__name__') == 'contextlib' and # type: ignore + subject.__globals__.get('__file__') == contextlib.__file__): # type: ignore + # contextmanger should be unwrapped + return True + + return False + + def signature(subject: Callable, bound_method: bool = False, follow_wrapped: bool = False ) -> inspect.Signature: """Return a Signature object for the given *subject*. @@ -431,7 +443,10 @@ def signature(subject: Callable, bound_method: bool = False, follow_wrapped: boo """ try: try: - signature = inspect.signature(subject, follow_wrapped=follow_wrapped) + if _should_unwrap(subject): + signature = inspect.signature(subject) + else: + signature = inspect.signature(subject, follow_wrapped=follow_wrapped) except ValueError: # follow built-in wrappers up (ex. functools.lru_cache) signature = inspect.signature(subject) diff --git a/tests/roots/test-ext-autodoc/target/wrappedfunction.py b/tests/roots/test-ext-autodoc/target/wrappedfunction.py index ea872f086..0bd2d2069 100644 --- a/tests/roots/test-ext-autodoc/target/wrappedfunction.py +++ b/tests/roots/test-ext-autodoc/target/wrappedfunction.py @@ -1,8 +1,15 @@ -# for py32 or above +from contextlib import contextmanager from functools import lru_cache +from typing import Generator @lru_cache(maxsize=None) def slow_function(message, timeout): """This function is slow.""" print(message) + + +@contextmanager +def feeling_good(x: int, y: int) -> Generator: + """You'll feel better in this context!""" + yield diff --git a/tests/test_ext_autodoc_autofunction.py b/tests/test_ext_autodoc_autofunction.py index b4be85019..579ad9f48 100644 --- a/tests/test_ext_autodoc_autofunction.py +++ b/tests/test_ext_autodoc_autofunction.py @@ -146,3 +146,16 @@ def test_wrapped_function(app): ' This function is slow.', '', ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_wrapped_function_contextmanager(app): + actual = do_autodoc(app, 'function', 'target.wrappedfunction.feeling_good') + assert list(actual) == [ + '', + '.. py:function:: feeling_good(x: int, y: int) -> Generator', + ' :module: target.wrappedfunction', + '', + " You'll feel better in this context!", + '', + ]