]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.11] gh-115011: Improve support of __index__() in setters of members with unsigned...
authorSerhiy Storchaka <storchaka@gmail.com>
Sun, 11 Feb 2024 12:03:48 +0000 (14:03 +0200)
committerGitHub <noreply@github.com>
Sun, 11 Feb 2024 12:03:48 +0000 (12:03 +0000)
Setters for members with an unsigned integer type now support
the same range of valid values for objects that has a __index__()
method as for int.

Previously, Py_T_UINT, Py_T_ULONG and Py_T_ULLONG did not support
objects that has a __index__() method larger than LONG_MAX.

Py_T_ULLONG did not support negative ints. Now it supports them and
emits a RuntimeWarning.
(cherry picked from commit d9d6909697501a2604d5895f9f88aeec61274ab0)

Lib/test/test_capi/test_structmembers.py
Misc/NEWS.d/next/Core and Builtins/2024-02-05-12-40-26.gh-issue-115011.L1AKF5.rst [new file with mode: 0644]
Python/structmember.c

index 06b170c83fe9b0ed0429fa1100fc7c2635ab529a..91b229de2bdbf183488d03655b6dfc7ff4b7fb34 100644 (file)
@@ -69,36 +69,22 @@ class ReadWriteTests(unittest.TestCase):
             self._test_warn(name, maxval+1, minval)
             self._test_warn(name, hardmaxval)
 
-        if indexlimit is None:
-            indexlimit = hardlimit
-        if not indexlimit:
+        if indexlimit is False:
             self.assertRaises(TypeError, setattr, ts, name, Index(minval))
             self.assertRaises(TypeError, setattr, ts, name, Index(maxval))
         else:
-            hardminindexval, hardmaxindexval = indexlimit
             self._test_write(name, Index(minval), minval)
-            if minval < hardminindexval:
-                self._test_write(name, Index(hardminindexval), hardminindexval)
-            if maxval < hardmaxindexval:
-                self._test_write(name, Index(maxval), maxval)
-            else:
-                self._test_write(name, Index(hardmaxindexval), hardmaxindexval)
-            self._test_overflow(name, Index(hardminindexval-1))
-            if name in ('T_UINT', 'T_ULONG'):
-                self.assertRaises(TypeError, setattr, ts, name,
-                                  Index(hardmaxindexval+1))
-                self.assertRaises(TypeError, setattr, ts, name,
-                                  Index(2**1000))
-            else:
-                self._test_overflow(name, Index(hardmaxindexval+1))
-                self._test_overflow(name, Index(2**1000))
+            self._test_write(name, Index(maxval), maxval)
+            self._test_overflow(name, Index(hardminval-1))
+            self._test_overflow(name, Index(hardmaxval+1))
+            self._test_overflow(name, Index(2**1000))
             self._test_overflow(name, Index(-2**1000))
-            if hardminindexval < minval and name != 'T_ULONGLONG':
-                self._test_warn(name, Index(hardminindexval))
-                self._test_warn(name, Index(minval-1))
-            if maxval < hardmaxindexval:
-                self._test_warn(name, Index(maxval+1))
-                self._test_warn(name, Index(hardmaxindexval))
+            if hardminval < minval:
+                self._test_warn(name, Index(hardminval))
+                self._test_warn(name, Index(minval-1), maxval)
+            if maxval < hardmaxval:
+                self._test_warn(name, Index(maxval+1), minval)
+                self._test_warn(name, Index(hardmaxval))
 
     def test_bool(self):
         ts.T_BOOL = True
@@ -125,14 +111,12 @@ class ReadWriteTests(unittest.TestCase):
         self._test_int_range('T_INT', INT_MIN, INT_MAX,
                              hardlimit=(LONG_MIN, LONG_MAX))
         self._test_int_range('T_UINT', 0, UINT_MAX,
-                             hardlimit=(LONG_MIN, ULONG_MAX),
-                             indexlimit=(LONG_MIN, LONG_MAX))
+                             hardlimit=(LONG_MIN, ULONG_MAX))
 
     def test_long(self):
         self._test_int_range('T_LONG', LONG_MIN, LONG_MAX)
         self._test_int_range('T_ULONG', 0, ULONG_MAX,
-                             hardlimit=(LONG_MIN, ULONG_MAX),
-                             indexlimit=(LONG_MIN, LONG_MAX))
+                             hardlimit=(LONG_MIN, ULONG_MAX))
 
     def test_py_ssize_t(self):
         self._test_int_range('T_PYSSIZET', PY_SSIZE_T_MIN, PY_SSIZE_T_MAX, indexlimit=False)
