From e604f311822e7d97f75563267ceb37014b8c114f Mon Sep 17 00:00:00 2001 From: Hai Shi Date: Tue, 3 Nov 2020 19:57:05 +0800 Subject: [PATCH 1/6] PyType_FromModuleAndSpec() can accept tp_doc=NULL --- Doc/whatsnew/3.10.rst | 3 +++ .../next/C API/2020-11-03-19-47-06.bpo-41832.dL1VJJ.rst | 1 + Modules/_testcapimodule.c | 5 +++++ Objects/typeobject.c | 6 ++++++ 4 files changed, 15 insertions(+) create mode 100644 Misc/NEWS.d/next/C API/2020-11-03-19-47-06.bpo-41832.dL1VJJ.rst diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 60dee0c6bd1651..8a16c6a7e86fb3 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -366,6 +366,9 @@ New Features * Added :c:func:`PyUnicode_AsUTF8AndSize` to the limited C API. (Contributed by Alex Gaynor in :issue:`41784`.) +* The :c:func:`PyType_FromModuleAndSpec` function can accept tp_doc=NULL. + (Contributed by Hai Shi in :issue:`41832`.) + Porting to Python 3.10 ---------------------- 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..76253b7180f209 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-11-03-19-47-06.bpo-41832.dL1VJJ.rst @@ -0,0 +1 @@ +:c:func:`PyType_FromModuleAndSpec` can accept tp_doc=NULL. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 28d2c124d51775..3f5dc22395131e 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3987,6 +3987,11 @@ test_structseq_newtype_doesnt_leak(PyObject *Py_UNUSED(self), assert(PyType_FastSubclass(structseq_type, Py_TPFLAGS_TUPLE_SUBCLASS)); Py_DECREF(structseq_type); + descr.doc = NULL; + structseq_type = PyStructSequence_NewType(&descr); + assert(structseq_type != NULL); + Py_DECREF(structseq_type); + Py_RETURN_NONE; } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index bd1587ace876f5..e70ac1783cd72a 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3012,6 +3012,12 @@ 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 */ + + /* bpo-41832: PyType_FromModuleAndSpec() can accept tp_doc=NULL. */ + if (slot->pfunc == NULL) { + type->tp_doc = NULL; + continue; + } size_t len = strlen(slot->pfunc)+1; char *tp_doc = PyObject_MALLOC(len); if (tp_doc == NULL) { From dabfb676234fc23c98dbca8dbf50c7c9282990c3 Mon Sep 17 00:00:00 2001 From: Hai Shi Date: Wed, 4 Nov 2020 00:22:28 +0800 Subject: [PATCH 2/6] apply victor's comment --- Doc/whatsnew/3.10.rst | 3 ++- Modules/_lsprof.c | 2 -- Modules/_testcapimodule.c | 19 +++++++++++++++---- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 8a16c6a7e86fb3..21058c733c7b4f 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -366,7 +366,8 @@ New Features * Added :c:func:`PyUnicode_AsUTF8AndSize` to the limited C API. (Contributed by Alex Gaynor in :issue:`41784`.) -* The :c:func:`PyType_FromModuleAndSpec` function can accept tp_doc=NULL. +* The :c:func:`PyType_FromModuleAndSpec` function now accepts NULL ``tp_doc`` + slot. (Contributed by Hai Shi in :issue:`41832`.) diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index 78d464d1481d75..a438c0457b85a3 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -489,14 +489,12 @@ static PyStructSequence_Field profiler_subentry_fields[] = { static PyStructSequence_Desc profiler_entry_desc = { .name = "_lsprof.profiler_entry", - .doc = "", .fields = profiler_entry_fields, .n_in_sequence = 6 }; static PyStructSequence_Desc profiler_subentry_desc = { .name = "_lsprof.profiler_subentry", - .doc = "", .fields = profiler_subentry_fields, .n_in_sequence = 5 }; diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 3f5dc22395131e..c371c824ea39a7 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3987,10 +3987,20 @@ test_structseq_newtype_doesnt_leak(PyObject *Py_UNUSED(self), assert(PyType_FastSubclass(structseq_type, Py_TPFLAGS_TUPLE_SUBCLASS)); Py_DECREF(structseq_type); - descr.doc = NULL; - structseq_type = PyStructSequence_NewType(&descr); - assert(structseq_type != NULL); - Py_DECREF(structseq_type); + Py_RETURN_NONE; +} + +static PyType_Spec HeapDocCType_spec; + +static PyObject * +test_PyType_FromSpec(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) +{ + void *tp_doc = HeapDocCType_spec.slots[0].pfunc; + HeapDocCType_spec.slots[0].pfunc = NULL; + PyObject *HeapDocCType = PyType_FromSpec(&HeapDocCType_spec); + assert(HeapDocCType != NULL); + HeapDocCType_spec.slots[0].pfunc = tp_doc; + Py_DECREF(HeapDocCType); Py_RETURN_NONE; } @@ -5606,6 +5616,7 @@ static PyMethodDef TestMethods[] = { {"test_decref_doesnt_leak", test_decref_doesnt_leak, METH_NOARGS}, {"test_structseq_newtype_doesnt_leak", test_structseq_newtype_doesnt_leak, METH_NOARGS}, + {"test_PyType_FromSpec", test_PyType_FromSpec, METH_NOARGS}, {"test_incref_decref_API", test_incref_decref_API, METH_NOARGS}, {"test_long_and_overflow", test_long_and_overflow, METH_NOARGS}, {"test_long_as_double", test_long_as_double, METH_NOARGS}, From 1f5eec340388667c536fba92518fbbfda6aac530 Mon Sep 17 00:00:00 2001 From: Hai Shi Date: Wed, 4 Nov 2020 12:47:27 +0800 Subject: [PATCH 3/6] apply victor's comment --- Lib/test/test_capi.py | 3 ++ .../2020-11-03-19-47-06.bpo-41832.dL1VJJ.rst | 3 +- Modules/_lsprof.c | 2 + Modules/_testcapimodule.c | 39 +++++++++++-------- 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index db62b47100ad3a..a4ebe4a0a1b5cb 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -405,6 +405,9 @@ 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 index 76253b7180f209..e0bce54eb93648 100644 --- 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 @@ -1 +1,2 @@ -:c:func:`PyType_FromModuleAndSpec` can accept tp_doc=NULL. +The :c:func:`PyType_FromModuleAndSpec` function now accepts NULL ``tp_doc`` +slot. diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index a438c0457b85a3..c32699cb8ad580 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -490,12 +490,14 @@ static PyStructSequence_Field profiler_subentry_fields[] = { static PyStructSequence_Desc profiler_entry_desc = { .name = "_lsprof.profiler_entry", .fields = profiler_entry_fields, + .doc = NULL, .n_in_sequence = 6 }; static PyStructSequence_Desc profiler_subentry_desc = { .name = "_lsprof.profiler_subentry", .fields = profiler_subentry_fields, + .doc = NULL, .n_in_sequence = 5 }; diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index c371c824ea39a7..afb490aefe4923 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3990,21 +3990,6 @@ test_structseq_newtype_doesnt_leak(PyObject *Py_UNUSED(self), Py_RETURN_NONE; } -static PyType_Spec HeapDocCType_spec; - -static PyObject * -test_PyType_FromSpec(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) -{ - void *tp_doc = HeapDocCType_spec.slots[0].pfunc; - HeapDocCType_spec.slots[0].pfunc = NULL; - PyObject *HeapDocCType = PyType_FromSpec(&HeapDocCType_spec); - assert(HeapDocCType != NULL); - HeapDocCType_spec.slots[0].pfunc = tp_doc; - Py_DECREF(HeapDocCType); - - Py_RETURN_NONE; -} - static PyObject * test_incref_decref_API(PyObject *ob, PyObject *Py_UNUSED(ignored)) { @@ -5616,7 +5601,6 @@ static PyMethodDef TestMethods[] = { {"test_decref_doesnt_leak", test_decref_doesnt_leak, METH_NOARGS}, {"test_structseq_newtype_doesnt_leak", test_structseq_newtype_doesnt_leak, METH_NOARGS}, - {"test_PyType_FromSpec", test_PyType_FromSpec, METH_NOARGS}, {"test_incref_decref_API", test_incref_decref_API, METH_NOARGS}, {"test_long_and_overflow", test_long_and_overflow, METH_NOARGS}, {"test_long_as_double", test_long_as_double, METH_NOARGS}, @@ -6524,6 +6508,23 @@ static PyType_Spec HeapDocCType_spec = { 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" @@ -7199,6 +7200,12 @@ PyInit__testcapi(void) } PyModule_AddObject(m, "HeapDocCType", HeapDocCType); + 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; From d0aafe3829aa663e3496831992d61ccc6bd54183 Mon Sep 17 00:00:00 2001 From: Hai Shi Date: Wed, 4 Nov 2020 20:30:09 +0800 Subject: [PATCH 4/6] apply petr's comment --- Doc/c-api/type.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 73f26875d8194a..6d353683034087 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -260,4 +260,7 @@ 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``. + .. versionchanged:: 3.10 + + The ``Py_tp_doc`` slot now can accepts ``NULL`` value. + Previously, the slot may not be ``NULL``. From cca7bc7edf11a7d212ae70dcff6f10cf96fab470 Mon Sep 17 00:00:00 2001 From: Hai Shi Date: Thu, 5 Nov 2020 12:53:20 +0800 Subject: [PATCH 5/6] apply victor's comment --- Doc/c-api/type.rst | 10 +++++----- Modules/_testcapimodule.c | 2 ++ Objects/typeobject.c | 2 -- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 6d353683034087..e9c4def9e7fa7f 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -169,6 +169,11 @@ The following functions and structs are used to create .. versionadded:: 3.9 + .. versionchanged:: 3.10 + + The ``Py_tp_doc`` slot now can accepts ``NULL`` value. + Previously, the slot may not be ``NULL``. + .. c:function:: PyObject* PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) Equivalent to ``PyType_FromModuleAndSpec(NULL, spec, bases)``. @@ -259,8 +264,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. - - .. versionchanged:: 3.10 - - The ``Py_tp_doc`` slot now can accepts ``NULL`` value. - Previously, the slot may not be ``NULL``. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index afb490aefe4923..22d20d220d4089 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -7200,6 +7200,8 @@ PyInit__testcapi(void) } 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; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index e70ac1783cd72a..b7eefd53981072 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3012,8 +3012,6 @@ 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 */ - - /* bpo-41832: PyType_FromModuleAndSpec() can accept tp_doc=NULL. */ if (slot->pfunc == NULL) { type->tp_doc = NULL; continue; From b452c0563956eae644dfc74b330409911d90c3f4 Mon Sep 17 00:00:00 2001 From: Hai Shi Date: Fri, 6 Nov 2020 12:20:20 +0800 Subject: [PATCH 6/6] apply victor's comment --- Doc/c-api/type.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index e9c4def9e7fa7f..fcd92e38e2428e 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -171,8 +171,7 @@ The following functions and structs are used to create .. versionchanged:: 3.10 - The ``Py_tp_doc`` slot now can accepts ``NULL`` value. - Previously, the slot may not be ``NULL``. + The function now accepts NULL ``tp_doc`` slot. .. c:function:: PyObject* PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)