]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-114388: Fix warnings when assign an unsigned integer member (GH-114391...
authorSerhiy Storchaka <storchaka@gmail.com>
Sun, 4 Feb 2024 17:21:05 +0000 (19:21 +0200)
committerGitHub <noreply@github.com>
Sun, 4 Feb 2024 17:21:05 +0000 (17:21 +0000)
* Fix a RuntimeWarning emitted when assign an integer-like value that
  is not an instance of int to an attribute that corresponds to a C
  struct member of type T_UINT and T_ULONG.
* Fix a double RuntimeWarning emitted when assign a negative integer value
  to an attribute that corresponds to a C struct member of type T_UINT.
(cherry picked from commit 3ddc5152550ea62280124c37d0b4339030ff7df4)

Lib/test/test_capi/test_structmembers.py
Misc/NEWS.d/next/Core and Builtins/2024-01-21-17-29-32.gh-issue-114388.UVGO4K.rst [new file with mode: 0644]
Python/structmember.c

index 2cf46b203478dc237f013adccef6664c5998a20f..415b8033bd16b3c9799f24fbdf90225811f83a47 100644 (file)
@@ -14,6 +14,13 @@ from _testcapi import (_test_structmembersType_OldAPI,
     PY_SSIZE_T_MAX, PY_SSIZE_T_MIN,
     )
 
+
+class Index:
+    def __init__(self, value):
+        self.value = value
+    def __index__(self):
+        return self.value
+
 # There are two classes: one using <structmember.h> and another using
 # `Py_`-prefixed API. They should behave the same in Python
 
@@ -72,6 +79,10 @@ class ReadWriteTests:
         self.assertEqual(ts.T_INT, INT_MIN)
         ts.T_UINT = UINT_MAX
         self.assertEqual(ts.T_UINT, UINT_MAX)
+        ts.T_UINT = Index(0)
+        self.assertEqual(ts.T_UINT, 0)
+        ts.T_UINT = Index(INT_MAX)
+        self.assertEqual(ts.T_UINT, INT_MAX)
 
     def test_long(self):
         ts = self.ts
@@ -81,6 +92,10 @@ class ReadWriteTests:
         self.assertEqual(ts.T_LONG, LONG_MIN)
         ts.T_ULONG = ULONG_MAX
         self.assertEqual(ts.T_ULONG, ULONG_MAX)
+        ts.T_ULONG = Index(0)
+        self.assertEqual(ts.T_ULONG, 0)
+        ts.T_ULONG = Index(LONG_MAX)
+        self.assertEqual(ts.T_ULONG, LONG_MAX)
 
     def test_py_ssize_t(self):
         ts = self.ts
@@ -173,6 +188,28 @@ class TestWarnings:
         with warnings_helper.check_warnings(('', RuntimeWarning)):
             ts.T_USHORT = USHRT_MAX+1
 
+    def test_int(self):
+        ts = self.ts
+        if LONG_MIN < INT_MIN:
+            with self.assertWarns(RuntimeWarning):
+                ts.T_INT = INT_MIN-1
+        if LONG_MAX > INT_MAX:
+            with self.assertWarns(RuntimeWarning):
+                ts.T_INT = INT_MAX+1
+
+    def test_uint(self):
+        ts = self.ts
+        with self.assertWarns(RuntimeWarning):
+            ts.T_UINT = -1
+        if ULONG_MAX > UINT_MAX:
+            with self.assertWarns(RuntimeWarning):
+                ts.T_UINT = UINT_MAX+1
+
+    def test_ulong(self):
+        ts = self.ts
+        with self.assertWarns(RuntimeWarning):
+            ts.T_ULONG = -1
+
 class TestWarnings_OldAPI(TestWarnings, unittest.TestCase):
     cls = _test_structmembersType_OldAPI
 
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-01-21-17-29-32.gh-issue-114388.UVGO4K.rst b/Misc/NEWS.d/next/Core and Builtins/2024-01-21-17-29-32.gh-issue-114388.UVGO4K.rst
new file mode 100644 (file)
index 0000000..52c2742
--- /dev/null
@@ -0,0 +1,5 @@
+Fix a :exc:`RuntimeWarning` emitted when assign an integer-like value that
+is not an instance of :class:`int` to an attribute that corresponds to a C
+struct member of :ref:`type <PyMemberDef-types>` T_UINT and T_ULONG. Fix a
+double :exc:`RuntimeWarning` emitted when assign a negative integer value to
+an attribute that corresponds to a C struct member of type T_UINT.
index 19a75224a0f32e052154e93ae9a8f608ee97c1fa..ebebaa0a034e0c7b1dd4e99ee57a2ca56ed86e93 100644 (file)
@@ -197,45 +197,74 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
             WARN("Truncation of value to int");
         break;
         }
