cpython/Modules/_testcapi/structmember.c

218 lines
8.9 KiB
C
Raw Normal View History

gh-47146: Soft-deprecate structmember.h, expose its contents via Python.h (GH-99014) The ``structmember.h`` header is deprecated, though it continues to be available and there are no plans to remove it. There are no deprecation warnings. Old code can stay unchanged (unless the extra include and non-namespaced macros bother you greatly). Specifically, no uses in CPython are updated -- that would just be unnecessary churn. The ``structmember.h`` header is deprecated, though it continues to be available and there are no plans to remove it. Its contents are now available just by including ``Python.h``, with a ``Py`` prefix added if it was missing: - `PyMemberDef`, `PyMember_GetOne` and`PyMember_SetOne` - Type macros like `Py_T_INT`, `Py_T_DOUBLE`, etc. (previously ``T_INT``, ``T_DOUBLE``, etc.) - The flags `Py_READONLY` (previously ``READONLY``) and `Py_AUDIT_READ` (previously all uppercase) Several items are not exposed from ``Python.h``: - `T_OBJECT` (use `Py_T_OBJECT_EX`) - `T_NONE` (previously undocumented, and pretty quirky) - The macro ``WRITE_RESTRICTED`` which does nothing. - The macros ``RESTRICTED`` and ``READ_RESTRICTED``, equivalents of `Py_AUDIT_READ`. - In some configurations, ``<stddef.h>`` is not included from ``Python.h``. It should be included manually when using ``offsetof()``. The deprecated header continues to provide its original contents under the original names. Your old code can stay unchanged, unless the extra include and non-namespaced macros bother you greatly. There is discussion on the issue to rename `T_PYSSIZET` to `PY_T_SSIZE` or similar. I chose not to do that -- users will probably copy/paste that with any spelling, and not renaming it makes migration docs simpler. Co-Authored-By: Alexander Belopolsky <abalkin@users.noreply.github.com> Co-Authored-By: Matthias Braun <MatzeB@users.noreply.github.com>
2022-11-22 03:25:43 -04:00
#define PY_SSIZE_T_CLEAN
#include "parts.h"
#include <stddef.h> // for offsetof()
// This defines two classes that contain all the simple member types, one
// using "new" Py_-prefixed API, and the other using "old" <structmember.h>.
// They should behave identically in Python.
typedef struct {
char bool_member;
char byte_member;
unsigned char ubyte_member;
short short_member;
unsigned short ushort_member;
int int_member;
unsigned int uint_member;
long long_member;
unsigned long ulong_member;
Py_ssize_t pyssizet_member;
float float_member;
double double_member;
char inplace_member[6];
long long longlong_member;
unsigned long long ulonglong_member;
} all_structmembers;
typedef struct {
PyObject_HEAD
all_structmembers structmembers;
} test_structmembers;
static struct PyMemberDef test_members_newapi[] = {
{"T_BOOL", Py_T_BOOL, offsetof(test_structmembers, structmembers.bool_member), 0, NULL},
{"T_BYTE", Py_T_BYTE, offsetof(test_structmembers, structmembers.byte_member), 0, NULL},
{"T_UBYTE", Py_T_UBYTE, offsetof(test_structmembers, structmembers.ubyte_member), 0, NULL},
{"T_SHORT", Py_T_SHORT, offsetof(test_structmembers, structmembers.short_member), 0, NULL},
{"T_USHORT", Py_T_USHORT, offsetof(test_structmembers, structmembers.ushort_member), 0, NULL},
{"T_INT", Py_T_INT, offsetof(test_structmembers, structmembers.int_member), 0, NULL},
{"T_UINT", Py_T_UINT, offsetof(test_structmembers, structmembers.uint_member), 0, NULL},
{"T_LONG", Py_T_LONG, offsetof(test_structmembers, structmembers.long_member), 0, NULL},
{"T_ULONG", Py_T_ULONG, offsetof(test_structmembers, structmembers.ulong_member), 0, NULL},
{"T_PYSSIZET", Py_T_PYSSIZET, offsetof(test_structmembers, structmembers.pyssizet_member), 0, NULL},
{"T_FLOAT", Py_T_FLOAT, offsetof(test_structmembers, structmembers.float_member), 0, NULL},
{"T_DOUBLE", Py_T_DOUBLE, offsetof(test_structmembers, structmembers.double_member), 0, NULL},
{"T_STRING_INPLACE", Py_T_STRING_INPLACE, offsetof(test_structmembers, structmembers.inplace_member), 0, NULL},
{"T_LONGLONG", Py_T_LONGLONG, offsetof(test_structmembers, structmembers.longlong_member), 0, NULL},
{"T_ULONGLONG", Py_T_ULONGLONG, offsetof(test_structmembers, structmembers.ulonglong_member), 0, NULL},
{NULL}
};
static PyObject *
test_structmembers_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
static char *keywords[] = {
"T_BOOL", "T_BYTE", "T_UBYTE", "T_SHORT", "T_USHORT",
"T_INT", "T_UINT", "T_LONG", "T_ULONG", "T_PYSSIZET",
"T_FLOAT", "T_DOUBLE", "T_STRING_INPLACE",
"T_LONGLONG", "T_ULONGLONG",
NULL};
static const char fmt[] = "|bbBhHiIlknfds#LK";
test_structmembers *ob;
const char *s = NULL;
Py_ssize_t string_len = 0;
ob = PyObject_New(test_structmembers, type);
if (ob == NULL) {
return NULL;
}
memset(&ob->structmembers, 0, sizeof(all_structmembers));
if (!PyArg_ParseTupleAndKeywords(args, kwargs, fmt, keywords,
&ob->structmembers.bool_member,
&ob->structmembers.byte_member,
&ob->structmembers.ubyte_member,
&ob->structmembers.short_member,
&ob->structmembers.ushort_member,
&ob->structmembers.int_member,
&ob->structmembers.uint_member,
&ob->structmembers.long_member,
&ob->structmembers.ulong_member,
&ob->structmembers.pyssizet_member,
&ob->structmembers.float_member,
&ob->structmembers.double_member,
&s, &string_len,
&ob->structmembers.longlong_member,
&ob->structmembers.ulonglong_member))
{
Py_DECREF(ob);
return NULL;
}
if (s != NULL) {
if (string_len > 5) {
Py_DECREF(ob);
PyErr_SetString(PyExc_ValueError, "string too long");
return NULL;
}
strcpy(ob->structmembers.inplace_member, s);
}
else {
strcpy(ob->structmembers.inplace_member, "");
}
return (PyObject *)ob;
}
static PyType_Slot test_structmembers_slots[] = {
{Py_tp_new, test_structmembers_new},
{Py_tp_members, test_members_newapi},
{0},
};
static PyType_Spec test_structmembers_spec = {
.name = "_testcapi._test_structmembersType_NewAPI",
.flags = Py_TPFLAGS_DEFAULT,
.basicsize = sizeof(test_structmembers),
.slots = test_structmembers_slots,
};
#include <structmember.h>
static struct PyMemberDef test_members[] = {
{"T_BOOL", T_BOOL, offsetof(test_structmembers, structmembers.bool_member), 0, NULL},
{"T_BYTE", T_BYTE, offsetof(test_structmembers, structmembers.byte_member), 0, NULL},
{"T_UBYTE", T_UBYTE, offsetof(test_structmembers, structmembers.ubyte_member), 0, NULL},
{"T_SHORT", T_SHORT, offsetof(test_structmembers, structmembers.short_member), 0, NULL},
{"T_USHORT", T_USHORT, offsetof(test_structmembers, structmembers.ushort_member), 0, NULL},
{"T_INT", T_INT, offsetof(test_structmembers, structmembers.int_member), 0, NULL},
{"T_UINT", T_UINT, offsetof(test_structmembers, structmembers.uint_member), 0, NULL},
{"T_LONG", T_LONG, offsetof(test_structmembers, structmembers.long_member), 0, NULL},
{"T_ULONG", T_ULONG, offsetof(test_structmembers, structmembers.ulong_member), 0, NULL},
{"T_PYSSIZET", T_PYSSIZET, offsetof(test_structmembers, structmembers.pyssizet_member), 0, NULL},
{"T_FLOAT", T_FLOAT, offsetof(test_structmembers, structmembers.float_member), 0, NULL},
{"T_DOUBLE", T_DOUBLE, offsetof(test_structmembers, structmembers.double_member), 0, NULL},
{"T_STRING_INPLACE", T_STRING_INPLACE, offsetof(test_structmembers, structmembers.inplace_member), 0, NULL},
{"T_LONGLONG", T_LONGLONG, offsetof(test_structmembers, structmembers.longlong_member), 0, NULL},
{"T_ULONGLONG", T_ULONGLONG, offsetof(test_structmembers, structmembers.ulonglong_member), 0, NULL},
{NULL}
};
static void
test_structmembers_free(PyObject *ob)
{
PyObject_Free(ob);
}
/* Designated initializers would work too, but this does test the *old* API */
static PyTypeObject test_structmembersType_OldAPI= {
PyVarObject_HEAD_INIT(NULL, 0)
"test_structmembersType_OldAPI",
sizeof(test_structmembers), /* tp_basicsize */
0, /* tp_itemsize */
test_structmembers_free, /* destructor tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
PyObject_GenericSetAttr, /* tp_setattro */
0, /* tp_as_buffer */
0, /* tp_flags */
"Type containing all structmember types",
0, /* traverseproc tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
test_members, /* tp_members */
0,
0,
0,
0,
0,
0,
0,
0,
test_structmembers_new, /* tp_new */
};
int
_PyTestCapi_Init_Structmember(PyObject *m)
{
int res;
res = PyType_Ready(&test_structmembersType_OldAPI);
if (res < 0) {
return -1;
}
res = PyModule_AddObject(
m,
"_test_structmembersType_OldAPI",
(PyObject *)&test_structmembersType_OldAPI);
if (res < 0) {
return -1;
}
PyObject *test_structmembersType_NewAPI = PyType_FromModuleAndSpec(
m, &test_structmembers_spec, NULL);
if (!test_structmembersType_NewAPI) {
return -1;
}
res = PyModule_AddType(m, (PyTypeObject*)test_structmembersType_NewAPI);
Py_DECREF(test_structmembersType_NewAPI);
if (res < 0) {
return -1;
}
return 0;
}