https://github.com/python/cpython/commit/6677c2c165c32765d3b61a496f806319ba31f768 commit: 6677c2c165c32765d3b61a496f806319ba31f768 branch: main author: Serhiy Storchaka <storch...@gmail.com> committer: serhiy-storchaka <storch...@gmail.com> date: 2025-04-28T11:02:18+03:00 summary:
gh-132987: Support __index__() for unsigned integers in Argument Clinic (GH-133011) files: A Misc/NEWS.d/next/Library/2025-04-26-17-41-20.gh-issue-132987.xxBCqg.rst M Objects/longobject.c M Tools/clinic/libclinic/converters.py diff --git a/Misc/NEWS.d/next/Library/2025-04-26-17-41-20.gh-issue-132987.xxBCqg.rst b/Misc/NEWS.d/next/Library/2025-04-26-17-41-20.gh-issue-132987.xxBCqg.rst new file mode 100644 index 00000000000000..7b75da382a0a6e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-04-26-17-41-20.gh-issue-132987.xxBCqg.rst @@ -0,0 +1,2 @@ +Many builtin and extension functions which accept an unsigned integer +argument, now use :meth:`~object.__index__` if available. diff --git a/Objects/longobject.c b/Objects/longobject.c index 2dfd82bab1a834..40d90ecf4fa068 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -1735,100 +1735,31 @@ PyLong_AsLongLongAndOverflow(PyObject *vv, int *overflow) return res; } -int -_PyLong_UnsignedShort_Converter(PyObject *obj, void *ptr) -{ - unsigned long uval; - - if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) { - PyErr_SetString(PyExc_ValueError, "value must be positive"); - return 0; - } - uval = PyLong_AsUnsignedLong(obj); - if (uval == (unsigned long)-1 && PyErr_Occurred()) - return 0; - if (uval > USHRT_MAX) { - PyErr_SetString(PyExc_OverflowError, - "Python int too large for C unsigned short"); - return 0; - } - - *(unsigned short *)ptr = Py_SAFE_DOWNCAST(uval, unsigned long, unsigned short); - return 1; -} - -int -_PyLong_UnsignedInt_Converter(PyObject *obj, void *ptr) -{ - unsigned long uval; - - if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) { - PyErr_SetString(PyExc_ValueError, "value must be positive"); - return 0; - } - uval = PyLong_AsUnsignedLong(obj); - if (uval == (unsigned long)-1 && PyErr_Occurred()) - return 0; - if (uval > UINT_MAX) { - PyErr_SetString(PyExc_OverflowError, - "Python int too large for C unsigned int"); - return 0; - } - - *(unsigned int *)ptr = Py_SAFE_DOWNCAST(uval, unsigned long, unsigned int); - return 1; -} - -int -_PyLong_UnsignedLong_Converter(PyObject *obj, void *ptr) -{ - unsigned long uval; - - if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) { - PyErr_SetString(PyExc_ValueError, "value must be positive"); - return 0; - } - uval = PyLong_AsUnsignedLong(obj); - if (uval == (unsigned long)-1 && PyErr_Occurred()) - return 0; - - *(unsigned long *)ptr = uval; - return 1; -} - -int -_PyLong_UnsignedLongLong_Converter(PyObject *obj, void *ptr) -{ - unsigned long long uval; - - if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) { - PyErr_SetString(PyExc_ValueError, "value must be positive"); - return 0; - } - uval = PyLong_AsUnsignedLongLong(obj); - if (uval == (unsigned long long)-1 && PyErr_Occurred()) - return 0; - - *(unsigned long long *)ptr = uval; - return 1; -} - -int -_PyLong_Size_t_Converter(PyObject *obj, void *ptr) -{ - size_t uval; - - if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) { - PyErr_SetString(PyExc_ValueError, "value must be positive"); - return 0; - } - uval = PyLong_AsSize_t(obj); - if (uval == (size_t)-1 && PyErr_Occurred()) - return 0; - - *(size_t *)ptr = uval; - return 1; -} +#define UNSIGNED_INT_CONVERTER(NAME, TYPE) \ +int \ +_PyLong_##NAME##_Converter(PyObject *obj, void *ptr) \ +{ \ + Py_ssize_t bytes = PyLong_AsNativeBytes(obj, ptr, sizeof(TYPE), \ + Py_ASNATIVEBYTES_NATIVE_ENDIAN | \ + Py_ASNATIVEBYTES_ALLOW_INDEX | \ + Py_ASNATIVEBYTES_REJECT_NEGATIVE | \ + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); \ + if (bytes < 0) { \ + return 0; \ + } \ + if ((size_t)bytes > sizeof(TYPE)) { \ + PyErr_SetString(PyExc_OverflowError, \ + "Python int too large for C "#TYPE); \ + return 0; \ + } \ + return 1; \ +} + +UNSIGNED_INT_CONVERTER(UnsignedShort, unsigned short) +UNSIGNED_INT_CONVERTER(UnsignedInt, unsigned int) +UNSIGNED_INT_CONVERTER(UnsignedLong, unsigned long) +UNSIGNED_INT_CONVERTER(UnsignedLongLong, unsigned long long) +UNSIGNED_INT_CONVERTER(Size_t, size_t) #define CHECK_BINOP(v,w) \ diff --git a/Tools/clinic/libclinic/converters.py b/Tools/clinic/libclinic/converters.py index b0557ae54c1362..633fb5f56a6693 100644 --- a/Tools/clinic/libclinic/converters.py +++ b/Tools/clinic/libclinic/converters.py @@ -211,6 +211,28 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st return super().parse_arg(argname, displayname, limited_capi=limited_capi) +def format_inline_unsigned_int_converter(self: CConverter, argname: str) -> str: + return self.format_code(""" + {{{{ + Py_ssize_t _bytes = PyLong_AsNativeBytes({argname}, &{paramname}, sizeof({type}), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_REJECT_NEGATIVE | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) {{{{ + goto exit; + }}}} + if ((size_t)_bytes > sizeof({type})) {{{{ + PyErr_SetString(PyExc_OverflowError, + "Python int too large for C {type}"); + goto exit; + }}}} + }}}} + """, + argname=argname, + type=self.type) + + class unsigned_short_converter(CConverter): type = 'unsigned short' default_type = int @@ -238,22 +260,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st argname=argname) if not limited_capi: return super().parse_arg(argname, displayname, limited_capi=limited_capi) - # NOTE: Raises OverflowError for negative integer. - return self.format_code(""" - {{{{ - unsigned long uval = PyLong_AsUnsignedLong({argname}); - if (uval == (unsigned long)-1 && PyErr_Occurred()) {{{{ - goto exit; - }}}} - if (uval > USHRT_MAX) {{{{ - PyErr_SetString(PyExc_OverflowError, - "Python int too large for C unsigned short"); - goto exit; - }}}} - {paramname} = (unsigned short) uval; - }}}} - """, - argname=argname) + return format_inline_unsigned_int_converter(self, argname) @add_legacy_c_converter('C', accept={str}) @@ -331,22 +338,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st argname=argname) if not limited_capi: return super().parse_arg(argname, displayname, limited_capi=limited_capi) - # NOTE: Raises OverflowError for negative integer. - return self.format_code(""" - {{{{ - unsigned long uval = PyLong_AsUnsignedLong({argname}); - if (uval == (unsigned long)-1 && PyErr_Occurred()) {{{{ - goto exit; - }}}} - if (uval > UINT_MAX) {{{{ - PyErr_SetString(PyExc_OverflowError, - "Python int too large for C unsigned int"); - goto exit; - }}}} - {paramname} = (unsigned int) uval; - }}}} - """, - argname=argname) + return format_inline_unsigned_int_converter(self, argname) class long_converter(CConverter): @@ -397,14 +389,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st ) if not limited_capi: return super().parse_arg(argname, displayname, limited_capi=limited_capi) - # NOTE: Raises OverflowError for negative integer. - return self.format_code(""" - {paramname} = PyLong_AsUnsignedLong({argname}); - if ({paramname} == (unsigned long)-1 && PyErr_Occurred()) {{{{ - goto exit; - }}}} - """, - argname=argname) + return format_inline_unsigned_int_converter(self, argname) class long_long_converter(CConverter): @@ -455,14 +440,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st ) if not limited_capi: return super().parse_arg(argname, displayname, limited_capi=limited_capi) - # NOTE: Raises OverflowError for negative integer. - return self.format_code(""" - {paramname} = PyLong_AsUnsignedLongLong({argname}); - if ({paramname} == (unsigned long long)-1 && PyErr_Occurred()) {{{{ - goto exit; - }}}} - """, - argname=argname) + return format_inline_unsigned_int_converter(self, argname) class Py_ssize_t_converter(CConverter): @@ -599,14 +577,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st argname=argname) if not limited_capi: return super().parse_arg(argname, displayname, limited_capi=limited_capi) - # NOTE: Raises OverflowError for negative integer. - return self.format_code(""" - {paramname} = PyLong_AsSize_t({argname}); - if ({paramname} == (size_t)-1 && PyErr_Occurred()) {{{{ - goto exit; - }}}} - """, - argname=argname) + return format_inline_unsigned_int_converter(self, argname) class fildes_converter(CConverter): _______________________________________________ Python-checkins mailing list -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: arch...@mail-archive.com