Skip to content

Commit

Permalink
pythongh-117031: Add support for new member types for PyMemberDef.type
Browse files Browse the repository at this point in the history
Add support for standard C and Posix integer types like Py_T_UINT32,
Py_T_PTRDIFF, Py_T_OFF and Py_T_PID.
Add Py_T_SSIZE as alias of Py_T_PYSSIZET.
  • Loading branch information
serhiy-storchaka committed Mar 19, 2024
1 parent f55e188 commit 656ad30
Show file tree
Hide file tree
Showing 9 changed files with 360 additions and 38 deletions.
32 changes: 32 additions & 0 deletions Doc/c-api/structures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,26 @@ Macro name C type Python type
.. c:macro:: Py_T_ULONG :c:expr:`unsigned long` :py:class:`int`
.. c:macro:: Py_T_ULONGLONG :c:expr:`unsigned long long` :py:class:`int`
.. c:macro:: Py_T_PYSSIZET :c:expr:`Py_ssize_t` :py:class:`int`
.. c:macro:: Py_T_SSIZE :c:expr:`Py_ssize_t` :py:class:`int`
.. c:macro:: Py_T_SIZE :c:expr:`size_t` :py:class:`int`
.. c:macro:: Py_T_INT8 :c:expr:`int8_t` :py:class:`int`
.. c:macro:: Py_T_UINT8 :c:expr:`uint8_t` :py:class:`int`
.. c:macro:: Py_T_INT16 :c:expr:`int16_t` :py:class:`int`
.. c:macro:: Py_T_UINT16 :c:expr:`uint16_t` :py:class:`int`
.. c:macro:: Py_T_INT32 :c:expr:`int32_t` :py:class:`int`
.. c:macro:: Py_T_UINT32 :c:expr:`uint32_t` :py:class:`int`
.. c:macro:: Py_T_INT64 :c:expr:`int64_t` :py:class:`int`
.. c:macro:: Py_T_UINT64 :c:expr:`uint64_t` :py:class:`int`
.. c:macro:: Py_T_INTMAX :c:expr:`intmax_t` :py:class:`int`
.. c:macro:: Py_T_UINTMAX :c:expr:`uintmax_t` :py:class:`int`
.. c:macro:: Py_T_INTPTR :c:expr:`intptr_t` :py:class:`int`
.. c:macro:: Py_T_UINTPTR :c:expr:`uintptr_t` :py:class:`int`
.. c:macro:: Py_T_PTRDIFF :c:expr:`ptrdiff_t` :py:class:`int`
.. c:macro:: Py_T_OFF :c:expr:`off_t` or :py:class:`int`
:c:expr:`long long`
(on Windows)
.. c:macro:: Py_T_PID :c:expr:`pid_t` :py:class:`int`
.. c:macro:: Py_T_FLOAT :c:expr:`float` :py:class:`float`
.. c:macro:: Py_T_DOUBLE :c:expr:`double` :py:class:`float`
.. c:macro:: Py_T_BOOL :c:expr:`char` :py:class:`bool`
Expand Down Expand Up @@ -675,6 +695,18 @@ Macro name C type Python type
Always ``None``. Must be used with :c:macro:`Py_READONLY`.
.. versionadded:: 3.13
Added :c:macro:`Py_T_SSIZE` (as alias of :c:macro:`Py_T_PYSSIZET`),
:c:macro:`Py_T_SIZE`, :c:macro:`Py_T_INT8`, :c:macro:`Py_T_UINT8`,
:c:macro:`Py_T_INT16`, :c:macro:`Py_T_UINT16`,
:c:macro:`Py_T_INT32`, :c:macro:`Py_T_UINT32`,
:c:macro:`Py_T_INT64`, :c:macro:`Py_T_UINT64`,
:c:macro:`Py_T_INTMAX`, :c:macro:`Py_T_UINTMAX`,
:c:macro:`Py_T_INTPTR`, :c:macro:`Py_T_UINTPTR`,
:c:macro:`Py_T_PTRDIFF`, :c:macro:`Py_T_OFF`, and :c:macro:`Py_T_PID`.
Defining Getters and Setters
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
11 changes: 11 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1701,6 +1701,17 @@ New Features
more information.
(Contributed by Victor Stinner in :gh:`111696`.)

