closes bpo-41689: Preserve text signature from tp_doc in C heap type creation. (GH-22058)

This commit is contained in:
Benjamin Peterson 2020-09-02 11:29:06 -05:00 committed by GitHub
parent 5a4a963a6c
commit 3940333637
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 48 additions and 3 deletions

View File

@ -401,6 +401,10 @@ class CAPITest(unittest.TestCase):
del L del L
self.assertEqual(PyList.num, 0) 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): def test_subclass_of_heap_gc_ctype_with_tpdealloc_decrefs_once(self):
class HeapGcCTypeSubclass(_testcapi.HeapGcCType): class HeapGcCTypeSubclass(_testcapi.HeapGcCType):
def __init__(self): def __init__(self):

View File

@ -0,0 +1,2 @@
Types created with :c:func:`PyType_FromSpec` now make any signature in their
``tp_doc`` slot accessible from ``__text_signature__``.

View File

@ -6462,6 +6462,30 @@ static PyTypeObject MethodDescriptor2_Type = {
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_VECTORCALL, .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__, PyDoc_STRVAR(heapgctype__doc__,
"A heap type with GC, and with overridden dealloc.\n\n" "A heap type with GC, and with overridden dealloc.\n\n"
"The 'value' attribute is set to 10 in __init__."); "The 'value' attribute is set to 10 in __init__.");
@ -7130,6 +7154,12 @@ PyInit__testcapi(void)
Py_INCREF(TestError); Py_INCREF(TestError);
PyModule_AddObject(m, "error", 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); PyObject *HeapGcCType = PyType_FromSpec(&HeapGcCType_spec);
if (HeapGcCType == NULL) { if (HeapGcCType == NULL) {
return NULL; return NULL;

View File

@ -3018,15 +3018,14 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
else if (slot->slot == Py_tp_doc) { else if (slot->slot == Py_tp_doc) {
/* For the docstring slot, which usually points to a static string /* For the docstring slot, which usually points to a static string
literal, we need to make a copy */ literal, we need to make a copy */
const char *old_doc = _PyType_DocWithoutSignature(type->tp_name, slot->pfunc); size_t len = strlen(slot->pfunc)+1;
size_t len = strlen(old_doc)+1;
char *tp_doc = PyObject_MALLOC(len); char *tp_doc = PyObject_MALLOC(len);
if (tp_doc == NULL) { if (tp_doc == NULL) {
type->tp_doc = NULL; type->tp_doc = NULL;
PyErr_NoMemory(); PyErr_NoMemory();
goto fail; goto fail;
} }
memcpy(tp_doc, old_doc, len); memcpy(tp_doc, slot->pfunc, len);
type->tp_doc = tp_doc; type->tp_doc = tp_doc;
} }
else if (slot->slot == Py_tp_members) { 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(); 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) { if (weaklistoffset) {
type->tp_weaklistoffset = weaklistoffset; type->tp_weaklistoffset = weaklistoffset;
if (PyDict_DelItemString((PyObject *)type->tp_dict, "__weaklistoffset__") < 0) if (PyDict_DelItemString((PyObject *)type->tp_dict, "__weaklistoffset__") < 0)