diff options
Diffstat (limited to 'Lib/inspect.py')
-rw-r--r-- | Lib/inspect.py | 46 |
1 files changed, 35 insertions, 11 deletions
diff --git a/Lib/inspect.py b/Lib/inspect.py index fcfe3b191ab..183e67fabf9 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1901,7 +1901,7 @@ _NonUserDefinedCallables = (types.WrapperDescriptorType, types.BuiltinFunctionType) -def _signature_get_user_defined_method(cls, method_name): +def _signature_get_user_defined_method(cls, method_name, *, follow_wrapper_chains=True): """Private helper. Checks if ``cls`` has an attribute named ``method_name`` and returns it only if it is a pure python function. @@ -1910,12 +1910,20 @@ def _signature_get_user_defined_method(cls, method_name): meth = getattr(cls, method_name, None) else: meth = getattr_static(cls, method_name, None) - if meth is None or isinstance(meth, _NonUserDefinedCallables): + if meth is None: + return None + + if follow_wrapper_chains: + meth = unwrap(meth, stop=(lambda m: hasattr(m, "__signature__") + or _signature_is_builtin(m))) + if isinstance(meth, _NonUserDefinedCallables): # Once '__signature__' will be added to 'C'-level # callables, this check won't be necessary return None if method_name != '__new__': meth = _descriptor_get(meth, cls) + if follow_wrapper_chains: + meth = unwrap(meth, stop=lambda m: hasattr(m, "__signature__")) return meth @@ -2066,13 +2074,11 @@ def _signature_is_functionlike(obj): code = getattr(obj, '__code__', None) defaults = getattr(obj, '__defaults__', _void) # Important to use _void ... kwdefaults = getattr(obj, '__kwdefaults__', _void) # ... and not None here - annotations = getattr(obj, '__annotations__', None) return (isinstance(code, types.CodeType) and isinstance(name, str) and (defaults is None or isinstance(defaults, tuple)) and - (kwdefaults is None or isinstance(kwdefaults, dict)) and - (isinstance(annotations, (dict)) or annotations is None) ) + (kwdefaults is None or isinstance(kwdefaults, dict))) def _signature_strip_non_python_syntax(signature): @@ -2507,12 +2513,26 @@ def _signature_from_callable(obj, *, # First, let's see if it has an overloaded __call__ defined # in its metaclass - call = _signature_get_user_defined_method(type(obj), '__call__') + call = _signature_get_user_defined_method( + type(obj), + '__call__', + follow_wrapper_chains=follow_wrapper_chains, + ) if call is not None: return _get_signature_of(call) - new = _signature_get_user_defined_method(obj, '__new__') - init = _signature_get_user_defined_method(obj, '__init__') + # NOTE: The user-defined method can be a function with a thin wrapper + # around object.__new__ (e.g., generated by `@warnings.deprecated`) + new = _signature_get_user_defined_method( + obj, + '__new__', + follow_wrapper_chains=follow_wrapper_chains, + ) + init = _signature_get_user_defined_method( + obj, + '__init__', + follow_wrapper_chains=follow_wrapper_chains, + ) # Go through the MRO and see if any class has user-defined # pure Python __new__ or __init__ method @@ -2552,10 +2572,14 @@ def _signature_from_callable(obj, *, # Last option is to check if its '__init__' is # object.__init__ or type.__init__. if type not in obj.__mro__: + obj_init = obj.__init__ + obj_new = obj.__new__ + if follow_wrapper_chains: + obj_init = unwrap(obj_init) + obj_new = unwrap(obj_new) # We have a class (not metaclass), but no user-defined # __init__ or __new__ for it - if (obj.__init__ is object.__init__ and - obj.__new__ is object.__new__): + if obj_init is object.__init__ and obj_new is object.__new__: # Return a signature of 'object' builtin. return sigcls.from_callable(object) else: @@ -3317,7 +3341,7 @@ def _main(): import argparse import importlib - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser(color=True) parser.add_argument( 'object', help="The object to be analysed. " |