Issue #19674: inspect.signature() now produces a correct signature
for some builtins.
diff --git a/Lib/inspect.py b/Lib/inspect.py
index edbf927..dc94e44 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -31,6 +31,7 @@
 __author__ = ('Ka-Ping Yee <ping@lfw.org>',
               'Yury Selivanov <yselivanov@sprymix.com>')
 
+import ast
 import importlib.machinery
 import itertools
 import linecache
@@ -1461,6 +1462,9 @@
     if isinstance(obj, types.FunctionType):
         return Signature.from_function(obj)
 
+    if isinstance(obj, types.BuiltinFunctionType):
+        return Signature.from_builtin(obj)
+
     if isinstance(obj, functools.partial):
         sig = signature(obj.func)
 
@@ -1942,6 +1946,64 @@
                    return_annotation=annotations.get('return', _empty),
                    __validate_parameters__=False)
 
+    @classmethod
+    def from_builtin(cls, func):
+        s = getattr(func, "__text_signature__", None)
+        if not s:
+            return None
+
+        if s.endswith("/)"):
+            kind = Parameter.POSITIONAL_ONLY
+            s = s[:-2] + ')'
+        else:
+            kind = Parameter.POSITIONAL_OR_KEYWORD
+
+        s = "def foo" + s + ": pass"
+
+        try:
+            module = ast.parse(s)
+        except SyntaxError:
+            return None
+        if not isinstance(module, ast.Module):
+            return None
+
+        # ast.FunctionDef
+        f = module.body[0]
+
+        parameters = []
+        empty = Parameter.empty
+
+        def p(name_node, default_node, default=empty):
+            name = name_node.arg
+
+            if isinstance(default_node, ast.Num):
+                default = default.n
+            elif isinstance(default_node, ast.NameConstant):
+                default = default_node.value
+            parameters.append(Parameter(name, kind, default=default, annotation=empty))
+
+        # non-keyword-only parameters
+        for name, default in reversed(list(itertools.zip_longest(reversed(f.args.args), reversed(f.args.defaults), fillvalue=None))):
+            p(name, default)
+
+        # *args
+        if f.args.vararg:
+            kind = Parameter.VAR_POSITIONAL
+            p(f.args.vararg, empty)
+
+        # keyword-only arguments
+        kind = Parameter.KEYWORD_ONLY
+        for name, default in zip(f.args.kwonlyargs, f.args.kw_defaults):
+            p(name, default)
+
+        # **kwargs
+        if f.args.kwarg:
+            kind = Parameter.VAR_KEYWORD
+            p(f.args.kwarg, empty)
+
+        return cls(parameters, return_annotation=cls.empty)
+
+
     @property
     def parameters(self):
         return self._parameters
diff --git a/Lib/pydoc.py b/Lib/pydoc.py
index e8127a2..2e63226 100755
--- a/Lib/pydoc.py
+++ b/Lib/pydoc.py
@@ -916,20 +916,18 @@
                 reallink = realname
             title = '<a name="%s"><strong>%s</strong></a> = %s' % (
                 anchor, name, reallink)