@@ -141,7 +125,7 @@ class ReadWriteTests(unittest.TestCase):
     def test_longlong(self):
         self._test_int_range('T_LONGLONG', LLONG_MIN, LLONG_MAX)
         self._test_int_range('T_ULONGLONG', 0, ULLONG_MAX,
-                             indexlimit=(LONG_MIN, LONG_MAX))
+                             hardlimit=(LONG_MIN, ULLONG_MAX))
 
     def test_bad_assignments(self):
         integer_attributes = [
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-02-05-12-40-26.gh-issue-115011.L1AKF5.rst b/Misc/NEWS.d/next/Core and Builtins/2024-02-05-12-40-26.gh-issue-115011.L1AKF5.rst
new file mode 100644 (file)
index 0000000..cf91a4f
--- /dev/null
@@ -0,0 +1,3 @@
+Setters for members with an unsigned integer type now support the same range
+of valid values for objects that has a :meth:`~object.__index__` method as
+for :class:`int`.
index c790656978734ac23bde5cfe85140fa13d3c6233..7ed7aabefa399355e8ce785a094afae7aaeab30f 100644 (file)
@@ -3,6 +3,8 @@
 
 #include "Python.h"
 #include "structmember.h"         // PyMemberDef
+#include "pycore_abstract.h"      // _PyNumber_Index()
+
 
 PyObject *
 PyMember_GetOne(const char *obj_addr, PyMemberDef *l)
@@ -190,27 +192,22 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
     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");
+        v = _PyNumber_Index(v);
+        if (v == NULL) {
             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");
+        if (Py_SIZE(v) < 0) {
+            long long_val = PyLong_AsLong(v);
+            Py_DECREF(v);
+            if (long_val == -1 && PyErr_Occurred()) {
+                return -1;
             }
+            *(unsigned int *)addr = (unsigned int)(unsigned long)long_val;
+            WARN("Writing negative value into unsigned field");
         }
         else {
             unsigned long ulong_val = PyLong_AsUnsignedLong(v);
+            Py_DECREF(v);
             if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
                 return -1;
             }
@@ -230,24 +227,22 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
     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");
+        v = _PyNumber_Index(v);
+        if (v == NULL) {
             return -1;
         }
-        else if (!overflow) {
-            *(unsigned long *)addr = (unsigned long)long_val;
-            if (long_val < 0) {
-                WARN("Writing negative value into unsigned field");
+        if (Py_SIZE(v) < 0) {
+            long long_val = PyLong_AsLong(v);
+            Py_DECREF(v);
+            if (long_val == -1 && PyErr_Occurred()) {
+                return -1;
             }
+            *(unsigned long *)addr = (unsigned long)long_val;
+            WARN("Writing negative value into unsigned field");
         }
         else {
             unsigned long ulong_val = PyLong_AsUnsignedLong(v);
+            Py_DECREF(v);
             if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
                 return -1;
             }
@@ -304,18 +299,30 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
             return -1;
         break;
         }
-    case T_ULONGLONG:{
-        unsigned long long value;
-        /* ??? PyLong_AsLongLong accepts an int, but PyLong_AsUnsignedLongLong
-            doesn't ??? */
-        if (PyLong_Check(v))
-            *(unsigned long long*)addr = value = PyLong_AsUnsignedLongLong(v);
-        else
-            *(unsigned long long*)addr = value = PyLong_AsLong(v);
-        if ((value == (unsigned long long)-1) && PyErr_Occurred())
+    case T_ULONGLONG: {
+        v = _PyNumber_Index(v);
+        if (v == NULL) {
             return -1;
-        break;
         }
+        if (Py_SIZE(v) < 0) {
+            long long_val = PyLong_AsLong(v);
+            Py_DECREF(v);
+            if (long_val == -1 && PyErr_Occurred()) {
+                return -1;
+            }
+            *(unsigned long long *)addr = (unsigned long long)(long long)long_val;
+            WARN("Writing negative value into unsigned field");
+        }
+        else {
+            unsigned long long ulonglong_val = PyLong_AsUnsignedLongLong(v);
+            Py_DECREF(v);
+            if (ulonglong_val == (unsigned long long)-1 && PyErr_Occurred()) {
+                return -1;
+            }
+            *(unsigned long long*)addr = ulonglong_val;
+        }
+        break;
+    }
     default:
         PyErr_Format(PyExc_SystemError,
                      "bad memberdescr type for %s", l->name);