gh-93649: Add Modules/_testcapi/type.c file (#129516)

Move PyType C API tests to a new file.

Move following tests from test_capi.test_misc to test_capi.test_type:

* BuiltinStaticTypesTests
* test_get_type_name()
* test_get_base_by_token()
This commit is contained in:
Victor Stinner 2025-01-31 15:03:54 +01:00 committed by GitHub
parent 881984b41a
commit 5424e3b034
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 434 additions and 393 deletions

View file

@ -63,5 +63,6 @@ int _PyTestCapi_Init_Object(PyObject *module);
int _PyTestCapi_Init_Config(PyObject *mod);
int _PyTestCapi_Init_Import(PyObject *mod);
int _PyTestCapi_Init_Frame(PyObject *mod);
int _PyTestCapi_Init_Type(PyObject *mod);
#endif // Py_TESTCAPI_PARTS_H

251
Modules/_testcapi/type.c Normal file
View file

@ -0,0 +1,251 @@
#include "parts.h"
#include "util.h"
static PyType_Slot HeapTypeNameType_slots[] = {
{0},
};
static PyType_Spec HeapTypeNameType_Spec = {
.name = "_testcapi.HeapTypeNameType",
.basicsize = sizeof(PyObject),
.flags = Py_TPFLAGS_DEFAULT,
.slots = HeapTypeNameType_slots,
};
static PyObject *
get_heaptype_for_name(PyObject *self, PyObject *Py_UNUSED(ignored))
{
return PyType_FromSpec(&HeapTypeNameType_Spec);
}
static PyObject *
get_type_name(PyObject *self, PyObject *type)
{
assert(PyType_Check(type));
return PyType_GetName((PyTypeObject *)type);
}
static PyObject *
get_type_qualname(PyObject *self, PyObject *type)
{
assert(PyType_Check(type));
return PyType_GetQualName((PyTypeObject *)type);
}
static PyObject *
get_type_fullyqualname(PyObject *self, PyObject *type)
{
assert(PyType_Check(type));
return PyType_GetFullyQualifiedName((PyTypeObject *)type);
}
static PyObject *
get_type_module_name(PyObject *self, PyObject *type)
{
assert(PyType_Check(type));
return PyType_GetModuleName((PyTypeObject *)type);
}
static PyObject *
test_get_type_dict(PyObject *self, PyObject *Py_UNUSED(ignored))
{
/* Test for PyType_GetDict */
// Assert ints have a `to_bytes` method
PyObject *long_dict = PyType_GetDict(&PyLong_Type);
assert(long_dict);
assert(PyDict_GetItemString(long_dict, "to_bytes")); // borrowed ref
Py_DECREF(long_dict);
// Make a new type, add an attribute to it and assert it's there
PyObject *HeapTypeNameType = PyType_FromSpec(&HeapTypeNameType_Spec);
assert(HeapTypeNameType);
assert(PyObject_SetAttrString(
HeapTypeNameType, "new_attr", Py_NewRef(Py_None)) >= 0);
PyObject *type_dict = PyType_GetDict((PyTypeObject*)HeapTypeNameType);
assert(type_dict);
assert(PyDict_GetItemString(type_dict, "new_attr")); // borrowed ref
Py_DECREF(HeapTypeNameType);
Py_DECREF(type_dict);
Py_RETURN_NONE;
}
static PyObject *
test_get_statictype_slots(PyObject *self, PyObject *Py_UNUSED(ignored))
{
newfunc tp_new = PyType_GetSlot(&PyLong_Type, Py_tp_new);
if (PyLong_Type.tp_new != tp_new) {
PyErr_SetString(PyExc_AssertionError, "mismatch: tp_new of long");
return NULL;
}
reprfunc tp_repr = PyType_GetSlot(&PyLong_Type, Py_tp_repr);
if (PyLong_Type.tp_repr != tp_repr) {
PyErr_SetString(PyExc_AssertionError, "mismatch: tp_repr of long");
return NULL;
}
ternaryfunc tp_call = PyType_GetSlot(&PyLong_Type, Py_tp_call);
if (tp_call != NULL) {
PyErr_SetString(PyExc_AssertionError, "mismatch: tp_call of long");
return NULL;
}
binaryfunc nb_add = PyType_GetSlot(&PyLong_Type, Py_nb_add);
if (PyLong_Type.tp_as_number->nb_add != nb_add) {
PyErr_SetString(PyExc_AssertionError, "mismatch: nb_add of long");
return NULL;
}
lenfunc mp_length = PyType_GetSlot(&PyLong_Type, Py_mp_length);
if (mp_length != NULL) {
PyErr_SetString(PyExc_AssertionError, "mismatch: mp_length of long");
return NULL;
}
void *over_value = PyType_GetSlot(&PyLong_Type, Py_bf_releasebuffer + 1);
if (over_value != NULL) {
PyErr_SetString(PyExc_AssertionError, "mismatch: max+1 of long");
return NULL;
}
tp_new = PyType_GetSlot(&PyLong_Type, 0);
if (tp_new != NULL) {
PyErr_SetString(PyExc_AssertionError, "mismatch: slot 0 of long");
return NULL;
}
if (PyErr_ExceptionMatches(PyExc_SystemError)) {
// This is the right exception
PyErr_Clear();
}
else {
return NULL;
}
Py_RETURN_NONE;
}
// Get type->tp_version_tag
static PyObject *
type_get_version(PyObject *self, PyObject *type)
{
if (!PyType_Check(type)) {
PyErr_SetString(PyExc_TypeError, "argument must be a type");
return NULL;
}
PyObject *res = PyLong_FromUnsignedLong(
((PyTypeObject *)type)->tp_version_tag);
if (res == NULL) {
assert(PyErr_Occurred());
return NULL;
}
return res;
}
static PyObject *
type_modified(PyObject *self, PyObject *arg)
{
if (!PyType_Check(arg)) {
PyErr_SetString(PyExc_TypeError, "argument must be a type");
return NULL;
}
PyTypeObject *type = (PyTypeObject*)arg;
PyType_Modified(type);
Py_RETURN_NONE;
}
static PyObject *
type_assign_version(PyObject *self, PyObject *arg)
{
if (!PyType_Check(arg)) {
PyErr_SetString(PyExc_TypeError, "argument must be a type");
return NULL;
}
PyTypeObject *type = (PyTypeObject*)arg;
int res = PyUnstable_Type_AssignVersionTag(type);
return PyLong_FromLong(res);
}
static PyObject *
type_get_tp_bases(PyObject *self, PyObject *arg)
{
if (!PyType_Check(arg)) {
PyErr_SetString(PyExc_TypeError, "argument must be a type");
return NULL;
}
PyTypeObject *type = (PyTypeObject*)arg;
PyObject *bases = type->tp_bases;
if (bases == NULL) {
Py_RETURN_NONE;
}
return Py_NewRef(bases);
}
static PyObject *
type_get_tp_mro(PyObject *self, PyObject *arg)
{
if (!PyType_Check(arg)) {
PyErr_SetString(PyExc_TypeError, "argument must be a type");
return NULL;
}
PyTypeObject *type = (PyTypeObject*)arg;
PyObject *mro = ((PyTypeObject *)type)->tp_mro;
if (mro == NULL) {
Py_RETURN_NONE;
}
return Py_NewRef(mro);
}
static PyObject *
type_freeze(PyObject *module, PyObject *arg)
{
if (!PyType_Check(arg)) {
PyErr_SetString(PyExc_TypeError, "argument must be a type");
return NULL;
}
PyTypeObject *type = (PyTypeObject*)arg;
if (PyType_Freeze(type) < 0) {
return NULL;
}
Py_RETURN_NONE;
}
static PyMethodDef test_methods[] = {
{"get_heaptype_for_name", get_heaptype_for_name, METH_NOARGS},
{"get_type_name", get_type_name, METH_O},
{"get_type_qualname", get_type_qualname, METH_O},
{"get_type_fullyqualname", get_type_fullyqualname, METH_O},
{"get_type_module_name", get_type_module_name, METH_O},
{"test_get_type_dict", test_get_type_dict, METH_NOARGS},
{"test_get_statictype_slots", test_get_statictype_slots, METH_NOARGS},
{"type_get_version", type_get_version, METH_O, PyDoc_STR("type->tp_version_tag")},
{"type_modified", type_modified, METH_O, PyDoc_STR("PyType_Modified")},
{"type_assign_version", type_assign_version, METH_O, PyDoc_STR("PyUnstable_Type_AssignVersionTag")},
{"type_get_tp_bases", type_get_tp_bases, METH_O},
{"type_get_tp_mro", type_get_tp_mro, METH_O},
{"type_freeze", type_freeze, METH_O},
{NULL},
};
int
_PyTestCapi_Init_Type(PyObject *m)
{
return PyModule_AddFunctions(m, test_methods);
}