diff -r 1a6cb184e939 Lib/inspect.py --- a/Lib/inspect.py Mon Jan 28 13:26:25 2013 +0200 +++ b/Lib/inspect.py Fri Feb 08 22:41:21 2013 +0100 @@ -1360,7 +1360,7 @@ else: return signature(wrapped) - if isinstance(obj, types.FunctionType): + if isfunction(obj): return Signature.from_function(obj) if isinstance(obj, functools.partial): @@ -1438,7 +1438,7 @@ # __call__, __new__, or __init__ methods return sig.replace(parameters=tuple(sig.parameters.values())[1:]) - if isinstance(obj, types.BuiltinFunctionType): + if isbuiltin(obj): # Raise a nicer error message for builtins msg = 'no signature found for builtin function {!r}'.format(obj) raise ValueError(msg) @@ -1773,23 +1773,26 @@ @classmethod def from_function(cls, func): - '''Constructs Signature for the given python function''' - - if not isinstance(func, types.FunctionType): - raise TypeError('{!r} is not a Python function'.format(func)) + '''Constructs Signature for the given python function + (or function-like object that implements the necessary special attributes). + ''' Parameter = cls._parameter_cls # Parameter information. - func_code = func.__code__ + try: + func_code = func.__code__ + annotations = func.__annotations__ + defaults = func.__defaults__ + kwdefaults = func.__kwdefaults__ + except AttributeError: + raise TypeError("'{!r}' is not a Python function".format(func)) from None + pos_count = func_code.co_argcount arg_names = func_code.co_varnames positional = tuple(arg_names[:pos_count]) keyword_only_count = func_code.co_kwonlyargcount keyword_only = arg_names[pos_count:(pos_count + keyword_only_count)] - annotations = func.__annotations__ - defaults = func.__defaults__ - kwdefaults = func.__kwdefaults__ if defaults: pos_default_count = len(defaults) diff -r 1a6cb184e939 Lib/test/test_inspect.py --- a/Lib/test/test_inspect.py Mon Jan 28 13:26:25 2013 +0200 +++ b/Lib/test/test_inspect.py Fri Feb 08 22:41:21 2013 +0100 @@ -1453,12 +1453,26 @@ inspect.signature(min) def test_signature_on_non_function(self): - with self.assertRaisesRegex(TypeError, 'is not a callable object'): + with self.assertRaisesRegex(TypeError, '42.*is not a callable object'): inspect.signature(42) - with self.assertRaisesRegex(TypeError, 'is not a Python function'): + with self.assertRaisesRegex(TypeError, '42.*is not a Python function'): inspect.Signature.from_function(42) + def test_signature_from_functionlike_object(self): + def func(a,b, *args, kwonly=True, kwonlyreq, **kwargs): + pass + + class funclike: + __code__ = func.__code__ + __annotations__ = func.__annotations__ + __defaults__ = func.__defaults__ + __kwdefaults__ = func.__kwdefaults__ + + sig_func = inspect.Signature.from_function(func) + sig_funclike = inspect.Signature.from_function(funclike()) + self.assertEqual(sig_funclike, sig_func) + def test_signature_on_method(self): class Test: def foo(self, arg1, arg2=1) -> int: