mirror of
https://github.com/python/cpython.git
synced 2025-08-31 05:58:33 +00:00
bpo-41960: Add globalns and localns parameters to inspect.signature and Signature.from_callable (GH-22583)
This commit is contained in:
parent
6b1ac809b9
commit
eee1c7745a
5 changed files with 77 additions and 52 deletions
|
@ -2137,9 +2137,9 @@ def _signature_fromstr(cls, obj, s, skip_bound_arg=True):
|
|||
|
||||
return cls(parameters, return_annotation=cls.empty)
|
||||
|
||||
def _get_type_hints(func):
|
||||
def _get_type_hints(func, **kwargs):
|
||||
try:
|
||||
return typing.get_type_hints(func)
|
||||
return typing.get_type_hints(func, **kwargs)
|
||||
except Exception:
|
||||
# First, try to use the get_type_hints to resolve
|
||||
# annotations. But for keeping the behavior intact
|
||||
|
@ -2164,7 +2164,8 @@ def _signature_from_builtin(cls, func, skip_bound_arg=True):
|
|||
return _signature_fromstr(cls, func, s, skip_bound_arg)
|
||||
|
||||
|
||||
def _signature_from_function(cls, func, skip_bound_arg=True):
|
||||
def _signature_from_function(cls, func, skip_bound_arg=True,
|
||||
globalns=None, localns=None):
|
||||
"""Private helper: constructs Signature for the given python function."""
|
||||
|
||||
is_duck_function = False
|
||||
|
@ -2190,7 +2191,7 @@ def _signature_from_function(cls, func, skip_bound_arg=True):
|
|||
positional = arg_names[:pos_count]
|
||||
keyword_only_count = func_code.co_kwonlyargcount
|
||||
keyword_only = arg_names[pos_count:pos_count + keyword_only_count]
|
||||
annotations = _get_type_hints(func)
|
||||
annotations = _get_type_hints(func, globalns=globalns, localns=localns)
|
||||
|
||||
defaults = func.__defaults__
|
||||
kwdefaults = func.__kwdefaults__
|
||||
|
@ -2262,23 +2263,28 @@ def _signature_from_function(cls, func, skip_bound_arg=True):
|
|||
def _signature_from_callable(obj, *,
|
||||
follow_wrapper_chains=True,
|
||||
skip_bound_arg=True,
|
||||
globalns=None,
|
||||
localns=None,
|
||||
sigcls):
|
||||
|
||||
"""Private helper function to get signature for arbitrary
|
||||
callable objects.
|
||||
"""
|
||||
|
||||
_get_signature_of = functools.partial(_signature_from_callable,
|
||||
follow_wrapper_chains=follow_wrapper_chains,
|
||||
skip_bound_arg=skip_bound_arg,
|
||||
globalns=globalns,
|
||||
localns=localns,
|
||||
sigcls=sigcls)
|
||||
|
||||
if not callable(obj):
|
||||
raise TypeError('{!r} is not a callable object'.format(obj))
|
||||
|
||||
if isinstance(obj, types.MethodType):
|
||||
# In this case we skip the first parameter of the underlying
|
||||
# function (usually `self` or `cls`).
|
||||
sig = _signature_from_callable(
|
||||
obj.__func__,
|
||||
follow_wrapper_chains=follow_wrapper_chains,
|
||||
skip_bound_arg=skip_bound_arg,
|
||||
sigcls=sigcls)
|
||||
sig = _get_signature_of(obj.__func__)
|
||||
|
||||
if skip_bound_arg:
|
||||
return _signature_bound_method(sig)
|
||||
|
@ -2292,11 +2298,7 @@ def _signature_from_callable(obj, *,
|
|||
# If the unwrapped object is a *method*, we might want to
|
||||
# skip its first parameter (self).
|
||||
# See test_signature_wrapped_bound_method for details.
|
||||
return _signature_from_callable(
|
||||
obj,
|
||||
follow_wrapper_chains=follow_wrapper_chains,
|
||||
skip_bound_arg=skip_bound_arg,
|
||||
sigcls=sigcls)
|
||||
return _get_signature_of(obj)
|
||||
|
||||
try:
|
||||
sig = obj.__signature__
|
||||
|
@ -2323,11 +2325,7 @@ def _signature_from_callable(obj, *,
|
|||
# (usually `self`, or `cls`) will not be passed
|
||||
# automatically (as for boundmethods)
|
||||
|
||||
wrapped_sig = _signature_from_callable(
|
||||
partialmethod.func,
|
||||
follow_wrapper_chains=follow_wrapper_chains,
|
||||
skip_bound_arg=skip_bound_arg,
|
||||
sigcls=sigcls)
|
||||
wrapped_sig = _get_signature_of(partialmethod.func)
|
||||
|
||||
sig = _signature_get_partial(wrapped_sig, partialmethod, (None,))
|
||||
first_wrapped_param = tuple(wrapped_sig.parameters.values())[0]
|
||||
|
@ -2346,18 +2344,15 @@ def _signature_from_callable(obj, *,
|
|||
# If it's a pure Python function, or an object that is duck type
|
||||
# of a Python function (Cython functions, for instance), then:
|
||||
return _signature_from_function(sigcls, obj,
|
||||
skip_bound_arg=skip_bound_arg)
|
||||
skip_bound_arg=skip_bound_arg,
|
||||
globalns=globalns, localns=localns)
|
||||
|
||||
if _signature_is_builtin(obj):
|
||||
return _signature_from_builtin(sigcls, obj,
|
||||
skip_bound_arg=skip_bound_arg)
|
||||
|
||||
if isinstance(obj, functools.partial):
|
||||
wrapped_sig = _signature_from_callable(
|
||||
obj.func,
|
||||
follow_wrapper_chains=follow_wrapper_chains,
|
||||
skip_bound_arg=skip_bound_arg,
|
||||
sigcls=sigcls)
|
||||
wrapped_sig = _get_signature_of(obj.func)
|
||||
return _signature_get_partial(wrapped_sig, obj)
|
||||
|
||||
sig = None
|
||||
|
@ -2368,29 +2363,17 @@ def _signature_from_callable(obj, *,
|
|||
# in its metaclass
|
||||
call = _signature_get_user_defined_method(type(obj), '__call__')
|
||||
if call is not None:
|
||||
sig = _signature_from_callable(
|
||||
call,
|
||||
follow_wrapper_chains=follow_wrapper_chains,
|
||||
skip_bound_arg=skip_bound_arg,
|
||||
sigcls=sigcls)
|
||||
sig = _get_signature_of(call)
|
||||
else:
|
||||
# Now we check if the 'obj' class has a '__new__' method
|
||||
new = _signature_get_user_defined_method(obj, '__new__')
|
||||
if new is not None:
|
||||
sig = _signature_from_callable(
|
||||
new,
|
||||
follow_wrapper_chains=follow_wrapper_chains,
|
||||
skip_bound_arg=skip_bound_arg,
|
||||
sigcls=sigcls)
|
||||
sig = _get_signature_of(new)
|
||||
else:
|
||||
# Finally, we should have at least __init__ implemented
|
||||
init = _signature_get_user_defined_method(obj, '__init__')
|
||||
if init is not None:
|
||||
sig = _signature_from_callable(
|
||||
init,
|
||||
follow_wrapper_chains=follow_wrapper_chains,
|
||||
skip_bound_arg=skip_bound_arg,
|
||||
sigcls=sigcls)
|
||||
sig = _get_signature_of(init)
|
||||
|
||||
if sig is None:
|
||||
# At this point we know, that `obj` is a class, with no user-
|
||||
|
@ -2436,11 +2419,7 @@ def _signature_from_callable(obj, *,
|
|||
call = _signature_get_user_defined_method(type(obj), '__call__')
|
||||
if call is not None:
|
||||
try:
|
||||
sig = _signature_from_callable(
|
||||
call,
|
||||
follow_wrapper_chains=follow_wrapper_chains,
|
||||
skip_bound_arg=skip_bound_arg,
|
||||
sigcls=sigcls)
|
||||
sig = _get_signature_of(call)
|
||||
except ValueError as ex:
|
||||
msg = 'no signature found for {!r}'.format(obj)
|
||||
raise ValueError(msg) from ex
|
||||
|
@ -2892,10 +2871,12 @@ class Signature:
|
|||
return _signature_from_builtin(cls, func)
|
||||
|
||||
@classmethod
|
||||
def from_callable(cls, obj, *, follow_wrapped=True):
|
||||
def from_callable(cls, obj, *,
|
||||
follow_wrapped=True, globalns=None, localns=None):
|
||||
"""Constructs Signature for the given callable object."""
|
||||
return _signature_from_callable(obj, sigcls=cls,
|
||||
follow_wrapper_chains=follow_wrapped)
|
||||
follow_wrapper_chains=follow_wrapped,
|
||||
globalns=globalns, localns=localns)
|
||||
|
||||
@property
|
||||
def parameters(self):
|
||||
|
@ -3143,9 +3124,10 @@ class Signature:
|
|||
return rendered
|
||||
|
||||
|
||||
def signature(obj, *, follow_wrapped=True):
|
||||
def signature(obj, *, follow_wrapped=True, globalns=None, localns=None):
|
||||
"""Get a signature object for the passed callable."""
|
||||
return Signature.from_callable(obj, follow_wrapped=follow_wrapped)
|
||||
return Signature.from_callable(obj, follow_wrapped=follow_wrapped,
|
||||
globalns=globalns, localns=localns)
|
||||
|
||||
|
||||
def _main():
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue