mirror of https://github.com/python/cpython
gh-93466: Document PyType_Spec doesn't accept repeated slot IDs; raise where this was problematic (GH-93471)
This commit is contained in:
parent
3124d9a5aa
commit
21a9a85ff4
|
@ -296,6 +296,8 @@ The following functions and structs are used to create
|
||||||
Array of :c:type:`PyType_Slot` structures.
|
Array of :c:type:`PyType_Slot` structures.
|
||||||
Terminated by the special slot value ``{0, NULL}``.
|
Terminated by the special slot value ``{0, NULL}``.
|
||||||
|
|
||||||
|
Each slot ID should be specified at most once.
|
||||||
|
|
||||||
.. c:type:: PyType_Slot
|
.. c:type:: PyType_Slot
|
||||||
|
|
||||||
Structure defining optional functionality of a type, containing a slot ID
|
Structure defining optional functionality of a type, containing a slot ID
|
||||||
|
|
|
@ -618,6 +618,12 @@ class CAPITest(unittest.TestCase):
|
||||||
with self.assertRaisesRegex(TypeError, msg):
|
with self.assertRaisesRegex(TypeError, msg):
|
||||||
t = _testcapi.pytype_fromspec_meta(_testcapi.HeapCTypeMetaclassCustomNew)
|
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):
|
def test_pynumber_tobase(self):
|
||||||
from _testcapi import pynumber_tobase
|
from _testcapi import pynumber_tobase
|
||||||
self.assertEqual(pynumber_tobase(123, 2), '0b1111011')
|
self.assertEqual(pynumber_tobase(123, 2), '0b1111011')
|
||||||
|
|
|
@ -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.
|
|
@ -1482,6 +1482,63 @@ test_type_from_ephemeral_spec(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||||
return result;
|
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 *
|
static PyObject *
|
||||||
test_get_type_qualname(PyObject *self, PyObject *Py_UNUSED(ignored))
|
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_name", test_get_type_name, METH_NOARGS},
|
||||||
{"test_get_type_qualname", test_get_type_qualname, METH_NOARGS},
|
{"test_get_type_qualname", test_get_type_qualname, METH_NOARGS},
|
||||||
{"test_type_from_ephemeral_spec", test_type_from_ephemeral_spec, 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,
|
{"test_from_spec_metatype_inheritance", test_from_spec_metatype_inheritance,
|
||||||
METH_NOARGS},
|
METH_NOARGS},
|
||||||
{"test_from_spec_invalid_metatype_inheritance",
|
{"test_from_spec_invalid_metatype_inheritance",
|
||||||
|
|
|
@ -3409,14 +3409,20 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module,
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
const PyType_Slot *slot;
|
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;
|
char *res_start;
|
||||||
short slot_offset, subslot_offset;
|
short slot_offset, subslot_offset;
|
||||||
|
|
||||||
nmembers = weaklistoffset = dictoffset = vectorcalloffset = 0;
|
nmembers = weaklistoffset = dictoffset = vectorcalloffset = 0;
|
||||||
for (slot = spec->slots; slot->slot; slot++) {
|
for (slot = spec->slots; slot->slot; slot++) {
|
||||||
if (slot->slot == Py_tp_members) {
|
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++) {
|
for (const PyMemberDef *memb = slot->pfunc; memb->name != NULL; memb++) {
|
||||||
nmembers++;
|
nmembers++;
|
||||||
if (strcmp(memb->name, "__weaklistoffset__") == 0) {
|
if (strcmp(memb->name, "__weaklistoffset__") == 0) {
|
||||||
|
@ -3559,6 +3565,12 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module,
|
||||||
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 */
|
||||||
|
if (type->tp_doc != NULL) {
|
||||||
|
PyErr_SetString(
|
||||||
|
PyExc_SystemError,
|
||||||
|
"Multiple Py_tp_doc slots are not supported.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
if (slot->pfunc == NULL) {
|
if (slot->pfunc == NULL) {
|
||||||
type->tp_doc = NULL;
|
type->tp_doc = NULL;
|
||||||
continue;
|
continue;
|
||||||
|
|
Loading…
Reference in New Issue