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

Reply via email to