diff -r 03fc7449f204 Lib/inspect.py --- a/Lib/inspect.py Wed Jan 29 23:02:49 2014 -0800 +++ b/Lib/inspect.py Thu Jan 30 11:47:00 2014 -0500 @@ -1601,6 +1601,23 @@ obj in (type, object)) +def _signature_is_functionlike(obj): + # Internal helper to test if `obj` is a duck type of FunctionType + + if not callable(obj): + return False + + code = getattr(obj, '__code__', None) + defaults = getattr(obj, '__defaults__', None) + kwdefaults = getattr(obj, '__kwdefaults__', None) + annotations = getattr(obj, '__annotations__', None) + + return (isinstance(code, types.CodeType) and + (defaults is None or isinstance(defaults, tuple)) and + (kwdefaults is None or isinstance(kwdefaults, dict)) and + isinstance(annotations, dict)) + + def _signature_get_bound_param(spec): # Internal helper to get first parameter name from a # __text_signature__ of a builtin method, which should @@ -1670,7 +1687,7 @@ if _signature_is_builtin(obj): return Signature.from_builtin(obj) - if isinstance(obj, types.FunctionType): + if isfunction(obj) or _signature_is_functionlike(obj): return Signature.from_function(obj) if isinstance(obj, functools.partial): @@ -2071,7 +2088,7 @@ def from_function(cls, func): '''Constructs Signature for the given python function''' - if not isinstance(func, types.FunctionType): + if not (isfunction(func) or _signature_is_functionlike(func)): raise TypeError('{!r} is not a Python function'.format(func)) Parameter = cls._parameter_cls diff -r 03fc7449f204 Lib/test/test_inspect.py --- a/Lib/test/test_inspect.py Wed Jan 29 23:02:49 2014 -0800 +++ b/Lib/test/test_inspect.py Thu Jan 30 11:47:00 2014 -0500 @@ -1736,6 +1736,33 @@ with self.assertRaisesRegex(TypeError, 'is not a Python builtin'): inspect.Signature.from_builtin(42) + def test_signature_from_functionlike_object(self): + def func(a,b, *args, kwonly=True, kwonlyreq, **kwargs): + pass + + class funclike: + # Has to be callable, and have correct + # __code__, __annotations__, __defaults__, and + # __kwdefaults__ attributes + + def __init__(self, func): + self.__code__ = func.__code__ + self.__annotations__ = func.__annotations__ + self.__defaults__ = func.__defaults__ + self.__kwdefaults__ = func.__kwdefaults__ + self.func = func + + def __call__(self, *args, **kwargs): + return self.func(*args, **kwargs) + + sig_func = inspect.Signature.from_function(func) + + sig_funclike = inspect.Signature.from_function(funclike(func)) + self.assertEqual(sig_funclike, sig_func) + + sig_funclike = inspect.signature(funclike(func)) + self.assertEqual(sig_funclike, sig_func) + def test_signature_on_method(self): class Test: def __init__(*args):