* Add support of new member types for :c:member:`PyMemberDef.type`:
:c:macro:`Py_T_SSIZE` (as alias of :c:macro:`Py_T_PYSSIZET`),
:c:macro:`Py_T_SIZE`, :c:macro:`Py_T_INT8`, :c:macro:`Py_T_UINT8`,
:c:macro:`Py_T_INT16`, :c:macro:`Py_T_UINT16`,
:c:macro:`Py_T_INT32`, :c:macro:`Py_T_UINT32`,
:c:macro:`Py_T_INT64`, :c:macro:`Py_T_UINT64`,
:c:macro:`Py_T_INTMAX`, :c:macro:`Py_T_UINTMAX`,
:c:macro:`Py_T_INTPTR`, :c:macro:`Py_T_UINTPTR`,
:c:macro:`Py_T_PTRDIFF`, :c:macro:`Py_T_OFF`, and :c:macro:`Py_T_PID`.
(Contributed by Serhiy Storchaka in :gh:`117031`.)


Porting to Python 3.13
----------------------
Expand Down
23 changes: 23 additions & 0 deletions Include/descrobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,29 @@ struct PyMemberDef {
#define Py_T_PYSSIZET 19 /* Py_ssize_t */
#define _Py_T_NONE 20 // Deprecated. Value is always None.

#define Py_T_SSIZE Py_T_PYSSIZET
#define Py_T_SIZE 21

#define Py_T_INT8 22
#define Py_T_UINT8 23
#define Py_T_INT16 24
#define Py_T_UINT16 25
#define Py_T_INT32 26
#define Py_T_UINT32 27
#define Py_T_INT64 28
#define Py_T_UINT64 29
#define Py_T_INTMAX 30
#define Py_T_UINTMAX 31
#define Py_T_INTPTR 32
#define Py_T_UINTPTR 33
#define Py_T_PTRDIFF 34
#ifdef MS_WINDOWS
# define Py_T_OFF Py_T_LONGLONG
#else
# define Py_T_OFF 35
#endif
#define Py_T_PID 36

/* Flags */
#define Py_READONLY 1
#define Py_AUDIT_READ 2 // Added in 3.10, harmless no-op before that
Expand Down
89 changes: 88 additions & 1 deletion Lib/test/test_capi/test_structmembers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
INT_MAX, INT_MIN, UINT_MAX,
LONG_MAX, LONG_MIN, ULONG_MAX,
LLONG_MAX, LLONG_MIN, ULLONG_MAX,
PY_SSIZE_T_MAX, PY_SSIZE_T_MIN,
PY_SSIZE_T_MAX, PY_SSIZE_T_MIN, SIZE_MAX,
SIZEOF_INTMAX_T, SIZEOF_INTPTR_T, SIZEOF_PTRDIFF_T, SIZEOF_OFF_T,
SIZEOF_PID_T, SIZEOF_INT,
)


Expand All @@ -20,6 +22,8 @@ def __init__(self, value):
self.value = value
def __index__(self):
return self.value
def __repr__(self):
return f'Index({self.value!r})'

# There are two classes: one using <structmember.h> and another using
# `Py_`-prefixed API. They should behave the same in Python
Expand Down Expand Up @@ -169,6 +173,89 @@ class ReadWriteTests_OldAPI(ReadWriteTests, unittest.TestCase):
class ReadWriteTests_NewAPI(ReadWriteTests, unittest.TestCase):
cls = _test_structmembersType_NewAPI

def test_size(self):
self._test_int_range('T_SSIZE', PY_SSIZE_T_MIN, PY_SSIZE_T_MAX, indexlimit=False)
self._test_int_range('T_SIZE', 0, SIZE_MAX, indexlimit=False)

def test_int8(self):
self._test_int_range('T_INT8', -2**7, 2**7-1)
self._test_int_range('T_UINT8', 0, 2**8-1, indexlimit=False)

def test_int16(self):
self._test_int_range('T_INT16', -2**15, 2**15-1)
self._test_int_range('T_UINT16', 0, 2**16-1, indexlimit=False)

def test_int32(self):
self._test_int_range('T_INT32', -2**31, 2**31-1)
self._test_int_range('T_UINT32', 0, 2**32-1, indexlimit=False)

def test_int64(self):
self._test_int_range('T_INT64', -2**63, 2**63-1)
self._test_int_range('T_UINT64', 0, 2**64-1, indexlimit=False)

def test_intmax(self):
bits = 8*SIZEOF_INTMAX_T
self._test_int_range('T_INTMAX', -2**(bits-1), 2**(bits-1)-1)
self._test_int_range('T_UINTMAX', 0, 2**bits-1, indexlimit=False)

