From dbf2faf579b4094387d65ee41f049456ca67c446 Mon Sep 17 00:00:00 2001 From: Anton Ryzhov Date: Thu, 10 Nov 2022 13:32:01 +0100 Subject: gh-74044: inspect.signature for wrappers around decorated bound methods (GH-736) --- Lib/inspect.py | 5 ++++- Lib/test/test_inspect.py | 9 +++++++-- .../next/Library/2022-11-09-20-48-42.gh-issue-74044.zBj26K.rst | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-11-09-20-48-42.gh-issue-74044.zBj26K.rst diff --git a/Lib/inspect.py b/Lib/inspect.py index f6750c3..d0015aa 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -2442,7 +2442,10 @@ def _signature_from_callable(obj, *, # Was this function wrapped by a decorator? if follow_wrapper_chains: - obj = unwrap(obj, stop=(lambda f: hasattr(f, "__signature__"))) + # Unwrap until we find an explicit signature or a MethodType (which will be + # handled explicitly below). + obj = unwrap(obj, stop=(lambda f: hasattr(f, "__signature__") + or isinstance(f, types.MethodType))) if isinstance(obj, types.MethodType): # If the unwrapped object is a *method*, we might want to # skip its first parameter (self). diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index cfc6e41..3f5c299 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -2960,8 +2960,6 @@ class TestSignatureObject(unittest.TestCase): self.assertEqual(str(inspect.signature(foo)), '(a)') def test_signature_on_decorated(self): - import functools - def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs) -> int: @@ -2973,6 +2971,8 @@ class TestSignatureObject(unittest.TestCase): def bar(self, a, b): pass + bar = decorator(Foo().bar) + self.assertEqual(self.signature(Foo.bar), ((('self', ..., ..., "positional_or_keyword"), ('a', ..., ..., "positional_or_keyword"), @@ -2991,6 +2991,11 @@ class TestSignatureObject(unittest.TestCase): # from "func" to "wrapper", hence no # return_annotation + self.assertEqual(self.signature(bar), + ((('a', ..., ..., "positional_or_keyword"), + ('b', ..., ..., "positional_or_keyword")), + ...)) + # Test that we handle method wrappers correctly def decorator(func): @functools.wraps(func) diff --git a/Misc/NEWS.d/next/Library/2022-11-09-20-48-42.gh-issue-74044.zBj26K.rst b/Misc/NEWS.d/next/Library/2022-11-09-20-48-42.gh-issue-74044.zBj26K.rst new file mode 100644 index 0000000..3102ef4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-11-09-20-48-42.gh-issue-74044.zBj26K.rst @@ -0,0 +1 @@ +Fixed bug where :func:`inspect.signature` reported incorrect arguments for decorated methods. -- cgit v0.12