https://github.com/python/cpython/commit/3ddc5152550ea62280124c37d0b4339030ff7df4
commit: 3ddc5152550ea62280124c37d0b4339030ff7df4
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2024-02-04T17:32:25+02:00
summary:

gh-114388: Fix warnings when assign an unsigned integer member (GH-114391)

* 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.

files:
A Misc/NEWS.d/next/Core and 
Builtins/2024-01-21-17-29-32.gh-issue-114388.UVGO4K.rst
M Lib/test/test_capi/test_structmembers.py
M Python/structmember.c

diff --git a/Lib/test/test_capi/test_structmembers.py 
b/Lib/test/test_capi/test_structmembers.py
index 2cf46b203478dc..415b8033bd16b3 100644
--- a/Lib/test/test_capi/test_structmembers.py
+++ b/Lib/test/test_capi/test_structmembers.py
@@ -14,6 +14,13 @@
     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 @@ def test_int(self):
         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 @@ def test_long(self):
         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 @@ def test_ushort_max(self):
         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
index 00000000000000..52c2742001d9ca
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and 
Builtins/2024-01-21-17-29-32.gh-issue-114388.UVGO4K.rst 
@@ -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.
diff --git a/Python/structmember.c b/Python/structmember.c
index 7a5a6a49d23116..18bd486952419b 100644
--- a/Python/structmember.c
+++ b/Python/structmember.c
@@ -197,45 +197,72 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
             WARN("Truncation of value to int");
         break;
         }
-    case Py_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 Py_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");
+        }
+        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 Py_T_LONG:{
         *(long*)addr = PyLong_AsLong(v);
         if ((*(long*)addr == -1) && PyErr_Occurred())
             return -1;
         break;
         }
-    case Py_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 Py_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");
+        }
+        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 Py_T_PYSSIZET:{
         *(Py_ssize_t*)addr = PyLong_AsSsize_t(v);
         if ((*(Py_ssize_t*)addr == (Py_ssize_t)-1)

_______________________________________________
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]

Reply via email to