summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Viktorin <encukou@gmail.com>2022-06-10 13:55:09 (GMT)
committerGitHub <noreply@github.com>2022-06-10 13:55:09 (GMT)
commit21a9a85ff4d62e8fc5a51b8eb56154f32c319b77 (patch)
treecb04c35fb6d1fde8fbe975dee5bf7b0da4168f3a
parent3124d9a5aafb64431aa9facd0ae0e12201be77fa (diff)
downloadcpython-21a9a85ff4d62e8fc5a51b8eb56154f32c319b77.zip
cpython-21a9a85ff4d62e8fc5a51b8eb56154f32c319b77.tar.gz
cpython-21a9a85ff4d62e8fc5a51b8eb56154f32c319b77.tar.bz2
gh-93466: Document PyType_Spec doesn't accept repeated slot IDs; raise where this was problematic (GH-93471)
-rw-r--r--Doc/c-api/type.rst2
-rw-r--r--Lib/test/test_capi.py6
-rw-r--r--Misc/NEWS.d/next/C API/2022-06-03-14-54-41.gh-issue-93466.DDtH0X.rst3
-rw-r--r--Modules/_testcapimodule.c59
-rw-r--r--Objects/typeobject.c16
5 files changed, 84 insertions, 2 deletions
diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst
index fece3e6..aa77c28 100644
--- a/Doc/c-api/type.rst
+++ b/Doc/c-api/type.rst
@@ -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
diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
index 95930ba..cd6a4de 100644
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -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
index 0000000..0bb65ea
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2022-06-03-14-54-41.gh-issue-93466.DDtH0X.rst
@@ -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.
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index b75e03c..33dc3db 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -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",
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 51dc5e3..e576514 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -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;