diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 634645124c7864..932dbbd474749a 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -559,7 +559,7 @@ The Signature object represents the call signature of a callable object and its return annotation. To retrieve a Signature object, use the :func:`signature` function. -.. function:: signature(callable, \*, follow_wrapped=True) +.. function:: signature(callable, \*, follow_wrapped=True, resolve_type_hints=False) Return a :class:`Signature` object for the given ``callable``:: @@ -588,6 +588,9 @@ function. to it are positional-only. For more info, see :ref:`the FAQ entry on positional-only parameters `. + .. versionadded:: 3.9 + ``resolve_type_hints`` parameter. Pass ``True`` to resolve the type annotations. + .. versionadded:: 3.5 ``follow_wrapped`` parameter. Pass ``False`` to get a signature of ``callable`` specifically (``callable.__wrapped__`` will not be used to @@ -671,7 +674,7 @@ function. >>> str(new_sig) "(a, b) -> 'new return anno'" - .. classmethod:: Signature.from_callable(obj, \*, follow_wrapped=True) + .. classmethod:: Signature.from_callable(obj, \*, follow_wrapped=True, resolve_type_hints=False) Return a :class:`Signature` (or its subclass) object for a given callable ``obj``. Pass ``follow_wrapped=False`` to get a signature of ``obj`` @@ -684,6 +687,9 @@ function. sig = MySignature.from_callable(min) assert isinstance(sig, MySignature) + .. versionadded:: 3.9 + ``resolve_type_hints`` parameter. Pass ``True`` to resolve the type annotations. + .. versionadded:: 3.5 diff --git a/Lib/inspect.py b/Lib/inspect.py index ad7e8cb1203e7f..6bf53b8851ae3e 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -2136,7 +2136,7 @@ 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, resolve_type_hints=False): """Private helper: constructs Signature for the given python function.""" is_duck_function = False @@ -2162,10 +2162,15 @@ 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 = func.__annotations__ defaults = func.__defaults__ kwdefaults = func.__kwdefaults__ + if resolve_type_hints: + import typing + annotations = typing.get_type_hints(func) + else: + annotations = func.__annotations__ + if defaults: pos_default_count = len(defaults) else: @@ -2233,6 +2238,7 @@ def _signature_from_function(cls, func, skip_bound_arg=True): def _signature_from_callable(obj, *, follow_wrapper_chains=True, skip_bound_arg=True, + resolve_type_hints=False, sigcls): """Private helper function to get signature for arbitrary @@ -2249,6 +2255,7 @@ def _signature_from_callable(obj, *, obj.__func__, follow_wrapper_chains=follow_wrapper_chains, skip_bound_arg=skip_bound_arg, + resolve_type_hints=resolve_type_hints, sigcls=sigcls) if skip_bound_arg: @@ -2267,6 +2274,7 @@ def _signature_from_callable(obj, *, obj, follow_wrapper_chains=follow_wrapper_chains, skip_bound_arg=skip_bound_arg, + resolve_type_hints=resolve_type_hints, sigcls=sigcls) try: @@ -2298,6 +2306,7 @@ def _signature_from_callable(obj, *, partialmethod.func, follow_wrapper_chains=follow_wrapper_chains, skip_bound_arg=skip_bound_arg, + resolve_type_hints=resolve_type_hints, sigcls=sigcls) sig = _signature_get_partial(wrapped_sig, partialmethod, (None,)) @@ -2317,7 +2326,8 @@ 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, + resolve_type_hints=resolve_type_hints) if _signature_is_builtin(obj): return _signature_from_builtin(sigcls, obj, @@ -2328,6 +2338,7 @@ def _signature_from_callable(obj, *, obj.func, follow_wrapper_chains=follow_wrapper_chains, skip_bound_arg=skip_bound_arg, + resolve_type_hints=resolve_type_hints, sigcls=sigcls) return _signature_get_partial(wrapped_sig, obj) @@ -2343,6 +2354,7 @@ def _signature_from_callable(obj, *, call, follow_wrapper_chains=follow_wrapper_chains, skip_bound_arg=skip_bound_arg, + resolve_type_hints=resolve_type_hints, sigcls=sigcls) else: # Now we check if the 'obj' class has a '__new__' method @@ -2352,6 +2364,7 @@ def _signature_from_callable(obj, *, new, follow_wrapper_chains=follow_wrapper_chains, skip_bound_arg=skip_bound_arg, + resolve_type_hints=resolve_type_hints, sigcls=sigcls) else: # Finally, we should have at least __init__ implemented @@ -2361,6 +2374,7 @@ def _signature_from_callable(obj, *, init, follow_wrapper_chains=follow_wrapper_chains, skip_bound_arg=skip_bound_arg, + resolve_type_hints=resolve_type_hints, sigcls=sigcls) if sig is None: @@ -2411,6 +2425,7 @@ def _signature_from_callable(obj, *, call, follow_wrapper_chains=follow_wrapper_chains, skip_bound_arg=skip_bound_arg, + resolve_type_hints=resolve_type_hints, sigcls=sigcls) except ValueError as ex: msg = 'no signature found for {!r}'.format(obj) @@ -2863,10 +2878,11 @@ def from_builtin(cls, func): return _signature_from_builtin(cls, func) @classmethod - def from_callable(cls, obj, *, follow_wrapped=True): + def from_callable(cls, obj, *, follow_wrapped=True, resolve_type_hints=False): """Constructs Signature for the given callable object.""" return _signature_from_callable(obj, sigcls=cls, - follow_wrapper_chains=follow_wrapped) + follow_wrapper_chains=follow_wrapped, + resolve_type_hints=resolve_type_hints) @property def parameters(self): @@ -3114,9 +3130,10 @@ def __str__(self): return rendered -def signature(obj, *, follow_wrapped=True): +def signature(obj, *, follow_wrapped=True, resolve_type_hints=False): """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, + resolve_type_hints=resolve_type_hints) def _main(): diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 898cc44b295ee0..d50aa05f6ad5a9 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -907,9 +907,12 @@ def spilldata(msg, attrs, predicate): decl = '' try: - signature = inspect.signature(object) - except (ValueError, TypeError): - signature = None + signature = inspect.signature(object, resolve_type_hints=True) + except (NameError, AttributeError, SyntaxError, TypeError, ValueError): + try: + signature = inspect.signature(object, resolve_type_hints=False) + except (ValueError, TypeError): + signature = None if signature: argspec = str(signature) if argspec and argspec != '()': @@ -967,9 +970,12 @@ def docroutine(self, object, name=None, mod=None, argspec = None if inspect.isroutine(object): try: - signature = inspect.signature(object) - except (ValueError, TypeError): - signature = None + signature = inspect.signature(object, resolve_type_hints=True) + except (NameError, AttributeError, SyntaxError, TypeError, ValueError): + try: + signature = inspect.signature(object, resolve_type_hints=False) + except (ValueError, TypeError): + signature = None if signature: argspec = str(signature) if realname == '': @@ -1226,9 +1232,12 @@ def makename(c, m=object.__module__): push = contents.append try: - signature = inspect.signature(object) - except (ValueError, TypeError): - signature = None + signature = inspect.signature(object, resolve_type_hints=True) + except (NameError, AttributeError, SyntaxError, TypeError, ValueError): + try: + signature = inspect.signature(object, resolve_type_hints=False) + except (ValueError, TypeError): + signature = None if signature: argspec = str(signature) if argspec and argspec != '()': @@ -1397,9 +1406,12 @@ def docroutine(self, object, name=None, mod=None, cl=None): if inspect.isroutine(object): try: - signature = inspect.signature(object) - except (ValueError, TypeError): - signature = None + signature = inspect.signature(object, resolve_type_hints=True) + except (NameError, AttributeError, SyntaxError, TypeError, ValueError): + try: + signature = inspect.signature(object, resolve_type_hints=False) + except (ValueError, TypeError): + signature = None if signature: argspec = str(signature) if realname == '': diff --git a/Misc/NEWS.d/next/Documentation/2020-05-03-00-47-47.bpo-39990.Qz_45W.rst b/Misc/NEWS.d/next/Documentation/2020-05-03-00-47-47.bpo-39990.Qz_45W.rst new file mode 100644 index 00000000000000..c27ccb6d6e6b08 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2020-05-03-00-47-47.bpo-39990.Qz_45W.rst @@ -0,0 +1 @@ +Add option to resolve annotation strings when creating a inspect.Signature from function/callable.