diff -r 03ff92b26fa9 Lib/test/test_capi.py --- a/Lib/test/test_capi.py Mon Apr 13 17:48:40 2015 -0400 +++ b/Lib/test/test_capi.py Mon Apr 13 17:18:05 2015 -0500 @@ -123,7 +123,7 @@ 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.__doc__, None) self.assertEqual(_testcapi.docstring_empty.__text_signature__, None) self.assertEqual(_testcapi.docstring_no_signature.__doc__, @@ -150,6 +150,10 @@ "This docstring has a valid signature.") self.assertEqual(_testcapi.docstring_with_signature.__text_signature__, "($module, /, sig)") + self.assertEqual(_testcapi.docstring_with_signature_but_no_doc.__doc__, None) + self.assertEqual(_testcapi.docstring_with_signature_but_no_doc.__text_signature__, + "($module, /, sig)") + self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__doc__, "\nThis docstring has a valid signature and some extra newlines.") self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__text_signature__, diff -r 03ff92b26fa9 Lib/test/test_inspect.py --- a/Lib/test/test_inspect.py Mon Apr 13 17:48:40 2015 -0400 +++ b/Lib/test/test_inspect.py Mon Apr 13 17:18:05 2015 -0500 @@ -1864,6 +1864,9 @@ test_unbound_method(dict.__delitem__) test_unbound_method(property.__delete__) + # Regression test for issue #20586 + test_callable(_testcapi.docstring_with_signature_but_no_doc) + @cpython_only @unittest.skipIf(MISSING_C_DOCSTRINGS, "Signature information for builtins requires docstrings") diff -r 03ff92b26fa9 Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c Mon Apr 13 17:48:40 2015 -0400 +++ b/Modules/_testcapimodule.c Mon Apr 13 17:18:05 2015 -0500 @@ -3083,6 +3083,12 @@ "This docstring has a valid signature." ); +PyDoc_STRVAR(docstring_with_signature_but_no_doc, +"docstring_with_signature_but_no_doc($module, /, sig)\n" +"--\n" +"\n" +); + PyDoc_STRVAR(docstring_with_signature_and_extra_newlines, "docstring_with_signature_and_extra_newlines($module, /, parameter)\n" "--\n" @@ -3635,6 +3641,9 @@ {"docstring_with_signature", (PyCFunction)test_with_docstring, METH_NOARGS, docstring_with_signature}, + {"docstring_with_signature_but_no_doc", + (PyCFunction)test_with_docstring, METH_NOARGS, + docstring_with_signature_but_no_doc}, {"docstring_with_signature_and_extra_newlines", (PyCFunction)test_with_docstring, METH_NOARGS, docstring_with_signature_and_extra_newlines}, diff -r 03ff92b26fa9 Objects/typeobject.c --- a/Objects/typeobject.c Mon Apr 13 17:48:40 2015 -0400 +++ b/Objects/typeobject.c Mon Apr 13 17:18:05 2015 -0500 @@ -137,7 +137,7 @@ { const char *doc = _PyType_DocWithoutSignature(name, internal_doc); - if (!doc) { + if (!doc || *doc == '\0') { Py_INCREF(Py_None); return Py_None; } diff -r 03ff92b26fa9 Tools/clinic/clinic.py --- a/Tools/clinic/clinic.py Mon Apr 13 17:48:40 2015 -0400 +++ b/Tools/clinic/clinic.py Mon Apr 13 17:18:05 2015 -0500 @@ -66,6 +66,8 @@ unknown = Unknown() +sig_end_marker = '--' + _text_accumulator_nt = collections.namedtuple("_text_accumulator", "text append output") @@ -559,8 +561,13 @@ add(quoted_for_c_string(line)) add('\\n"\n') - text.pop() - add('"') + if text[-2] == sig_end_marker: + # If we only have a signature, add the blank line that the + # __text_signature__ getter expects to be there. + add('"\\n"') + else: + text.pop() + add('"') return ''.join(text) def output_templates(self, f): @@ -4015,7 +4022,7 @@ # add(f.return_converter.py_default) if not f.docstring_only: - add("\n--\n") + add("\n" + sig_end_marker + "\n") docstring_first_line = output()