def test_intptr(self):
bits = 8*SIZEOF_INTPTR_T
self._test_int_range('T_INTPTR', -2**(bits-1), 2**(bits-1)-1)
self._test_int_range('T_UINTPTR', 0, 2**bits-1, indexlimit=False)

def test_ptrdiff(self):
bits = 8*SIZEOF_PTRDIFF_T
self._test_int_range('T_PTRDIFF', -2**(bits-1), 2**(bits-1)-1)

def test_off(self):
bits = 8*SIZEOF_OFF_T
self._test_int_range('T_OFF', -2**(bits-1), 2**(bits-1)-1)

def test_pid(self):
bits = 8*SIZEOF_PID_T
self._test_int_range('T_PID', -2**(bits-1), 2**(bits-1)-1)


class TestWarnings:
def setUp(self):
self.ts = _make_test_object(self.cls)

def test_byte_max(self):
ts = self.ts
with warnings_helper.check_warnings(('', RuntimeWarning)):
ts.T_BYTE = CHAR_MAX+1

def test_byte_min(self):
ts = self.ts
with warnings_helper.check_warnings(('', RuntimeWarning)):
ts.T_BYTE = CHAR_MIN-1

def test_ubyte_max(self):
ts = self.ts
with warnings_helper.check_warnings(('', RuntimeWarning)):
ts.T_UBYTE = UCHAR_MAX+1

def test_short_max(self):
ts = self.ts
with warnings_helper.check_warnings(('', RuntimeWarning)):
ts.T_SHORT = SHRT_MAX+1

def test_short_min(self):
ts = self.ts
with warnings_helper.check_warnings(('', RuntimeWarning)):
ts.T_SHORT = SHRT_MIN-1

def test_ushort_max(self):
ts = self.ts
with warnings_helper.check_warnings(('', RuntimeWarning)):
ts.T_USHORT = USHRT_MAX+1

class TestWarnings_OldAPI(TestWarnings, unittest.TestCase):
cls = _test_structmembersType_OldAPI

class TestWarnings_NewAPI(TestWarnings, unittest.TestCase):
cls = _test_structmembersType_NewAPI


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Add support for new member types for :c:member:`PyMemberDef.type`:
:c:macro:`Py_T_SSIZE` (as alias of :c:macro:`Py_T_PYSSIZET`),
:c:macro:`Py_T_SIZE`, :c:macro:`Py_T_INT8`, :c:macro:`Py_T_UINT8`,
:c:macro:`Py_T_INT16`, :c:macro:`Py_T_UINT16`, :c:macro:`Py_T_INT32`,
:c:macro:`Py_T_UINT32`, :c:macro:`Py_T_INT64`, :c:macro:`Py_T_UINT64`,
:c:macro:`Py_T_INTMAX`, :c:macro:`Py_T_UINTMAX`, :c:macro:`Py_T_INTPTR`,
:c:macro:`Py_T_UINTPTR`, :c:macro:`Py_T_PTRDIFF`, :c:macro:`Py_T_OFF`, and
:c:macro:`Py_T_PID`.
37 changes: 37 additions & 0 deletions Modules/_testcapi/structmember.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,31 @@ typedef struct {
long long_member;
unsigned long ulong_member;
Py_ssize_t pyssizet_member;
size_t size_member;
float float_member;
double double_member;
char inplace_member[6];
long long longlong_member;
unsigned long long ulonglong_member;
int8_t int8_member;
uint8_t uint8_member;
int16_t int16_member;
uint16_t uint16_member;
int32_t int32_member;
uint32_t uint32_member;
int64_t int64_member;
uint64_t uint64_member;
intmax_t intmax_member;
uintmax_t uintmax_member;
intptr_t intptr_member;
uintptr_t uintptr_member;
ptrdiff_t ptrdiff_member;
#ifdef MS_WINDOWS
long long off_member;
#else
off_t off_member;
#endif
pid_t pid_member;
} all_structmembers;

