From bcd4fc161ae40aaed4c8812d0bf37aa94c6cc2df Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Wed, 20 May 2015 14:30:08 -0400 Subject: [PATCH] Issue 20691: Add follow_wrapped arg to inspect.signature/from_callable. --- Doc/library/inspect.rst | 14 ++++++++++---- Doc/whatsnew/3.5.rst | 3 +++ Lib/inspect.py | 9 +++++---- Lib/test/test_inspect.py | 16 ++++++++++++++-- Misc/NEWS | 4 ++++ 5 files changed, 36 insertions(+), 10 deletions(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 823b4bb50ab..d3cd1a879ac 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -431,7 +431,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) +.. function:: signature(callable, \*, follow_wrapped=True) Return a :class:`Signature` object for the given ``callable``:: @@ -456,6 +456,11 @@ function. Raises :exc:`ValueError` if no signature can be provided, and :exc:`TypeError` if that type of object is not supported. + .. versionadded:: 3.5 + ``follow_wrapped`` parameter. Pass ``False`` to get a signature of + ``callable`` specifically (``callable.__wrapped__`` will not be used to + unwrap decorated callables.) + .. note:: Some callables may not be introspectable in certain implementations of @@ -528,12 +533,13 @@ function. >>> str(new_sig) "(a, b) -> 'new return anno'" - .. classmethod:: Signature.from_callable(obj) + .. classmethod:: Signature.from_callable(obj, \*, follow_wrapped=True) Return a :class:`Signature` (or its subclass) object for a given callable - ``obj``. This method simplifies subclassing of :class:`Signature`: + ``obj``. Pass ``follow_wrapped=False`` to get a signature of ``obj`` + without unwrapping its ``__wrapped__`` chain. - :: + This method simplifies subclassing of :class:`Signature`:: class MySignature(Signature): pass diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst index 85027ed9dfb..3761b89e623 100644 --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -430,6 +430,9 @@ inspect subclassing of :class:`~inspect.Signature` easier. (Contributed by Yury Selivanov and Eric Snow in :issue:`17373`.) +* New argument ``follow_wrapped`` for :func:`inspect.signature`. + (Contributed by Yury Selivanov in :issue:`20691`.) + ipaddress --------- diff --git a/Lib/inspect.py b/Lib/inspect.py index 13d0c7bc5ca..9ad55a90bc4 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -2664,9 +2664,10 @@ class Signature: return _signature_from_builtin(cls, func) @classmethod - def from_callable(cls, obj): + def from_callable(cls, obj, *, follow_wrapped=True): """Constructs Signature for the given callable object.""" - return _signature_from_callable(obj, sigcls=cls) + return _signature_from_callable(obj, sigcls=cls, + follow_wrapper_chains=follow_wrapped) @property def parameters(self): @@ -2915,9 +2916,9 @@ class Signature: return rendered -def signature(obj): +def signature(obj, *, follow_wrapped=True): """Get a signature object for the passed callable.""" - return Signature.from_callable(obj) + return Signature.from_callable(obj, follow_wrapped=follow_wrapped) def _main(): diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 92bc09664e1..8d1178314af 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -1735,8 +1735,8 @@ class MyParameter(inspect.Parameter): class TestSignatureObject(unittest.TestCase): @staticmethod - def signature(func): - sig = inspect.signature(func) + def signature(func, **kw): + sig = inspect.signature(func, **kw) return (tuple((param.name, (... if param.default is param.empty else param.default), (... if param.annotation is param.empty @@ -1956,6 +1956,11 @@ class TestSignatureObject(unittest.TestCase): self.assertEqual(inspect.signature(func), inspect.signature(decorated_func)) + def wrapper_like(*args, **kwargs) -> int: pass + self.assertEqual(inspect.signature(decorated_func, + follow_wrapped=False), + inspect.signature(wrapper_like)) + @cpython_only def test_signature_on_builtins_no_signature(self): import _testcapi @@ -2384,6 +2389,13 @@ class TestSignatureObject(unittest.TestCase): ('b', ..., ..., "positional_or_keyword")), ...)) + self.assertEqual(self.signature(Foo.bar, follow_wrapped=False), + ((('args', ..., ..., "var_positional"), + ('kwargs', ..., ..., "var_keyword")), + ...)) # functools.wraps will copy __annotations__ + # from "func" to "wrapper", hence no + # return_annotation + # Test that we handle method wrappers correctly def decorator(func): @functools.wraps(func) diff --git a/Misc/NEWS b/Misc/NEWS index 15ab1c8f49b..ab12a6562a2 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -176,6 +176,10 @@ Library - Issue 24190: Implement inspect.BoundArgument.apply_defaults() method. Contributed by Yury Selivanov. +- Issue 20691: Add 'follow_wrapped' argument to + inspect.Signature.from_callable() and inspect.signature(). + Contributed by Yury Selivanov. + Tests -----