Skip to content

Commit 7273a58

Browse files
[3.11] gh-115011: Improve support of __index__() in setters of members with unsigned integer type (GH-115029) (GH-115295)
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 d9d6909)
1 parent e722550 commit 7273a58

File tree

3 files changed

+61
-67
lines changed

3 files changed

+61
-67
lines changed

Lib/test/test_capi/test_structmembers.py

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -69,36 +69,22 @@ def _test_int_range(self, name, minval, maxval, *, hardlimit=None,
6969
self._test_warn(name, maxval+1, minval)
7070
self._test_warn(name, hardmaxval)
7171

72-
if indexlimit is None:
73-
indexlimit = hardlimit
74-
if not indexlimit:
72+
if indexlimit is False:
7573
self.assertRaises(TypeError, setattr, ts, name, Index(minval))
7674
self.assertRaises(TypeError, setattr, ts, name, Index(maxval))
7775
else:
78-
hardminindexval, hardmaxindexval = indexlimit
7976
self._test_write(name, Index(minval), minval)
80-
if minval < hardminindexval:
81-
self._test_write(name, Index(hardminindexval), hardminindexval)
82-
if maxval < hardmaxindexval:
83-
self._test_write(name, Index(maxval), maxval)
84-
else:
85-
self._test_write(name, Index(hardmaxindexval), hardmaxindexval)
86-
self._test_overflow(name, Index(hardminindexval-1))
87-
if name in ('T_UINT', 'T_ULONG'):
88-
self.assertRaises(TypeError, setattr, ts, name,
89-
Index(hardmaxindexval+1))
90-
self.assertRaises(TypeError, setattr, ts, name,
91-
Index(2**1000))
92-
else:
93-
self._test_overflow(name, Index(hardmaxindexval+1))
94-
self._test_overflow(name, Index(2**1000))
77+
self._test_write(name, Index(maxval), maxval)
78+
self._test_overflow(name, Index(hardminval-1))
79+
self._test_overflow(name, Index(hardmaxval+1))
80+
self._test_overflow(name, Index(2**1000))
9581
self._test_overflow(name, Index(-2**1000))
96-
if hardminindexval < minval and name != 'T_ULONGLONG':
97-
self._test_warn(name, Index(hardminindexval))
98-
self._test_warn(name, Index(minval-1))
99-
if maxval < hardmaxindexval:
100-
self._test_warn(name, Index(maxval+1))
101-
self._test_warn(name, Index(hardmaxindexval))
82+
if hardminval < minval:
83+
self._test_warn(name, Index(hardminval))
84+
self._test_warn(name, Index(minval-1), maxval)
85+
if maxval < hardmaxval:
86+
self._test_warn(name, Index(maxval+1), minval)
87+
self._test_warn(name, Index(hardmaxval))
10288

10389
def test_bool(self):
10490
ts.T_BOOL = True
@@ -125,14 +111,12 @@ def test_int(self):
125111
self._test_int_range('T_INT', INT_MIN, INT_MAX,
126112
hardlimit=(LONG_MIN, LONG_MAX))
127113
self._test_int_range('T_UINT', 0, UINT_MAX,
128-
hardlimit=(LONG_MIN, ULONG_MAX),
129-
indexlimit=(LONG_MIN, LONG_MAX))
114+
hardlimit=(LONG_MIN, ULONG_MAX))
130115

131116
def test_long(self):
132117
self._test_int_range('T_LONG', LONG_MIN, LONG_MAX)
133118
self._test_int_range('T_ULONG', 0, ULONG_MAX,
134-
hardlimit=(LONG_MIN, ULONG_MAX),
135-
indexlimit=(LONG_MIN, LONG_MAX))
119+
hardlimit=(LONG_MIN, ULONG_MAX))
136120

137121
def test_py_ssize_t(self):
138122
self._test_int_range('T_PYSSIZET', PY_SSIZE_T_MIN, PY_SSIZE_T_MAX, indexlimit=False)
@@ -141,7 +125,7 @@ def test_py_ssize_t(self):
141125
def test_longlong(self):
142126
self._test_int_range('T_LONGLONG', LLONG_MIN, LLONG_MAX)
143127
self._test_int_range('T_ULONGLONG', 0, ULLONG_MAX,
144-
indexlimit=(LONG_MIN, LONG_MAX))
128+
hardlimit=(LONG_MIN, ULLONG_MAX))
145129

