diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 4290aeb..694d3f2 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -429,11 +429,14 @@ function. Accepts a wide range of python callables, from plain functions and classes to :func:`functools.partial` objects. + Raises :exc:`ValueError` if no signature can be provided, and + :exc:`TypeError` if type of callable is not supported. + .. note:: Some callables may not be introspectable in certain implementations of - Python. For example, in CPython, built-in functions defined in C provide - no metadata about their arguments. + Python. For example, in CPython, some built-in functions defined in C + provide no metadata about their arguments. .. class:: Signature diff --git a/Lib/inspect.py b/Lib/inspect.py index d6bd8cd..c373276 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1950,6 +1950,28 @@ class Signature: @classmethod def from_builtin(cls, func): + '''Constructs Signature for builtin functions''' + + error_msg = 'no signature for builtin {!r}'.format(func) + + try: + sig = cls._from_builtin(func) + except ValueError: + raise + except Exception: + # May be RuntimeError, TypeError, or something else. + # To make it more predictable, we wrap it in a single + # ValueError. And there is almost no point of having + # error context here, hence 'from None'. + raise ValueError(error_msg) from None + + if sig is None: + raise ValueError(error_msg) + + return sig + + @classmethod + def _from_builtin(cls, func): s = getattr(func, "__text_signature__", None) if not s: return None diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 1bfe724..093b13c 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -1594,9 +1594,6 @@ class TestSignatureObject(unittest.TestCase): @unittest.skipIf(MISSING_C_DOCSTRINGS, "Signature information for builtins requires docstrings") def test_signature_on_builtins(self): - # min doesn't have a signature (yet) - self.assertEqual(inspect.signature(min), None) - signature = inspect.signature(_testcapi.docstring_with_signature_with_defaults) self.assertTrue(isinstance(signature, inspect.Signature)) def p(name): return signature.parameters[name].default @@ -1611,6 +1608,10 @@ class TestSignatureObject(unittest.TestCase): self.assertEqual(p('sys'), sys.maxsize) self.assertEqual(p('exp'), sys.maxsize - 1) + def test_signature_on_builtins_no_signature(self): + with self.assertRaisesRegex(ValueError, 'no signature for builtin'): + inspect.signature(_testcapi.docstring_no_signature) + def test_signature_on_non_function(self): with self.assertRaisesRegex(TypeError, 'is not a callable object'): inspect.signature(42)