]> granicus.if.org Git - python/commitdiff
Issue #20586: Argument Clinic now ensures signatures on functions without docstrings.
authorZachary Ware <zachary.ware@gmail.com>
Mon, 13 Apr 2015 23:22:35 +0000 (18:22 -0500)
committerZachary Ware <zachary.ware@gmail.com>
Mon, 13 Apr 2015 23:22:35 +0000 (18:22 -0500)
Lib/test/test_capi.py
Lib/test/test_inspect.py
Misc/NEWS
Modules/_testcapimodule.c
Modules/cjkcodecs/clinic/multibytecodec.c.h
Modules/clinic/pyexpat.c.h
Objects/typeobject.c
Tools/clinic/clinic.py

index dff717b5e05cfab00b9e4ae02bf524e7056c4beb..367feaa0b89af2f113a23eeeeb1c431f2165a256 100644 (file)
@@ -123,7 +123,7 @@ class CAPITest(unittest.TestCase):
         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 @@ class CAPITest(unittest.TestCase):
             "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__,
index cd051c4183b991a423959cbba9d4defa76813bbc..76f2b47401218e8b43329b3ccf216d34ef826c6b 100644 (file)
@@ -1864,6 +1864,9 @@ class TestSignatureObject(unittest.TestCase):
         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")
index 79ed545695f58f389ea592ae014edce0fca1a60c..969da2cc92000e2cead8b33d79be5f770c2b83ee 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -156,6 +156,9 @@ Tests
 Tools/Demos
 -----------
 
+- Issue #20586: Argument Clinic now ensures that functions without docstrings
+  have signatures.
+
 - Issue #23492: Argument Clinic now generates argument parsing code with
   PyArg_Parse instead of PyArg_ParseTuple if possible.
 
index d6eb6d4509a1e11c67f909ae68999ad86a63c352..d77d1dbcd98ad7f2d75d5b060ba7b3cd26f9d773 100644 (file)
@@ -3083,6 +3083,12 @@ PyDoc_STRVAR(docstring_with_signature,
 "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 @@ static PyMethodDef TestMethods[] = {
     {"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},
index 2f9cb639276420aca34dc4e3398d602c9ea924b1..29b00acd88b470ce62cf747ccfed65bf6e6b7320 100644 (file)
@@ -78,7 +78,8 @@ exit:
 
 PyDoc_STRVAR(_multibytecodec_MultibyteIncrementalEncoder_encode__doc__,
 "encode($self, /, input, final=0)\n"
-"--");
+"--\n"
+"\n");
 
 #define _MULTIBYTECODEC_MULTIBYTEINCREMENTALENCODER_ENCODE_METHODDEF    \
     {"encode", (PyCFunction)_multibytecodec_MultibyteIncrementalEncoder_encode, METH_VARARGS|METH_KEYWORDS, _multibytecodec_MultibyteIncrementalEncoder_encode__doc__},
@@ -106,7 +107,8 @@ exit:
 
 PyDoc_STRVAR(_multibytecodec_MultibyteIncrementalEncoder_reset__doc__,
 "reset($self, /)\n"
-"--");
+"--\n"
+"\n");
 
 #define _MULTIBYTECODEC_MULTIBYTEINCREMENTALENCODER_RESET_METHODDEF    \
     {"reset", (PyCFunction)_multibytecodec_MultibyteIncrementalEncoder_reset, METH_NOARGS, _multibytecodec_MultibyteIncrementalEncoder_reset__doc__},
@@ -122,7 +124,8 @@ _multibytecodec_MultibyteIncrementalEncoder_reset(MultibyteIncrementalEncoderObj
 
 PyDoc_STRVAR(_multibytecodec_MultibyteIncrementalDecoder_decode__doc__,
 "decode($self, /, input, final=0)\n"
-"--");
+"--\n"
+"\n");
 
 #define _MULTIBYTECODEC_MULTIBYTEINCREMENTALDECODER_DECODE_METHODDEF    \
     {"decode", (PyCFunction)_multibytecodec_MultibyteIncrementalDecoder_decode, METH_VARARGS|METH_KEYWORDS, _multibytecodec_MultibyteIncrementalDecoder_decode__doc__},