146130
def test_bad_assignments(self):
147131
integer_attributes = [
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Setters for members with an unsigned integer type now support the same range
2+
of valid values for objects that has a :meth:`~object.__index__` method as
3+
for :class:`int`.

Python/structmember.c

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#include "Python.h"
55
#include "structmember.h" // PyMemberDef
6+
#include "pycore_abstract.h" // _PyNumber_Index()
7+
68

79
PyObject *
810
PyMember_GetOne(const char *obj_addr, PyMemberDef *l)
@@ -190,27 +192,22 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
190192
case T_UINT: {
191193
/* XXX: For compatibility, accept negative int values
192194
as well. */
193-
int overflow;
194-
long long_val = PyLong_AsLongAndOverflow(v, &overflow);
195-
if (long_val == -1 && PyErr_Occurred()) {
196-
return -1;
197-
}
198-
if (overflow < 0) {
199-
PyErr_SetString(PyExc_OverflowError,
200-
"Python int too large to convert to C long");
195+
v = _PyNumber_Index(v);
196+
if (v == NULL) {
201197
return -1;
202198
}
203-
else if (!overflow) {
204-
*(unsigned int *)addr = (unsigned int)(unsigned long)long_val;
205-
if (long_val < 0) {
206-
WARN("Writing negative value into unsigned field");
207-
}
208-
else if ((unsigned long)long_val > UINT_MAX) {
209-
WARN("Truncation of value to unsigned short");
199+
if (Py_SIZE(v) < 0) {
200+
long long_val = PyLong_AsLong(v);
201+
Py_DECREF(v);
202+
if (long_val == -1 && PyErr_Occurred()) {
203+
return -1;
210204
}
205+
*(unsigned int *)addr = (unsigned int)(unsigned long)long_val;
206+
WARN("Writing negative value into unsigned field");
211207
}
212208
else {
213209
unsigned long ulong_val = PyLong_AsUnsignedLong(v);
210+
Py_DECREF(v);
214211
if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
215212
return -1;
216213
}
@@ -230,24 +227,22 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
230227
case T_ULONG: {
231228
/* XXX: For compatibility, accept negative int values
232229
as well. */
233-
int overflow;
234-
long long_val = PyLong_AsLongAndOverflow(v, &overflow);
235-
if (long_val == -1 && PyErr_Occurred()) {
236-
return -1;
237-
}
238-
if (overflow < 0) {
239-
PyErr_SetString(PyExc_OverflowError,
240-
"Python int too large to convert to C long");
230+
v = _PyNumber_Index(v);
231+
if (v == NULL) {
241232
return -1;
242233
}
243-
else if (!overflow) {
244-
*(unsigned long *)addr = (unsigned long)long_val;
245-
if (long_val < 0) {
246-
WARN("Writing negative value into unsigned field");
234+
if (Py_SIZE(v) < 0) {
235+
long long_val = PyLong_AsLong(v);
236+
Py_DECREF(v);
237+
if (long_val == -1 && PyErr_Occurred()) {
238+
return -1;
247239
}
240+
*(unsigned long *)addr = (unsigned long)long_val;
241+
WARN("Writing negative value into unsigned field");
248242
}
249243
else {
250244
unsigned long ulong_val = PyLong_AsUnsignedLong(v);
245+
Py_DECREF(v);
251246
if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
252247
return -1;
253248
}
@@ -304,18 +299,30 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
304299
return -1;
305300
break;
306301
}
307-
case T_ULONGLONG:{
308-
unsigned long long value;
309-
/* ??? PyLong_AsLongLong accepts an int, but PyLong_AsUnsignedLongLong
310-
doesn't ??? */
311-
if (PyLong_Check(v))
312-
*(unsigned long long*)addr = value = PyLong_AsUnsignedLongLong(v);
313-
else
314-
*(unsigned long long*)addr = value = PyLong_AsLong(v);
315-
if ((value == (unsigned long long)-1) && PyErr_Occurred())
302+
case T_ULONGLONG: {
303+
v = _PyNumber_Index(v);
304+
if (v == NULL) {
316305
return -1;
317-
break;
318306
}
307+
if (Py_SIZE(v) < 0) {
308+
long long_val = PyLong_AsLong(v);
309+
Py_DECREF(v);
310+
if (long_val == -1 && PyErr_Occurred()) {
311+
return -1;
312+
}
313+
*(unsigned long long *)addr = (unsigned long long)(long long)long_val;
314+
WARN("Writing negative value into unsigned field");
315+
}
316+
else {
317+
unsigned long long ulonglong_val = PyLong_AsUnsignedLongLong(v);
318+
Py_DECREF(v);
319+
if (ulonglong_val == (unsigned long long)-1 && PyErr_Occurred()) {
320+
return -1;
321+
}
322+
*(unsigned long long*)addr = ulonglong_val;
323+
}
324+
break;
325+
}
319326
default:
320327
PyErr_Format(PyExc_SystemError,
321328
"bad memberdescr type for %s", l->name);

0 commit comments

Comments
 (0)