aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/inspect.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/inspect.py')
-rw-r--r--Lib/inspect.py46
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. "