From 3940333637b98a2781869977b077552514784529 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 2 Sep 2020 11:29:06 -0500 Subject: [PATCH] closes bpo-41689: Preserve text signature from tp_doc in C heap type creation. (GH-22058) --- Lib/test/test_capi.py | 4 +++ .../2020-09-01-23-39-45.bpo-41689.zxHbLB.rst | 2 ++ Modules/_testcapimodule.c | 30 +++++++++++++++++++ Objects/typeobject.c | 15 ++++++++-- 4 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2020-09-01-23-39-45.bpo-41689.zxHbLB.rst diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 892cc74ec39..db62b47100a 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -401,6 +401,10 @@ class CAPITest(unittest.TestCase): 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_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-09-01-23-39-45.bpo-41689.zxHbLB.rst b/Misc/NEWS.d/next/C API/2020-09-01-23-39-45.bpo-41689.zxHbLB.rst new file mode 100644 index 00000000000..44cf58a4b06 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-09-01-23-39-45.bpo-41689.zxHbLB.rst @@ -0,0 +1,2 @@ +Types created with :c:func:`PyType_FromSpec` now make any signature in their +``tp_doc`` slot accessible from ``__text_signature__``. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 593034ef65e..7536d295350 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -6462,6 +6462,30 @@ 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 +}; + + PyDoc_STRVAR(heapgctype__doc__, "A heap type with GC, and with overridden dealloc.\n\n" "The 'value' attribute is set to 10 in __init__."); @@ -7130,6 +7154,12 @@ 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); + PyObject *HeapGcCType = PyType_FromSpec(&HeapGcCType_spec); if (HeapGcCType == NULL) { return NULL; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index c66f8fcec8e..74040757a07 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3018,15 +3018,14 @@ 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 */ - const char *old_doc = _PyType_DocWithoutSignature(type->tp_name, slot->pfunc); - size_t len = strlen(old_doc)+1; + size_t len = strlen(slot->pfunc)+1; char *tp_doc = PyObject_MALLOC(len); if (tp_doc == NULL) { type->tp_doc = NULL; PyErr_NoMemory(); goto fail; } - memcpy(tp_doc, old_doc, len); + memcpy(tp_doc, slot->pfunc, len); type->tp_doc = tp_doc; } else if (slot->slot == Py_tp_members) { @@ -3058,6 +3057,16 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) res->ht_cached_keys = _PyDict_NewKeysForClass(); } + if (type->tp_doc) { + PyObject *__doc__ = PyUnicode_FromString(_PyType_DocWithoutSignature(type->tp_name, type->tp_doc)); + if (!__doc__) + goto fail; + int ret = _PyDict_SetItemId(type->tp_dict, &PyId___doc__, __doc__); + Py_DECREF(__doc__); + if (ret < 0) + goto fail; + } + if (weaklistoffset) { type->tp_weaklistoffset = weaklistoffset; if (PyDict_DelItemString((PyObject *)type->tp_dict, "__weaklistoffset__") < 0)