@@ -154,7 +157,8 @@ exit:
 
 PyDoc_STRVAR(_multibytecodec_MultibyteIncrementalDecoder_reset__doc__,
 "reset($self, /)\n"
-"--");
+"--\n"
+"\n");
 
 #define _MULTIBYTECODEC_MULTIBYTEINCREMENTALDECODER_RESET_METHODDEF    \
     {"reset", (PyCFunction)_multibytecodec_MultibyteIncrementalDecoder_reset, METH_NOARGS, _multibytecodec_MultibyteIncrementalDecoder_reset__doc__},
@@ -170,7 +174,8 @@ _multibytecodec_MultibyteIncrementalDecoder_reset(MultibyteIncrementalDecoderObj
 
 PyDoc_STRVAR(_multibytecodec_MultibyteStreamReader_read__doc__,
 "read($self, sizeobj=None, /)\n"
-"--");
+"--\n"
+"\n");
 
 #define _MULTIBYTECODEC_MULTIBYTESTREAMREADER_READ_METHODDEF    \
     {"read", (PyCFunction)_multibytecodec_MultibyteStreamReader_read, METH_VARARGS, _multibytecodec_MultibyteStreamReader_read__doc__},
@@ -196,7 +201,8 @@ exit:
 
 PyDoc_STRVAR(_multibytecodec_MultibyteStreamReader_readline__doc__,
 "readline($self, sizeobj=None, /)\n"
-"--");
+"--\n"
+"\n");
 
 #define _MULTIBYTECODEC_MULTIBYTESTREAMREADER_READLINE_METHODDEF    \
     {"readline", (PyCFunction)_multibytecodec_MultibyteStreamReader_readline, METH_VARARGS, _multibytecodec_MultibyteStreamReader_readline__doc__},
@@ -222,7 +228,8 @@ exit:
 
 PyDoc_STRVAR(_multibytecodec_MultibyteStreamReader_readlines__doc__,
 "readlines($self, sizehintobj=None, /)\n"
-"--");
+"--\n"
+"\n");
 
 #define _MULTIBYTECODEC_MULTIBYTESTREAMREADER_READLINES_METHODDEF    \
     {"readlines", (PyCFunction)_multibytecodec_MultibyteStreamReader_readlines, METH_VARARGS, _multibytecodec_MultibyteStreamReader_readlines__doc__},
@@ -248,7 +255,8 @@ exit:
 
 PyDoc_STRVAR(_multibytecodec_MultibyteStreamReader_reset__doc__,
 "reset($self, /)\n"
-"--");
+"--\n"
+"\n");
 
 #define _MULTIBYTECODEC_MULTIBYTESTREAMREADER_RESET_METHODDEF    \
     {"reset", (PyCFunction)_multibytecodec_MultibyteStreamReader_reset, METH_NOARGS, _multibytecodec_MultibyteStreamReader_reset__doc__},
