diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index ee76f52289387f..f332e47d38a904 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -172,6 +172,10 @@ The following functions and structs are used to create .. versionadded:: 3.9 + .. versionchanged:: 3.10 + + The function now accepts NULL ``tp_doc`` slot. + .. c:function:: PyObject* PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) Equivalent to ``PyType_FromModuleAndSpec(NULL, spec, bases)``. @@ -263,5 +267,3 @@ The following functions and structs are used to create The desired value of the slot. In most cases, this is a pointer to a function. - - May not be ``NULL``. diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 5150d5770f6644..a4928469881121 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -398,6 +398,13 @@ def __del__(self): del L self.assertEqual(PyList.num, 0) + def test_heap_ctype_doc_and_text_signature(self): + self.assertEqual(_testcapi.HeapDocCType.__doc__, "somedoc") + self.assertEqual(_testcapi.HeapDocCType.__text_signature__, "(arg1, arg2)") + + def test_null_type_doc(self): + self.assertEqual(_testcapi.NullTpDocType.__doc__, None) + def test_subclass_of_heap_gc_ctype_with_tpdealloc_decrefs_once(self): class HeapGcCTypeSubclass(_testcapi.HeapGcCType): def __init__(self): diff --git a/Misc/NEWS.d/next/C API/2020-11-03-19-47-06.bpo-41832.dL1VJJ.rst b/Misc/NEWS.d/next/C API/2020-11-03-19-47-06.bpo-41832.dL1VJJ.rst new file mode 100644 index 00000000000000..e0bce54eb93648 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-11-03-19-47-06.bpo-41832.dL1VJJ.rst @@ -0,0 +1,2 @@ +The :c:func:`PyType_FromModuleAndSpec` function now accepts NULL ``tp_doc`` +slot. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 54c1e62a282414..a1b736505d0a1d 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -6215,6 +6215,47 @@ static PyTypeObject MethodDescriptor2_Type = { .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_VECTORCALL, }; +PyDoc_STRVAR(heapdocctype__doc__, +"HeapDocCType(arg1, arg2)\n" +"--\n" +"\n" +"somedoc"); + +typedef struct { + PyObject_HEAD +} HeapDocCTypeObject; + +static PyType_Slot HeapDocCType_slots[] = { + {Py_tp_doc, (char*)heapdocctype__doc__}, + {0}, +}; + +static PyType_Spec HeapDocCType_spec = { + "_testcapi.HeapDocCType", + sizeof(HeapDocCTypeObject), + 0, + Py_TPFLAGS_DEFAULT, + HeapDocCType_slots +}; + +typedef struct { + PyObject_HEAD +} NullTpDocTypeObject; + +static PyType_Slot NullTpDocType_slots[] = { + {Py_tp_doc, NULL}, + {0, 0}, +}; + +static PyType_Spec NullTpDocType_spec = { + "_testcapi.NullTpDocType", + sizeof(NullTpDocTypeObject), + 0, + Py_TPFLAGS_DEFAULT, + NullTpDocType_slots +}; + + PyDoc_STRVAR(heapgctype__doc__, "A heap type with GC, and with overridden dealloc.\n\n" "The 'value' attribute is set to 10 in __init__."); @@ -6883,6 +6924,20 @@ PyInit__testcapi(void) Py_INCREF(TestError); PyModule_AddObject(m, "error", TestError); + PyObject *HeapDocCType = PyType_FromSpec(&HeapDocCType_spec); + if (HeapDocCType == NULL) { + return NULL; + } + PyModule_AddObject(m, "HeapDocCType", HeapDocCType); + + /* bpo-41832: Add a new type to test PyType_FromSpec() + now can accept a NULL tp_doc slot. */ + PyObject *NullTpDocType = PyType_FromSpec(&NullTpDocType_spec); + if (NullTpDocType == NULL) { + return NULL; + } + PyModule_AddObject(m, "NullTpDocType", NullTpDocType); + PyObject *HeapGcCType = PyType_FromSpec(&HeapGcCType_spec); if (HeapGcCType == NULL) { return NULL; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index acbe3fa3b5a7f1..066ec826905fb7 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3015,6 +3015,10 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) else if (slot->slot == Py_tp_doc) { /* For the docstring slot, which usually points to a static string literal, we need to make a copy */ + if (slot->pfunc == NULL) { + type->tp_doc = NULL; + continue; + } const char *old_doc = _PyType_DocWithoutSignature(type->tp_name, slot->pfunc); size_t len = strlen(old_doc)+1; char *tp_doc = PyObject_MALLOC(len);