-        if inspect.isfunction(object):
-            args, varargs, kwonlyargs, kwdefaults, varkw, defaults, ann = \
-                inspect.getfullargspec(object)
-            argspec = inspect.formatargspec(
-                args, varargs, kwonlyargs, kwdefaults, varkw, defaults, ann,
-                formatvalue=self.formatvalue,
-                formatannotation=inspect.formatannotationrelativeto(object))
-            if realname == '<lambda>':
-                title = '<strong>%s</strong> <em>lambda</em> ' % name
-                # XXX lambda's won't usually have func_annotations['return']
-                # since the syntax doesn't support but it is possible.
-                # So removing parentheses isn't truly safe.
-                argspec = argspec[1:-1] # remove parentheses
-        else:
+        argspec = None
+        if inspect.isfunction(object) or inspect.isbuiltin(object):
+            signature = inspect.signature(object)
+            if signature:
+                argspec = str(signature)
+                if realname == '<lambda>':
+                    title = '<strong>%s</strong> <em>lambda</em> ' % name
+                    # XXX lambda's won't usually have func_annotations['return']
+                    # since the syntax doesn't support but it is possible.
+                    # So removing parentheses isn't truly safe.
+                    argspec = argspec[1:-1] # remove parentheses
+        if not argspec:
             argspec = '(...)'
 
         decl = title + argspec + (note and self.grey(
@@ -1313,20 +1311,18 @@
                 cl.__dict__[realname] is object):
                 skipdocs = 1
             title = self.bold(name) + ' = ' + realname
-        if inspect.isfunction(object):
-            args, varargs, varkw, defaults, kwonlyargs, kwdefaults, ann = \
-              inspect.getfullargspec(object)
-            argspec = inspect.formatargspec(
-                args, varargs, varkw, defaults, kwonlyargs, kwdefaults, ann,
-                formatvalue=self.formatvalue,
-                formatannotation=inspect.formatannotationrelativeto(object))
-            if realname == '<lambda>':
-                title = self.bold(name) + ' lambda '
-                # XXX lambda's won't usually have func_annotations['return']
-                # since the syntax doesn't support but it is possible.
-                # So removing parentheses isn't truly safe.
-                argspec = argspec[1:-1] # remove parentheses
-        else:
+        argspec = None
+        if inspect.isfunction(object) or inspect.isbuiltin(object):
+            signature = inspect.signature(object)
+            if signature:
+                argspec = str(signature)
+                if realname == '<lambda>':
+                    title = self.bold(name) + ' lambda '
+                    # XXX lambda's won't usually have func_annotations['return']
+                    # since the syntax doesn't support but it is possible.
+                    # So removing parentheses isn't truly safe.
+                    argspec = argspec[1:-1] # remove parentheses
+        if not argspec:
             argspec = '(...)'
         decl = title + argspec + note
 
diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
index 000079e..6f75b77 100644
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -109,6 +109,35 @@
         self.assertRaises(TypeError, _posixsubprocess.fork_exec,
                           Z(),[b'1'],3,[1, 2],5,6,7,8,9,10,11,12,13,14,15,16,17)
 
+    def test_docstring_signature_parsing(self):
+
+        self.assertEqual(_testcapi.no_docstring.__doc__, None)
+        self.assertEqual(_testcapi.no_docstring.__text_signature__, None)
+
+        self.assertEqual(_testcapi.docstring_empty.__doc__, "")
+        self.assertEqual(_testcapi.docstring_empty.__text_signature__, None)
+
+        self.assertEqual(_testcapi.docstring_no_signature.__doc__,
+            "This docstring has no signature.")
+        self.assertEqual(_testcapi.docstring_no_signature.__text_signature__, None)
+
+        self.assertEqual(_testcapi.docstring_with_invalid_signature.__doc__,
+            "docstring_with_invalid_signature (boo)\n"
+            "\n"
+            "This docstring has an invalid signature."
+            )
+        self.assertEqual(_testcapi.docstring_with_invalid_signature.__text_signature__, None)
+
+        self.assertEqual(_testcapi.docstring_with_signature.__doc__,
+            "This docstring has a valid signature.")
+        self.assertEqual(_testcapi.docstring_with_signature.__text_signature__, "(sig)")
+
+        self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__doc__,
+            "This docstring has a valid signature and some extra newlines.")
+        self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__text_signature__,
+            "(parameter)")
+
+
 @unittest.skipUnless(threading, 'Threading required for this test.')
 class TestPendingCalls(unittest.TestCase):
 
diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py
index 9d34904..0258d3df 100644
--- a/Lib/test/test_inspect.py
+++ b/Lib/test/test_inspect.py
@@ -1588,10 +1588,9 @@
         with self.assertRaisesRegex(ValueError, 'not supported by signature'):
             # support for 'method-wrapper'
             inspect.signature(min.__call__)
-        with self.assertRaisesRegex(ValueError,
-                                     'no signature found for builtin function'):
-            # support for 'method-wrapper'
-            inspect.signature(min)
+        self.assertEqual(inspect.signature(min), None)
+        signature = inspect.signature(os.stat)
+        self.assertTrue(isinstance(signature, inspect.Signature))
 
     def test_signature_on_non_function(self):
         with self.assertRaisesRegex(TypeError, 'is not a callable object'):