]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-132987: Support __index__() for unsigned integers in Argument Clinic (GH-133011)
authorSerhiy Storchaka <storchaka@gmail.com>
Mon, 28 Apr 2025 08:02:18 +0000 (11:02 +0300)
committerGitHub <noreply@github.com>
Mon, 28 Apr 2025 08:02:18 +0000 (11:02 +0300)
Misc/NEWS.d/next/Library/2025-04-26-17-41-20.gh-issue-132987.xxBCqg.rst [new file with mode: 0644]
Objects/longobject.c
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 (file)
index 0000000..7b75da3
--- /dev/null
@@ -0,0 +1,2 @@
+Many builtin and extension functions which accept an unsigned integer
+argument, now use :meth:`~object.__index__` if available.
index 2dfd82bab1a834eb3d41dff22b0896adc8e16f94..40d90ecf4fa068230995c61c91463704166a267a 100644 (file)
@@ -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)                                \
index b0557ae54c13621d2f69205fe0f280b4b70d53dd..633fb5f56a6693b50a5c2501d4765d2cd538cfd2 100644 (file)
@@ -211,6 +211,28 @@ class short_converter(CConverter):
         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 @@ class unsigned_short_converter(CConverter):
                 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 @@ class unsigned_int_converter(CConverter):
                 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 @@ class unsigned_long_converter(CConverter):
             )
         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 @@ class unsigned_long_long_converter(CConverter):
             )
         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 @@ class size_t_converter(CConverter):
                 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):