@@ -264,21 +272,24 @@ _multibytecodec_MultibyteStreamReader_reset(MultibyteStreamReaderObject *self, P
 
 PyDoc_STRVAR(_multibytecodec_MultibyteStreamWriter_write__doc__,
 "write($self, strobj, /)\n"
-"--");
+"--\n"
+"\n");
 
 #define _MULTIBYTECODEC_MULTIBYTESTREAMWRITER_WRITE_METHODDEF    \
     {"write", (PyCFunction)_multibytecodec_MultibyteStreamWriter_write, METH_O, _multibytecodec_MultibyteStreamWriter_write__doc__},
 
 PyDoc_STRVAR(_multibytecodec_MultibyteStreamWriter_writelines__doc__,
 "writelines($self, lines, /)\n"
-"--");
+"--\n"
+"\n");
 
 #define _MULTIBYTECODEC_MULTIBYTESTREAMWRITER_WRITELINES_METHODDEF    \
     {"writelines", (PyCFunction)_multibytecodec_MultibyteStreamWriter_writelines, METH_O, _multibytecodec_MultibyteStreamWriter_writelines__doc__},
 
 PyDoc_STRVAR(_multibytecodec_MultibyteStreamWriter_reset__doc__,
 "reset($self, /)\n"
-"--");
+"--\n"
+"\n");
 
 #define _MULTIBYTECODEC_MULTIBYTESTREAMWRITER_RESET_METHODDEF    \
     {"reset", (PyCFunction)_multibytecodec_MultibyteStreamWriter_reset, METH_NOARGS, _multibytecodec_MultibyteStreamWriter_reset__doc__},
@@ -294,8 +305,9 @@ _multibytecodec_MultibyteStreamWriter_reset(MultibyteStreamWriterObject *self, P
 
 PyDoc_STRVAR(_multibytecodec___create_codec__doc__,
 "__create_codec($module, arg, /)\n"
-"--");
+"--\n"
+"\n");
 
 #define _MULTIBYTECODEC___CREATE_CODEC_METHODDEF    \
     {"__create_codec", (PyCFunction)_multibytecodec___create_codec, METH_O, _multibytecodec___create_codec__doc__},
-/*[clinic end generated code: output=dff1459dec464796 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=0ea29cd57f7cbc1a input=a9049054013a1b77]*/
index d4a3a4b8ce48f9c55099b845ba611e82f55a987c..707cc0c8c3b0c7739a48375652e67c252bbfe120 100644 (file)
@@ -209,7 +209,8 @@ exit:
 
 PyDoc_STRVAR(pyexpat_xmlparser___dir____doc__,
 "__dir__($self, /)\n"
-"--");
+"--\n"
+"\n");
 
 #define PYEXPAT_XMLPARSER___DIR___METHODDEF    \
     {"__dir__", (PyCFunction)pyexpat_xmlparser___dir__, METH_NOARGS, pyexpat_xmlparser___dir____doc__},
@@ -286,4 +287,4 @@ exit:
 #ifndef PYEXPAT_XMLPARSER_USEFOREIGNDTD_METHODDEF
     #define PYEXPAT_XMLPARSER_USEFOREIGNDTD_METHODDEF
 #endif /* !defined(PYEXPAT_XMLPARSER_USEFOREIGNDTD_METHODDEF) */
-/*[clinic end generated code: output=9715b916f2d618fa input=a9049054013a1b77]*/
+/*[clinic end generated code: output=e5993de4e9dd2236 input=a9049054013a1b77]*/
index 0e54fe60b0f4e628f7c9acf22f69d1764d936c03..4b992878bf6fd213200df75c268acb44f98dc329 100644 (file)
@@ -137,7 +137,7 @@ _PyType_GetDocFromInternalDoc(const char *name, const char *internal_doc)
 {
     const char *doc = _PyType_DocWithoutSignature(name, internal_doc);
 
-    if (!doc) {
+    if (!doc || *doc == '\0') {
         Py_INCREF(Py_None);
         return Py_None;
     }
index 9623ab44663f2b0d6624523059e5218a601d0c10..99f5c3d60af40eebca560fc59082db8fd0f4b49b 100755 (executable)
@@ -66,6 +66,8 @@ class Unknown:
 
 unknown = Unknown()
 
+sig_end_marker = '--'
+
 
 _text_accumulator_nt = collections.namedtuple("_text_accumulator", "text append output")
 
@@ -559,8 +561,13 @@ class CLanguage(Language):
             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 @@ class DSLParser:
         #     add(f.return_converter.py_default)
 
         if not f.docstring_only:
-            add("\n--\n")
+            add("\n" + sig_end_marker + "\n")
 
         docstring_first_line = output()