https://github.com/python/cpython/commit/6677c2c165c32765d3b61a496f806319ba31f768
commit: 6677c2c165c32765d3b61a496f806319ba31f768
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
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 -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]