From 656ad3083d3537df757de7500ec8512f9b3fb858 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 20 Jan 2024 12:48:05 +0200 Subject: [PATCH 01/11] gh-117031: Add support for new member types for PyMemberDef.type 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. --- Doc/c-api/structures.rst | 32 ++++ Doc/whatsnew/3.13.rst | 11 ++ Include/descrobject.h | 23 +++ Lib/test/test_capi/test_structmembers.py | 89 ++++++++++- ...-03-19-21-13-20.gh-issue-117031.0s3Ruq.rst | 8 + Modules/_testcapi/structmember.c | 37 +++++ Modules/_testcapimodule.c | 10 ++ Modules/selectmodule.c | 47 ++---- Python/structmember.c | 141 ++++++++++++++++++ 9 files changed, 360 insertions(+), 38 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2024-03-19-21-13-20.gh-issue-117031.0s3Ruq.rst diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index f9461ab01f6049..956e3ce4ec44d1 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -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` @@ -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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 0e04dcd196d306..aaa7e594df0263 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -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 ---------------------- diff --git a/Include/descrobject.h b/Include/descrobject.h index fd66d17b497a31..a552ca6c8290a5 100644 --- a/Include/descrobject.h +++ b/Include/descrobject.h @@ -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 diff --git a/Lib/test/test_capi/test_structmembers.py b/Lib/test/test_capi/test_structmembers.py index 08ca1f828529cf..9bc6cf07d7eed8 100644 --- a/Lib/test/test_capi/test_structmembers.py +++ b/Lib/test/test_capi/test_structmembers.py @@ -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, ) @@ -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 and another using # `Py_`-prefixed API. They should behave the same in Python @@ -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() diff --git a/Misc/NEWS.d/next/C API/2024-03-19-21-13-20.gh-issue-117031.0s3Ruq.rst b/Misc/NEWS.d/next/C API/2024-03-19-21-13-20.gh-issue-117031.0s3Ruq.rst new file mode 100644 index 00000000000000..21a92de8a59e33 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2024-03-19-21-13-20.gh-issue-117031.0s3Ruq.rst @@ -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`. diff --git a/Modules/_testcapi/structmember.c b/Modules/_testcapi/structmember.c index 096eaecd40855f..30950c32547baa 100644 --- a/Modules/_testcapi/structmember.c +++ b/Modules/_testcapi/structmember.c @@ -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 { @@ -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} }; diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 7928cd7d6fe1ae..8766939fc6c5e4 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -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); diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index f16173aafa7d3c..a5567dcc227506 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -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 "_" @@ -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 @@ -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 @@ -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 @@ -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 */ diff --git a/Python/structmember.c b/Python/structmember.c index ba881d18a0973d..3a0c9ac199f420 100644 --- a/Python/structmember.c +++ b/Python/structmember.c @@ -4,6 +4,7 @@ #include "Python.h" #include "pycore_abstract.h" // _PyNumber_Index() #include "pycore_long.h" // _PyLong_IsNegative() +#include PyObject * @@ -49,6 +50,9 @@ PyMember_GetOne(const char *obj_addr, PyMemberDef *l) case Py_T_PYSSIZET: v = PyLong_FromSsize_t(*(Py_ssize_t*)addr); break; + case Py_T_SIZE: + v = PyLong_FromSize_t(*(size_t*)addr); + break; case Py_T_FLOAT: v = PyFloat_FromDouble((double)*(float*)addr); break; @@ -94,6 +98,59 @@ PyMember_GetOne(const char *obj_addr, PyMemberDef *l) case _Py_T_NONE: v = Py_NewRef(Py_None); break; + case Py_T_INT8: + v = PyLong_FromLong(*(int8_t*)addr); + break; + case Py_T_UINT8: + v = PyLong_FromUnsignedLong(*(uint8_t*)addr); + break; + case Py_T_INT16: + v = PyLong_FromLong(*(int16_t*)addr); + break; + case Py_T_UINT16: + v = PyLong_FromUnsignedLong(*(uint16_t*)addr); + break; + case Py_T_INT32: + v = PyLong_FromLong(*(int32_t*)addr); + break; + case Py_T_UINT32: + v = PyLong_FromUnsignedLong(*(uint32_t*)addr); + break; + case Py_T_INT64: + v = PyLong_FromLongLong(*(int64_t*)addr); + break; + case Py_T_UINT64: + v = PyLong_FromUnsignedLongLong(*(uint64_t*)addr); + break; + case Py_T_INTMAX: + v = _PyLong_FromByteArray((const unsigned char *)addr, + sizeof(intmax_t), PY_LITTLE_ENDIAN, 1); + break; + case Py_T_UINTMAX: + v = _PyLong_FromByteArray((const unsigned char *)addr, + sizeof(uintmax_t), PY_LITTLE_ENDIAN, 0); + break; + case Py_T_INTPTR: + v = _PyLong_FromByteArray((const unsigned char *)addr, + sizeof(intptr_t), PY_LITTLE_ENDIAN, 1); + break; + case Py_T_UINTPTR: + v = _PyLong_FromByteArray((const unsigned char *)addr, + sizeof(uintptr_t), PY_LITTLE_ENDIAN, 0); + break; + case Py_T_PTRDIFF: + v = _PyLong_FromByteArray((const unsigned char *)addr, + sizeof(ptrdiff_t), PY_LITTLE_ENDIAN, 1); + break; +#ifndef MS_WINDOWS + case Py_T_OFF: + v = _PyLong_FromByteArray((const unsigned char *)addr, + sizeof(off_t), PY_LITTLE_ENDIAN, 1); + break; +#endif + case Py_T_PID: + v = PyLong_FromPid(*(pid_t*)addr); + break; default: PyErr_SetString(PyExc_SystemError, "bad memberdescr type"); v = NULL; @@ -107,6 +164,26 @@ PyMember_GetOne(const char *obj_addr, PyMemberDef *l) return -1; \ } while (0) +#define SET_UNSIGNED_INT(N) do { \ + if (!PyLong_Check(v)) { \ + PyErr_SetString(PyExc_TypeError, "an integer is required"); \ + return -1; \ + } \ + return _PyLong_AsByteArray((PyLongObject *)v, (unsigned char *)addr,\ + (N), PY_LITTLE_ENDIAN, 0, 1); \ + } while (0) + +#define SET_SIGNED_INT(N) do { \ + v = _PyNumber_Index(v); \ + if (v == NULL) { \ + return -1; \ + } \ + int rc = _PyLong_AsByteArray((PyLongObject *)v, (unsigned char *)addr,\ + (N), PY_LITTLE_ENDIAN, 1, 1); \ + Py_DECREF(v); \ + return rc; \ + } while (0) + int PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v) { @@ -267,6 +344,14 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v) return -1; break; } + case Py_T_SIZE: { + size_t value = PyLong_AsSize_t(v); + if (value == (size_t)-1 && PyErr_Occurred()) { + return -1; + } + *(size_t*)addr = value; + break; + } case Py_T_FLOAT:{ double double_val = PyFloat_AsDouble(v); if ((double_val == -1) && PyErr_Occurred()) @@ -332,6 +417,62 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v) } break; } + case Py_T_INT8: + SET_SIGNED_INT(1); + break; + case Py_T_UINT8: + SET_UNSIGNED_INT(1); + break; + case Py_T_INT16: + SET_SIGNED_INT(2); + break; + case Py_T_UINT16: + SET_UNSIGNED_INT(2); + break; + case Py_T_INT32: + SET_SIGNED_INT(4); + break; + case Py_T_UINT32: + SET_UNSIGNED_INT(4); + break; + case Py_T_INT64: + SET_SIGNED_INT(8); + break; + case Py_T_UINT64: + SET_UNSIGNED_INT(8); + break; + case Py_T_INTMAX: + SET_SIGNED_INT(sizeof(intmax_t)); + break; + case Py_T_UINTMAX: + SET_UNSIGNED_INT(sizeof(uintmax_t)); + break; + case Py_T_INTPTR: + SET_SIGNED_INT(sizeof(intptr_t)); + break; + case Py_T_UINTPTR: + SET_UNSIGNED_INT(sizeof(uintptr_t)); + break; + case Py_T_PTRDIFF: + SET_SIGNED_INT(sizeof(ptrdiff_t)); + break; +#ifndef MS_WINDOWS + case Py_T_OFF: + SET_SIGNED_INT(sizeof(off_t)); + break; +#endif + case Py_T_PID: { +#if !defined(SIZEOF_PID_T) || SIZEOF_PID_T == SIZEOF_INT + pid_t value = PyLong_AsInt(v); +#else + pid_t value = PyLong_AsPid(v); +#endif + if (value == (pid_t)-1 && PyErr_Occurred()) { + return -1; + } + *(pid_t*)addr = value; + break; + } default: PyErr_Format(PyExc_SystemError, "bad memberdescr type for %s", l->name); From 586c6a3c0b14637644557f03a1399bf7b20148a9 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 20 Mar 2024 17:00:54 +0200 Subject: [PATCH 02/11] Add types in nitpick_ignore. --- Doc/conf.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Doc/conf.py b/Doc/conf.py index f4c75c5758cb28..948af0c0cb9537 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -128,14 +128,22 @@ ('c:func', 'vsnprintf'), # Standard C types ('c:type', 'FILE'), + ('c:type', 'int8_t'), + ('c:type', 'int16_t'), + ('c:type', 'int32_t'), ('c:type', 'int64_t'), ('c:type', 'intmax_t'), + ('c:type', 'intptr_t'), ('c:type', 'off_t'), + ('c:type', 'pid_t'), ('c:type', 'ptrdiff_t'), ('c:type', 'siginfo_t'), ('c:type', 'size_t'), ('c:type', 'ssize_t'), ('c:type', 'time_t'), + ('c:type', 'uint8_t'), + ('c:type', 'uint16_t'), + ('c:type', 'uint32_t'), ('c:type', 'uint64_t'), ('c:type', 'uintmax_t'), ('c:type', 'uintptr_t'), From c3e87debe2bf79bfcd85977431e1e4d73e08a618 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 26 Mar 2024 20:04:14 +0200 Subject: [PATCH 03/11] Fix type names on macOS. --- Modules/selectmodule.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index a5567dcc227506..6392fe81e492af 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -1817,7 +1817,7 @@ typedef struct kqueue_queue_Object { # define DATA_TYPE Py_T_INT64 # define DATA_FMT_UNIT INT64_FMT_UNIT #else -# define DATA_TYPE Py_T_INTPTRT +# define DATA_TYPE Py_T_INTPTR # define DATA_FMT_UNIT _Py_PARSE_INTPTRT #endif @@ -1828,12 +1828,12 @@ typedef struct kqueue_queue_Object { #define KQ_OFF(x) offsetof(kqueue_event_Object, x) static struct PyMemberDef kqueue_event_members[] = { - {"ident", Py_T_UINTPTRT, KQ_OFF(e.ident)}, + {"ident", Py_T_UINTPTR, 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)}, {"data", DATA_TYPE, KQ_OFF(e.data)}, - {"udata", Py_T_UINTPTRT, KQ_OFF(e.udata)}, + {"udata", Py_T_UINTPTR, KQ_OFF(e.udata)}, {NULL} /* Sentinel */ }; #undef KQ_OFF From 73edcfe7b4fb9f2724c49927adbceb744bb3b2b8 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 26 Mar 2024 21:53:21 +0200 Subject: [PATCH 04/11] Fix yet one name on macOS. --- Modules/selectmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 6392fe81e492af..846bfc29b766fa 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -1818,7 +1818,7 @@ typedef struct kqueue_queue_Object { # define DATA_FMT_UNIT INT64_FMT_UNIT #else # define DATA_TYPE Py_T_INTPTR -# define DATA_FMT_UNIT _Py_PARSE_INTPTRT +# define DATA_FMT_UNIT _Py_PARSE_INTPTR #endif /* Unfortunately, we can't store python objects in udata, because From 97472feb6357bba646b7073bac31425638738bbc Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 26 Mar 2024 21:58:31 +0200 Subject: [PATCH 05/11] More reuse of constants. --- Modules/_multiprocessing/multiprocessing.h | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Modules/_multiprocessing/multiprocessing.h b/Modules/_multiprocessing/multiprocessing.h index 099004b437828e..4bab9046e2ef4e 100644 --- a/Modules/_multiprocessing/multiprocessing.h +++ b/Modules/_multiprocessing/multiprocessing.h @@ -60,15 +60,8 @@ * Format codes */ -#if SIZEOF_VOID_P == SIZEOF_LONG -# define F_POINTER "k" -# define T_POINTER T_ULONG -#elif SIZEOF_VOID_P == SIZEOF_LONG_LONG -# define F_POINTER "K" -# define T_POINTER T_ULONGLONG -#else -# error "can't find format code for unsigned integer of same size as void*" -#endif +#define F_POINTER _Py_PARSE_UINTPTR +#define T_POINTER Py_T_UINTPTR #ifdef MS_WINDOWS # define F_HANDLE F_POINTER From cf4ce29f35d91c365812c03b9a81a4035f34c888 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 26 Mar 2024 22:39:10 +0200 Subject: [PATCH 06/11] Yet one try to fix build on macOS. --- Modules/selectmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 846bfc29b766fa..a7839cfb33c056 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -1857,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 - _Py_PARSE_UINTPTRT ":kevent"; + _Py_PARSE_UINTPTR ":kevent"; EV_SET(&(self->e), 0, EVFILT_READ, EV_ADD, 0, 0, 0); /* defaults */ From 86902496eb7c9153639943a86c8b4f08dcc285d0 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 28 Mar 2024 20:18:46 +0200 Subject: [PATCH 07/11] :c:expr:`uintptr_t` can now be used. --- Doc/c-api/hash.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/hash.rst b/Doc/c-api/hash.rst index ddf0b3e15dbdbe..ba3d9289c0773b 100644 --- a/Doc/c-api/hash.rst +++ b/Doc/c-api/hash.rst @@ -77,7 +77,7 @@ See also the :c:member:`PyTypeObject.tp_hash` member and :ref:`numeric-hash`. .. c:function:: Py_hash_t Py_HashPointer(const void *ptr) Hash a pointer value: process the pointer value as an integer (cast it to - ``uintptr_t`` internally). The pointer is not dereferenced. + :c:expr:`uintptr_t` internally). The pointer is not dereferenced. The function cannot fail: it cannot return ``-1``. From 1b98718a11319b2d289a838fbde7322fe01f2d80 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 15 Apr 2025 12:17:40 +0300 Subject: [PATCH 08/11] Update versionadded. --- Doc/c-api/structures.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index e4f8bd37b8ebad..1c1c74beb2714d 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -706,7 +706,7 @@ Macro name C type Python type Always ``None``. Must be used with :c:macro:`Py_READONLY`. -.. versionadded:: 3.13 +.. versionadded:: next 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`, From c261729c768536cc6892c2c3a5945ee28c890b19 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 15 Apr 2025 13:42:52 +0300 Subject: [PATCH 09/11] Use PyLong_FromNativeBytes()/PyLong_AsNativeBytes(). --- Lib/test/test_capi/test_structmembers.py | 26 +++++++------- Python/structmember.c | 46 ++++++++++++------------ 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/Lib/test/test_capi/test_structmembers.py b/Lib/test/test_capi/test_structmembers.py index 1c2d60cf171723..7916abcd409f44 100644 --- a/Lib/test/test_capi/test_structmembers.py +++ b/Lib/test/test_capi/test_structmembers.py @@ -64,22 +64,22 @@ def _test_warn(self, name, value, expected=None): if expected is not None: self.assertEqual(getattr(ts, name), expected) - def _test_overflow(self, name, value): + def _test_overflow(self, name, value, error=OverflowError): ts = self.ts - self.assertRaises(OverflowError, setattr, ts, name, value) + self.assertRaises(error, setattr, ts, name, value) def _test_int_range(self, name, minval, maxval, *, hardlimit=None, - indexlimit=None): + indexlimit=None, negvalueerror=OverflowError): if hardlimit is None: hardlimit = (minval, maxval) ts = self.ts self._test_write(name, minval) self._test_write(name, maxval) hardminval, hardmaxval = hardlimit - self._test_overflow(name, hardminval-1) + self._test_overflow(name, hardminval-1, error=negvalueerror) self._test_overflow(name, hardmaxval+1) self._test_overflow(name, 2**1000) - self._test_overflow(name, -2**1000) + self._test_overflow(name, -2**1000, error=negvalueerror) if hardminval < minval: self._test_warn(name, hardminval) self._test_warn(name, minval-1, maxval) @@ -93,10 +93,10 @@ def _test_int_range(self, name, minval, maxval, *, hardlimit=None, else: self._test_write(name, Index(minval), minval) self._test_write(name, Index(maxval), maxval) - self._test_overflow(name, Index(hardminval-1)) + self._test_overflow(name, Index(hardminval-1), error=negvalueerror) self._test_overflow(name, Index(hardmaxval+1)) self._test_overflow(name, Index(2**1000)) - self._test_overflow(name, Index(-2**1000)) + self._test_overflow(name, Index(-2**1000), error=negvalueerror) if hardminval < minval: self._test_warn(name, Index(hardminval)) self._test_warn(name, Index(minval-1), maxval) @@ -191,29 +191,29 @@ def test_size(self): 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) + self._test_int_range('T_UINT8', 0, 2**8-1, negvalueerror=ValueError) 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) + self._test_int_range('T_UINT16', 0, 2**16-1, negvalueerror=ValueError) 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) + self._test_int_range('T_UINT32', 0, 2**32-1, negvalueerror=ValueError) 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) + self._test_int_range('T_UINT64', 0, 2**64-1, negvalueerror=ValueError) 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) + self._test_int_range('T_UINTMAX', 0, 2**bits-1, negvalueerror=ValueError) 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) + self._test_int_range('T_UINTPTR', 0, 2**bits-1, negvalueerror=ValueError) def test_ptrdiff(self): bits = 8*SIZEOF_PTRDIFF_T diff --git a/Python/structmember.c b/Python/structmember.c index 8204b3eda5fa7a..b38fc3da39fa7e 100644 --- a/Python/structmember.c +++ b/Python/structmember.c @@ -145,29 +145,23 @@ PyMember_GetOne(const char *obj_addr, PyMemberDef *l) v = PyLong_FromUnsignedLongLong(*(uint64_t*)addr); break; case Py_T_INTMAX: - v = _PyLong_FromByteArray((const unsigned char *)addr, - sizeof(intmax_t), PY_LITTLE_ENDIAN, 1); + v = PyLong_FromNativeBytes(addr, sizeof(intmax_t), -1); break; case Py_T_UINTMAX: - v = _PyLong_FromByteArray((const unsigned char *)addr, - sizeof(uintmax_t), PY_LITTLE_ENDIAN, 0); + v = PyLong_FromUnsignedNativeBytes(addr, sizeof(uintmax_t), -1); break; case Py_T_INTPTR: - v = _PyLong_FromByteArray((const unsigned char *)addr, - sizeof(intptr_t), PY_LITTLE_ENDIAN, 1); + v = PyLong_FromNativeBytes(addr, sizeof(intptr_t), -1); break; case Py_T_UINTPTR: - v = _PyLong_FromByteArray((const unsigned char *)addr, - sizeof(uintptr_t), PY_LITTLE_ENDIAN, 0); + v = PyLong_FromUnsignedNativeBytes(addr, sizeof(uintptr_t), -1); break; case Py_T_PTRDIFF: - v = _PyLong_FromByteArray((const unsigned char *)addr, - sizeof(ptrdiff_t), PY_LITTLE_ENDIAN, 1); + v = PyLong_FromNativeBytes(addr, sizeof(ptrdiff_t), -1); break; #ifndef MS_WINDOWS case Py_T_OFF: - v = _PyLong_FromByteArray((const unsigned char *)addr, - sizeof(off_t), PY_LITTLE_ENDIAN, 1); + v = PyLong_FromNativeBytes(addr, sizeof(off_t), -1); break; #endif case Py_T_PID: @@ -187,23 +181,31 @@ PyMember_GetOne(const char *obj_addr, PyMemberDef *l) } while (0) #define SET_UNSIGNED_INT(N) do { \ - if (!PyLong_Check(v)) { \ - PyErr_SetString(PyExc_TypeError, "an integer is required"); \ + Py_ssize_t bytes = PyLong_AsNativeBytes(v, addr, (N), \ + Py_ASNATIVEBYTES_NATIVE_ENDIAN | \ + Py_ASNATIVEBYTES_ALLOW_INDEX | \ + Py_ASNATIVEBYTES_UNSIGNED_BUFFER | \ + Py_ASNATIVEBYTES_REJECT_NEGATIVE); \ + if (bytes < 0) { \ + return -1; \ + } \ + if ((size_t)bytes > (N)) { \ + PyErr_SetString(PyExc_OverflowError, "int too big to convert"); \ return -1; \ } \ - return _PyLong_AsByteArray((PyLongObject *)v, (unsigned char *)addr,\ - (N), PY_LITTLE_ENDIAN, 0, 1); \ } while (0) #define SET_SIGNED_INT(N) do { \ - v = _PyNumber_Index(v); \ - if (v == NULL) { \ + Py_ssize_t bytes = PyLong_AsNativeBytes(v, addr, (N), \ + Py_ASNATIVEBYTES_NATIVE_ENDIAN | \ + Py_ASNATIVEBYTES_ALLOW_INDEX); \ + if (bytes < 0) { \ + return -1; \ + } \ + if ((size_t)bytes > (N)) { \ + PyErr_SetString(PyExc_OverflowError, "int too big to convert"); \ return -1; \ } \ - int rc = _PyLong_AsByteArray((PyLongObject *)v, (unsigned char *)addr,\ - (N), PY_LITTLE_ENDIAN, 1, 1); \ - Py_DECREF(v); \ - return rc; \ } while (0) int From ab6071659d5a4f8afb0bfee7c9a6f19c87a062c7 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 15 Apr 2025 17:49:37 +0300 Subject: [PATCH 10/11] Remove Py_T_INTMAX, Py_T_UINTMAX and Py_T_PTRDIFF. --- Doc/c-api/structures.rst | 6 +----- Doc/whatsnew/3.14.rst | 3 +-- Include/descrobject.h | 12 +++++------- Lib/test/test_capi/test_structmembers.py | 9 --------- ...4-03-19-21-13-20.gh-issue-117031.0s3Ruq.rst | 3 +-- Modules/_testcapi/structmember.c | 6 ------ Python/structmember.c | 18 ------------------ 7 files changed, 8 insertions(+), 49 deletions(-) diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index 1c1c74beb2714d..5b0cf1007f17c9 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -636,11 +636,8 @@ Macro name C type Python type .. 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) @@ -713,9 +710,8 @@ Macro name C type Python type :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`. + :c:macro:`Py_T_OFF`, and :c:macro:`Py_T_PID`. Defining Getters and Setters diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 0eede37c7c78b3..a6b75b7db2f34d 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1824,9 +1824,8 @@ New features :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`. + :c:macro:`Py_T_OFF`, and :c:macro:`Py_T_PID`. (Contributed by Serhiy Storchaka in :gh:`117031`.) * Add :c:func:`PyBytes_Join(sep, iterable) ` function, diff --git a/Include/descrobject.h b/Include/descrobject.h index a552ca6c8290a5..931db5392efc5a 100644 --- a/Include/descrobject.h +++ b/Include/descrobject.h @@ -90,17 +90,15 @@ struct PyMemberDef { #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 + +#define Py_T_INTPTR 30 +#define Py_T_UINTPTR 31 #ifdef MS_WINDOWS # define Py_T_OFF Py_T_LONGLONG #else -# define Py_T_OFF 35 +# define Py_T_OFF 32 #endif -#define Py_T_PID 36 +#define Py_T_PID 33 /* Flags */ #define Py_READONLY 1 diff --git a/Lib/test/test_capi/test_structmembers.py b/Lib/test/test_capi/test_structmembers.py index 7916abcd409f44..d8fe8f945dfc2c 100644 --- a/Lib/test/test_capi/test_structmembers.py +++ b/Lib/test/test_capi/test_structmembers.py @@ -205,20 +205,11 @@ 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, negvalueerror=ValueError) - 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, negvalueerror=ValueError) - 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, negvalueerror=ValueError) - 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) diff --git a/Misc/NEWS.d/next/C_API/2024-03-19-21-13-20.gh-issue-117031.0s3Ruq.rst b/Misc/NEWS.d/next/C_API/2024-03-19-21-13-20.gh-issue-117031.0s3Ruq.rst index 21a92de8a59e33..9f492de9c55cb2 100644 --- a/Misc/NEWS.d/next/C_API/2024-03-19-21-13-20.gh-issue-117031.0s3Ruq.rst +++ b/Misc/NEWS.d/next/C_API/2024-03-19-21-13-20.gh-issue-117031.0s3Ruq.rst @@ -3,6 +3,5 @@ Add support for new member types for :c:member:`PyMemberDef.type`: :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_INTPTR`, :c:macro:`Py_T_UINTPTR`, :c:macro:`Py_T_OFF`, and :c:macro:`Py_T_PID`. diff --git a/Modules/_testcapi/structmember.c b/Modules/_testcapi/structmember.c index 61ffcd4d8de1be..267728148da67d 100644 --- a/Modules/_testcapi/structmember.c +++ b/Modules/_testcapi/structmember.c @@ -32,11 +32,8 @@ typedef struct { 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 @@ -78,11 +75,8 @@ static struct PyMemberDef test_members_newapi[] = { {"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} diff --git a/Python/structmember.c b/Python/structmember.c index b38fc3da39fa7e..8cf91ac33d7746 100644 --- a/Python/structmember.c +++ b/Python/structmember.c @@ -144,21 +144,12 @@ PyMember_GetOne(const char *obj_addr, PyMemberDef *l) case Py_T_UINT64: v = PyLong_FromUnsignedLongLong(*(uint64_t*)addr); break; - case Py_T_INTMAX: - v = PyLong_FromNativeBytes(addr, sizeof(intmax_t), -1); - break; - case Py_T_UINTMAX: - v = PyLong_FromUnsignedNativeBytes(addr, sizeof(uintmax_t), -1); - break; case Py_T_INTPTR: v = PyLong_FromNativeBytes(addr, sizeof(intptr_t), -1); break; case Py_T_UINTPTR: v = PyLong_FromUnsignedNativeBytes(addr, sizeof(uintptr_t), -1); break; - case Py_T_PTRDIFF: - v = PyLong_FromNativeBytes(addr, sizeof(ptrdiff_t), -1); - break; #ifndef MS_WINDOWS case Py_T_OFF: v = PyLong_FromNativeBytes(addr, sizeof(off_t), -1); @@ -473,21 +464,12 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v) case Py_T_UINT64: SET_UNSIGNED_INT(8); break; - case Py_T_INTMAX: - SET_SIGNED_INT(sizeof(intmax_t)); - break; - case Py_T_UINTMAX: - SET_UNSIGNED_INT(sizeof(uintmax_t)); - break; case Py_T_INTPTR: SET_SIGNED_INT(sizeof(intptr_t)); break; case Py_T_UINTPTR: SET_UNSIGNED_INT(sizeof(uintptr_t)); break; - case Py_T_PTRDIFF: - SET_SIGNED_INT(sizeof(ptrdiff_t)); - break; #ifndef MS_WINDOWS case Py_T_OFF: SET_SIGNED_INT(sizeof(off_t)); From a32421d463ae817afa21b77a9846bbd3829450a1 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 17 Apr 2025 16:01:14 +0300 Subject: [PATCH 11/11] Support combined. --- Include/descrobject.h | 27 ++++++++++----------- Lib/test/test_capi/test_structmembers.py | 10 +++++--- Modules/_testcapi/structmember.c | 8 +++++++ Python/structmember.c | 30 ++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 17 deletions(-) diff --git a/Include/descrobject.h b/Include/descrobject.h index 931db5392efc5a..6ad4b412528910 100644 --- a/Include/descrobject.h +++ b/Include/descrobject.h @@ -81,24 +81,23 @@ struct PyMemberDef { #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_INTPTR 30 -#define Py_T_UINTPTR 31 +#define Py_T_INTPTR 22 +#define Py_T_UINTPTR 23 #ifdef MS_WINDOWS # define Py_T_OFF Py_T_LONGLONG #else -# define Py_T_OFF 32 +# define Py_T_OFF 24 #endif -#define Py_T_PID 33 +#define Py_T_PID 25 + +#define Py_T_INT8 0x100|0x1 +#define Py_T_UINT8 0x100|0x2 +#define Py_T_INT16 0x104|0x1 +#define Py_T_UINT16 0x104|0x2 +#define Py_T_INT32 0x108|0x1 +#define Py_T_UINT32 0x108|0x2 +#define Py_T_INT64 0x110|0x1 +#define Py_T_UINT64 0x110|0x2 /* Flags */ #define Py_READONLY 1 diff --git a/Lib/test/test_capi/test_structmembers.py b/Lib/test/test_capi/test_structmembers.py index d8fe8f945dfc2c..bf8a1e21b0d8ab 100644 --- a/Lib/test/test_capi/test_structmembers.py +++ b/Lib/test/test_capi/test_structmembers.py @@ -69,12 +69,12 @@ def _test_overflow(self, name, value, error=OverflowError): self.assertRaises(error, setattr, ts, name, value) def _test_int_range(self, name, minval, maxval, *, hardlimit=None, - indexlimit=None, negvalueerror=OverflowError): + indexlimit=None, negvalueerror=OverflowError, wrap=False): if hardlimit is None: hardlimit = (minval, maxval) ts = self.ts self._test_write(name, minval) - self._test_write(name, maxval) + self._test_write(name, maxval, -1 if wrap else maxval) hardminval, hardmaxval = hardlimit self._test_overflow(name, hardminval-1, error=negvalueerror) self._test_overflow(name, hardmaxval+1) @@ -92,7 +92,7 @@ def _test_int_range(self, name, minval, maxval, *, hardlimit=None, self.assertRaises(TypeError, setattr, ts, name, Index(maxval)) else: self._test_write(name, Index(minval), minval) - self._test_write(name, Index(maxval), maxval) + self._test_write(name, Index(maxval), -1 if wrap else maxval) self._test_overflow(name, Index(hardminval-1), error=negvalueerror) self._test_overflow(name, Index(hardmaxval+1)) self._test_overflow(name, Index(2**1000)) @@ -192,18 +192,22 @@ def test_size(self): 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, negvalueerror=ValueError) + self._test_int_range('T_XINT8', -2**7, 2**8-1, wrap=True) 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, negvalueerror=ValueError) + self._test_int_range('T_XINT16', -2**15, 2**16-1, wrap=True) 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, negvalueerror=ValueError) + self._test_int_range('T_XINT32', -2**31, 2**32-1, wrap=True) 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, negvalueerror=ValueError) + self._test_int_range('T_XINT64', -2**63, 2**64-1, wrap=True) def test_intptr(self): bits = 8*SIZEOF_INTPTR_T diff --git a/Modules/_testcapi/structmember.c b/Modules/_testcapi/structmember.c index 267728148da67d..fd1daff5f9812c 100644 --- a/Modules/_testcapi/structmember.c +++ b/Modules/_testcapi/structmember.c @@ -26,12 +26,16 @@ typedef struct { char char_member; int8_t int8_member; uint8_t uint8_member; + uint8_t xint8_member; int16_t int16_member; uint16_t uint16_member; + uint16_t xint16_member; int32_t int32_member; uint32_t uint32_member; + uint32_t xint32_member; int64_t int64_member; uint64_t uint64_member; + uint64_t xint64_member; intptr_t intptr_member; uintptr_t uintptr_member; #ifdef MS_WINDOWS @@ -69,12 +73,16 @@ static struct PyMemberDef test_members_newapi[] = { {"T_CHAR", Py_T_CHAR, offsetof(test_structmembers, structmembers.char_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_XINT8", Py_T_INT8|Py_T_UINT8, offsetof(test_structmembers, structmembers.xint8_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_XINT16", Py_T_INT16|Py_T_UINT16, offsetof(test_structmembers, structmembers.xint16_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_XINT32", Py_T_INT32|Py_T_UINT32, offsetof(test_structmembers, structmembers.xint32_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_XINT64", Py_T_INT64|Py_T_UINT64, offsetof(test_structmembers, structmembers.xint64_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_OFF", Py_T_OFF, offsetof(test_structmembers, structmembers.off_member), 0, NULL}, diff --git a/Python/structmember.c b/Python/structmember.c index 8cf91ac33d7746..af2fec8edd4a52 100644 --- a/Python/structmember.c +++ b/Python/structmember.c @@ -121,24 +121,28 @@ PyMember_GetOne(const char *obj_addr, PyMemberDef *l) v = Py_NewRef(Py_None); break; case Py_T_INT8: + case Py_T_INT8|Py_T_UINT8: v = PyLong_FromLong(*(int8_t*)addr); break; case Py_T_UINT8: v = PyLong_FromUnsignedLong(*(uint8_t*)addr); break; case Py_T_INT16: + case Py_T_INT16|Py_T_UINT16: v = PyLong_FromLong(*(int16_t*)addr); break; case Py_T_UINT16: v = PyLong_FromUnsignedLong(*(uint16_t*)addr); break; case Py_T_INT32: + case Py_T_INT32|Py_T_UINT32: v = PyLong_FromLong(*(int32_t*)addr); break; case Py_T_UINT32: v = PyLong_FromUnsignedLong(*(uint32_t*)addr); break; case Py_T_INT64: + case Py_T_INT64|Py_T_UINT64: v = PyLong_FromLongLong(*(int64_t*)addr); break; case Py_T_UINT64: @@ -199,6 +203,20 @@ PyMember_GetOne(const char *obj_addr, PyMemberDef *l) } \ } while (0) +#define SET_COMBINED_INT(N) do { \ + Py_ssize_t bytes = PyLong_AsNativeBytes(v, addr, (N), \ + Py_ASNATIVEBYTES_NATIVE_ENDIAN | \ + Py_ASNATIVEBYTES_ALLOW_INDEX | \ + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); \ + if (bytes < 0) { \ + return -1; \ + } \ + if ((size_t)bytes > (N)) { \ + PyErr_SetString(PyExc_OverflowError, "int too big to convert"); \ + return -1; \ + } \ + } while (0) + int PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v) { @@ -446,24 +464,36 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v) case Py_T_UINT8: SET_UNSIGNED_INT(1); break; + case Py_T_INT8|Py_T_UINT8: + SET_COMBINED_INT(1); + break; case Py_T_INT16: SET_SIGNED_INT(2); break; case Py_T_UINT16: SET_UNSIGNED_INT(2); break; + case Py_T_INT16|Py_T_UINT16: + SET_COMBINED_INT(2); + break; case Py_T_INT32: SET_SIGNED_INT(4); break; case Py_T_UINT32: SET_UNSIGNED_INT(4); break; + case Py_T_INT32|Py_T_UINT32: + SET_COMBINED_INT(4); + break; case Py_T_INT64: SET_SIGNED_INT(8); break; case Py_T_UINT64: SET_UNSIGNED_INT(8); break; + case Py_T_INT64|Py_T_UINT64: + SET_COMBINED_INT(8); + break; case Py_T_INTPTR: SET_SIGNED_INT(sizeof(intptr_t)); break;