typedef struct {
Expand All @@ -41,11 +61,28 @@ static struct PyMemberDef test_members_newapi[] = {
{"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_SSIZE", Py_T_SSIZE, offsetof(test_structmembers, structmembers.pyssizet_member), 0, NULL},
{"T_SIZE", Py_T_SIZE, offsetof(test_structmembers, structmembers.size_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},
{"T_INT8", Py_T_INT8, offsetof(test_structmembers, structmembers.int8_member), 0, NULL},
{"T_UINT8", Py_T_UINT8, offsetof(test_structmembers, structmembers.uint8_member), 0, NULL},
{"T_INT16", Py_T_INT16, offsetof(test_structmembers, structmembers.int16_member), 0, NULL},
{"T_UINT16", Py_T_UINT16, offsetof(test_structmembers, structmembers.uint16_member), 0, NULL},
{"T_INT32", Py_T_INT32, offsetof(test_structmembers, structmembers.int32_member), 0, NULL},
{"T_UINT32", Py_T_UINT32, offsetof(test_structmembers, structmembers.uint32_member), 0, NULL},
{"T_INT64", Py_T_INT64, offsetof(test_structmembers, structmembers.int64_member), 0, NULL},
{"T_UINT64", Py_T_UINT64, offsetof(test_structmembers, structmembers.uint64_member), 0, NULL},
{"T_INTMAX", Py_T_INTMAX, offsetof(test_structmembers, structmembers.intmax_member), 0, NULL},
{"T_UINTMAX", Py_T_UINTMAX, offsetof(test_structmembers, structmembers.uintmax_member), 0, NULL},
{"T_INTPTR", Py_T_INTPTR, offsetof(test_structmembers, structmembers.intptr_member), 0, NULL},
{"T_UINTPTR", Py_T_UINTPTR, offsetof(test_structmembers, structmembers.uintptr_member), 0, NULL},
{"T_PTRDIFF", Py_T_PTRDIFF, offsetof(test_structmembers, structmembers.ptrdiff_member), 0, NULL},
{"T_OFF", Py_T_OFF, offsetof(test_structmembers, structmembers.off_member), 0, NULL},
{"T_PID", Py_T_PID, offsetof(test_structmembers, structmembers.pid_member), 0, NULL},
{NULL}
};

Expand Down
10 changes: 10 additions & 0 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3946,6 +3946,16 @@ PyInit__testcapi(void)
PyModule_AddObject(m, "SIZEOF_WCHAR_T", PyLong_FromSsize_t(sizeof(wchar_t)));
PyModule_AddObject(m, "SIZEOF_VOID_P", PyLong_FromSsize_t(sizeof(void*)));
PyModule_AddObject(m, "SIZEOF_TIME_T", PyLong_FromSsize_t(sizeof(time_t)));
PyModule_AddObject(m, "SIZEOF_INTMAX_T", PyLong_FromSsize_t(sizeof(intmax_t)));
PyModule_AddObject(m, "SIZEOF_INTPTR_T", PyLong_FromSsize_t(sizeof(intptr_t)));
PyModule_AddObject(m, "SIZEOF_PTRDIFF_T", PyLong_FromSsize_t(sizeof(ptrdiff_t)));
#ifdef MS_WINDOWS
PyModule_AddObject(m, "SIZEOF_OFF_T", PyLong_FromSsize_t(sizeof(long long)));
#else
PyModule_AddObject(m, "SIZEOF_OFF_T", PyLong_FromSsize_t(sizeof(off_t)));
#endif
PyModule_AddObject(m, "SIZEOF_INT", PyLong_FromSsize_t(SIZEOF_PID_T));
PyModule_AddObject(m, "SIZEOF_PID_T", PyLong_FromSsize_t(sizeof(pid_t)));
PyModule_AddObject(m, "Py_Version", PyLong_FromUnsignedLong(Py_Version));
Py_INCREF(&PyInstanceMethod_Type);
PyModule_AddObject(m, "instancemethod", (PyObject *)&PyInstanceMethod_Type);
Expand Down
47 changes: 10 additions & 37 deletions Modules/selectmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1774,48 +1774,21 @@ typedef struct kqueue_queue_Object {
SOCKET kqfd; /* kqueue control fd */
} kqueue_queue_Object;

#if (SIZEOF_UINTPTR_T != SIZEOF_VOID_P)
# error uintptr_t does not match void *!
#elif (SIZEOF_UINTPTR_T == SIZEOF_LONG_LONG)
# define T_UINTPTRT Py_T_ULONGLONG
# define T_INTPTRT Py_T_LONGLONG
# define UINTPTRT_FMT_UNIT "K"
# define INTPTRT_FMT_UNIT "L"
#elif (SIZEOF_UINTPTR_T == SIZEOF_LONG)
# define T_UINTPTRT Py_T_ULONG
# define T_INTPTRT Py_T_LONG
# define UINTPTRT_FMT_UNIT "k"
# define INTPTRT_FMT_UNIT "l"
#elif (SIZEOF_UINTPTR_T == SIZEOF_INT)
# define T_UINTPTRT Py_T_UINT
# define T_INTPTRT Py_T_INT
# define UINTPTRT_FMT_UNIT "I"
# define INTPTRT_FMT_UNIT "i"
#else
# error uintptr_t does not match int, long, or long long!
#endif

#if SIZEOF_LONG_LONG == 8
# define T_INT64 Py_T_LONGLONG
# define INT64_FMT_UNIT "L"
#elif SIZEOF_LONG == 8
# define T_INT64 Py_T_LONG
# define INT64_FMT_UNIT "l"
#elif SIZEOF_INT == 8
# define T_INT64 Py_T_INT
# define INT64_FMT_UNIT "i"
#else
# define INT64_FMT_UNIT "_"
#endif

#if SIZEOF_LONG_LONG == 4
# define T_UINT32 Py_T_ULONGLONG
# define UINT32_FMT_UNIT "K"
#elif SIZEOF_LONG == 4
# define T_UINT32 Py_T_ULONG
# define UINT32_FMT_UNIT "k"
#elif SIZEOF_INT == 4
# define T_UINT32 Py_T_UINT
# define UINT32_FMT_UNIT "I"
#else
# define UINT32_FMT_UNIT "_"
Expand All @@ -1825,11 +1798,11 @@ typedef struct kqueue_queue_Object {
* kevent is not standard and its members vary across BSDs.
*/
#ifdef __NetBSD__
# define FILTER_TYPE T_UINT32
# define FILTER_TYPE Py_T_UINT32
# define FILTER_FMT_UNIT UINT32_FMT_UNIT
# define FLAGS_TYPE T_UINT32
# define FLAGS_TYPE Py_T_UINT32
# define FLAGS_FMT_UNIT UINT32_FMT_UNIT
# define FFLAGS_TYPE T_UINT32
# define FFLAGS_TYPE Py_T_UINT32
# define FFLAGS_FMT_UNIT UINT32_FMT_UNIT
#else
# define FILTER_TYPE Py_T_SHORT
Expand All @@ -1841,11 +1814,11 @@ typedef struct kqueue_queue_Object {
#endif

#if defined(__NetBSD__) || defined(__OpenBSD__)
# define DATA_TYPE T_INT64
# define DATA_TYPE Py_T_INT64
# define DATA_FMT_UNIT INT64_FMT_UNIT
#else
# define DATA_TYPE T_INTPTRT
# define DATA_FMT_UNIT INTPTRT_FMT_UNIT
# define DATA_TYPE Py_T_INTPTRT
# define DATA_FMT_UNIT _Py_PARSE_INTPTRT
#endif

/* Unfortunately, we can't store python objects in udata, because
Expand All @@ -1855,12 +1828,12 @@ typedef struct kqueue_queue_Object {

#define KQ_OFF(x) offsetof(kqueue_event_Object, x)
static struct PyMemberDef kqueue_event_members[] = {
{"ident", T_UINTPTRT, KQ_OFF(e.ident)},
{"ident", Py_T_UINTPTRT, KQ_OFF(e.ident)},
{"filter", FILTER_TYPE, KQ_OFF(e.filter)},
{"flags", FLAGS_TYPE, KQ_OFF(e.flags)},
{"fflags", Py_T_UINT, KQ_OFF(e.fflags)},
{"fflags", Py_T_UINT, KQ_OFF(e.fflags)},
{"data", DATA_TYPE, KQ_OFF(e.data)},
{"udata", T_UINTPTRT, KQ_OFF(e.udata)},
{"udata", Py_T_UINTPTRT, KQ_OFF(e.udata)},
{NULL} /* Sentinel */
};
#undef KQ_OFF
Expand All @@ -1884,7 +1857,7 @@ kqueue_event_init(kqueue_event_Object *self, PyObject *args, PyObject *kwds)
"data", "udata", NULL};
static const char fmt[] = "O|"
FILTER_FMT_UNIT FLAGS_FMT_UNIT FFLAGS_FMT_UNIT DATA_FMT_UNIT
UINTPTRT_FMT_UNIT ":kevent";
_Py_PARSE_UINTPTRT ":kevent";

EV_SET(&(self->e), 0, EVFILT_READ, EV_ADD, 0, 0, 0); /* defaults */

Expand Down

0 comments on commit 656ad30

Please sign in to comment.