-    case T_UINT:{
-        unsigned long ulong_val = PyLong_AsUnsignedLong(v);
-        if ((ulong_val == (unsigned long)-1) && PyErr_Occurred()) {
-            /* XXX: For compatibility, accept negative int values
-               as well. */
-            PyErr_Clear();
-            ulong_val = PyLong_AsLong(v);
-            if ((ulong_val == (unsigned long)-1) &&
-                PyErr_Occurred())
+    case T_UINT: {
+        /* XXX: For compatibility, accept negative int values
+           as well. */
+        int overflow;
+        long long_val = PyLong_AsLongAndOverflow(v, &overflow);
+        if (long_val == -1 && PyErr_Occurred()) {
+            return -1;
+        }
+        if (overflow < 0) {
+            PyErr_SetString(PyExc_OverflowError,
+                            "Python int too large to convert to C long");
+            return -1;
+        }
+        else if (!overflow) {
+            *(unsigned int *)addr = (unsigned int)(unsigned long)long_val;
+            if (long_val < 0) {
+                WARN("Writing negative value into unsigned field");
+            }
+            else if ((unsigned long)long_val > UINT_MAX) {
+                WARN("Truncation of value to unsigned short");
+            }
+        }
+        else {
+            unsigned long ulong_val = PyLong_AsUnsignedLong(v);
+            if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
                 return -1;
-            *(unsigned int *)addr = (unsigned int)ulong_val;
-            WARN("Writing negative value into unsigned field");
-        } else
-            *(unsigned int *)addr = (unsigned int)ulong_val;
-        if (ulong_val > UINT_MAX)
-            WARN("Truncation of value to unsigned int");
-        break;
+            }
+            *(unsigned int*)addr = (unsigned int)ulong_val;
+            if (ulong_val > UINT_MAX) {
+                WARN("Truncation of value to unsigned int");
+            }
         }
+        break;
+    }
     case T_LONG:{
         *(long*)addr = PyLong_AsLong(v);
         if ((*(long*)addr == -1) && PyErr_Occurred())
             return -1;
         break;
         }
-    case T_ULONG:{
-        *(unsigned long*)addr = PyLong_AsUnsignedLong(v);
-        if ((*(unsigned long*)addr == (unsigned long)-1)
-            && PyErr_Occurred()) {
-            /* XXX: For compatibility, accept negative int values
-               as well. */
-            PyErr_Clear();
-            *(unsigned long*)addr = PyLong_AsLong(v);
-            if ((*(unsigned long*)addr == (unsigned long)-1)
-                && PyErr_Occurred())
+    case T_ULONG: {
+        /* XXX: For compatibility, accept negative int values
+           as well. */
+        int overflow;
+        long long_val = PyLong_AsLongAndOverflow(v, &overflow);
+        if (long_val == -1 && PyErr_Occurred()) {
+            return -1;
+        }
+        if (overflow < 0) {
+            PyErr_SetString(PyExc_OverflowError,
+                            "Python int too large to convert to C long");
+            return -1;
+        }
+        else if (!overflow) {
+            *(unsigned long *)addr = (unsigned long)long_val;
+            if (long_val < 0) {
+                WARN("Writing negative value into unsigned field");
+            }
+        }
+        else {
+            unsigned long ulong_val = PyLong_AsUnsignedLong(v);
+            if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
                 return -1;
-            WARN("Writing negative value into unsigned field");
+            }
+            *(unsigned long*)addr = ulong_val;
         }
         break;
-        }
+    }
     case T_PYSSIZET:{
         *(Py_ssize_t*)addr = PyLong_AsSsize_t(v);
         if ((*(Py_ssize_t*)addr == (Py_ssize_t)-1)