From 378d70642aa1c8de2a53ecb811927faf0388db2d Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Thu, 18 May 2017 04:00:51 +0900 Subject: [PATCH] bpo-30149: Fix partialmethod without explicit self parameter (#1308) --- Lib/inspect.py | 13 +++++++++---- Lib/test/test_inspect.py | 35 +++++++++++++++++++++++++++++++++++ Misc/NEWS | 4 ++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/Lib/inspect.py b/Lib/inspect.py index 2894672f50..3317f58f47 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -2241,11 +2241,16 @@ def _signature_from_callable(obj, *, sigcls=sigcls) sig = _signature_get_partial(wrapped_sig, partialmethod, (None,)) - first_wrapped_param = tuple(wrapped_sig.parameters.values())[0] - new_params = (first_wrapped_param,) + tuple(sig.parameters.values()) - - return sig.replace(parameters=new_params) + if first_wrapped_param.kind is Parameter.VAR_POSITIONAL: + # First argument of the wrapped callable is `*args`, as in + # `partialmethod(lambda *args)`. + return sig + else: + sig_params = tuple(sig.parameters.values()) + assert first_wrapped_param is not sig_params[0] + new_params = (first_wrapped_param,) + sig_params + return sig.replace(parameters=new_params) if isfunction(obj) or _signature_is_functionlike(obj): # If it's a pure Python function, or an object that is duck type diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 799a9faa78..c55efd6942 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -1986,6 +1986,41 @@ class TestSignatureObject(unittest.TestCase): ('kwargs', ..., int, "var_keyword")), ...)) + def test_signature_without_self(self): + def test_args_only(*args): # NOQA + pass + + def test_args_kwargs_only(*args, **kwargs): # NOQA + pass + + class A: + @classmethod + def test_classmethod(*args): # NOQA + pass + + @staticmethod + def test_staticmethod(*args): # NOQA + pass + + f1 = functools.partialmethod((test_classmethod), 1) + f2 = functools.partialmethod((test_args_only), 1) + f3 = functools.partialmethod((test_staticmethod), 1) + f4 = functools.partialmethod((test_args_kwargs_only),1) + + self.assertEqual(self.signature(test_args_only), + ((('args', ..., ..., 'var_positional'),), ...)) + self.assertEqual(self.signature(test_args_kwargs_only), + ((('args', ..., ..., 'var_positional'), + ('kwargs', ..., ..., 'var_keyword')), ...)) + self.assertEqual(self.signature(A.f1), + ((('args', ..., ..., 'var_positional'),), ...)) + self.assertEqual(self.signature(A.f2), + ((('args', ..., ..., 'var_positional'),), ...)) + self.assertEqual(self.signature(A.f3), + ((('args', ..., ..., 'var_positional'),), ...)) + self.assertEqual(self.signature(A.f4), + ((('args', ..., ..., 'var_positional'), + ('kwargs', ..., ..., 'var_keyword')), ...)) @cpython_only @unittest.skipIf(MISSING_C_DOCSTRINGS, "Signature information for builtins requires docstrings") diff --git a/Misc/NEWS b/Misc/NEWS index 4006ccd44d..5948cd92fe 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -323,6 +323,10 @@ Extension Modules Library ------- +- bpo-30149: inspect.signature() now supports callables with + variable-argument parameters wrapped with partialmethod. + Patch by Dong-hee Na. + - bpo-30301: Fix AttributeError when using SimpleQueue.empty() under *spawn* and *forkserver* start methods. -- 2.40.0