]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-93466: Document PyType_Spec doesn't accept repeated slot IDs; raise where this...
authorPetr Viktorin <encukou@gmail.com>
Fri, 10 Jun 2022 13:55:09 +0000 (15:55 +0200)
committerGitHub <noreply@github.com>
Fri, 10 Jun 2022 13:55:09 +0000 (15:55 +0200)
Doc/c-api/type.rst
Lib/test/test_capi.py
Misc/NEWS.d/next/C API/2022-06-03-14-54-41.gh-issue-93466.DDtH0X.rst [new file with mode: 0644]
Modules/_testcapimodule.c
Objects/typeobject.c

index fece3e6e642ff58b1649dbae44d5115b715408d6..aa77c285e3b8299351bb679dbcf615640e46d293 100644 (file)
@@ -296,6 +296,8 @@ The following functions and structs are used to create
       Array of :c:type:`PyType_Slot` structures.
       Terminated by the special slot value ``{0, NULL}``.
 
+      Each slot ID should be specified at most once.
+
 .. c:type:: PyType_Slot
 
    Structure defining optional functionality of a type, containing a slot ID
index 95930ba40ceb519badcf653859314ff4360d138d..cd6a4de47a73d8594fc1385a0e0ee00a0e43188d 100644 (file)
@@ -618,6 +618,12 @@ class CAPITest(unittest.TestCase):
         with self.assertRaisesRegex(TypeError, msg):
             t = _testcapi.pytype_fromspec_meta(_testcapi.HeapCTypeMetaclassCustomNew)
 
+    def test_pytype_fromspec_with_repeated_slots(self):
+        for variant in range(2):
+            with self.subTest(variant=variant):
+                with self.assertRaises(SystemError):
+                    _testcapi.create_type_from_repeated_slots(variant)
+
     def test_pynumber_tobase(self):
         from _testcapi import pynumber_tobase
         self.assertEqual(pynumber_tobase(123, 2), '0b1111011')
diff --git a/Misc/NEWS.d/next/C API/2022-06-03-14-54-41.gh-issue-93466.DDtH0X.rst b/Misc/NEWS.d/next/C API/2022-06-03-14-54-41.gh-issue-93466.DDtH0X.rst
new file mode 100644 (file)
index 0000000..0bb65ea
--- /dev/null
@@ -0,0 +1,3 @@
+Slot IDs in PyType_Spec may not be repeated. The documentation was updated
+to mention this. For some cases of repeated slots, PyType_FromSpec and
+related functions will now raise an exception.
index b75e03c0c068d5958df58daae264eb84822c3bf4..33dc3dbe493c515c31b21d7e0827d3a7b0c431d0 100644 (file)
@@ -1482,6 +1482,63 @@ test_type_from_ephemeral_spec(PyObject *self, PyObject *Py_UNUSED(ignored))
     return result;
 }
 
+PyType_Slot repeated_doc_slots[] = {
+    {Py_tp_doc, "A class used for testsĀ·"},
+    {Py_tp_doc, "A class used for tests"},
+    {0, 0},
+};
+
+PyType_Spec repeated_doc_slots_spec = {
+    .name = "RepeatedDocSlotClass",
+    .basicsize = sizeof(PyObject),
+    .slots = repeated_doc_slots,
+};
+
+typedef struct {
+    PyObject_HEAD
+    int data;
+} HeapCTypeWithDataObject;
+
+
+static struct PyMemberDef members_to_repeat[] = {
+    {"T_INT", T_INT, offsetof(HeapCTypeWithDataObject, data), 0, NULL},
+    {NULL}
+};
+
+PyType_Slot repeated_members_slots[] = {
+    {Py_tp_members, members_to_repeat},
+    {Py_tp_members, members_to_repeat},
+    {0, 0},
+};
+
+PyType_Spec repeated_members_slots_spec = {
+    .name = "RepeatedMembersSlotClass",
+    .basicsize = sizeof(HeapCTypeWithDataObject),
+    .slots = repeated_members_slots,
+};
+
+static PyObject *
+create_type_from_repeated_slots(PyObject *self, PyObject *variant_obj)
+{
+    PyObject *class = NULL;
+    int variant = PyLong_AsLong(variant_obj);
+    if (PyErr_Occurred()) {
+        return NULL;
+    }
+    switch (variant) {
+        case 0:
+            class = PyType_FromSpec(&repeated_doc_slots_spec);
+            break;
+        case 1:
+            class = PyType_FromSpec(&repeated_members_slots_spec);
+            break;
+        default:
+            PyErr_SetString(PyExc_ValueError, "bad test variant");
+            break;
+        }
+    return class;
+}
+
 
 static PyObject *
 test_get_type_qualname(PyObject *self, PyObject *Py_UNUSED(ignored))
@@ -6107,6 +6164,8 @@ static PyMethodDef TestMethods[] = {
     {"test_get_type_name",        test_get_type_name,            METH_NOARGS},
     {"test_get_type_qualname",    test_get_type_qualname,        METH_NOARGS},
     {"test_type_from_ephemeral_spec", test_type_from_ephemeral_spec, METH_NOARGS},
+    {"create_type_from_repeated_slots",
+        create_type_from_repeated_slots, METH_O},
     {"test_from_spec_metatype_inheritance", test_from_spec_metatype_inheritance,
      METH_NOARGS},
     {"test_from_spec_invalid_metatype_inheritance",
index 51dc5e34c81a25f5b3a1745e4fc508ab8e3a9066..e57651446f888f0e275fe5cbf4eb8d43b803feb2 100644 (file)
@@ -3409,14 +3409,20 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module,
     int r;
 
     const PyType_Slot *slot;
-    Py_ssize_t nmembers, weaklistoffset, dictoffset, vectorcalloffset;
+    Py_ssize_t nmembers = 0;
+    Py_ssize_t weaklistoffset, dictoffset, vectorcalloffset;
     char *res_start;
     short slot_offset, subslot_offset;
 
     nmembers = weaklistoffset = dictoffset = vectorcalloffset = 0;
     for (slot = spec->slots; slot->slot; slot++) {
         if (slot->slot == Py_tp_members) {
-            nmembers = 0;
+            if (nmembers != 0) {
+                PyErr_SetString(
+                    PyExc_SystemError,
+                    "Multiple Py_tp_members slots are not supported.");
+                return NULL;
+            }
             for (const PyMemberDef *memb = slot->pfunc; memb->name != NULL; memb++) {
                 nmembers++;
                 if (strcmp(memb->name, "__weaklistoffset__") == 0) {
@@ -3559,6 +3565,12 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module,
         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 (type->tp_doc != NULL) {
+                PyErr_SetString(
+                    PyExc_SystemError,
+                    "Multiple Py_tp_doc slots are not supported.");
+                return NULL;
+            }
             if (slot->pfunc == NULL) {
                 type->tp_doc = NULL;
                 continue;