Skip to content

Commit d9d6909

Browse files
gh-115011: Improve support of __index__() in setters of members with unsigned integer type (GH-115029)
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.
1 parent d2c4baa commit d9d6909

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
@@ -81,36 +81,22 @@ def _test_int_range(self, name, minval, maxval, *, hardlimit=None,
8181
self._test_warn(name, maxval+1, minval)
8282
self._test_warn(name, hardmaxval)
8383

84-
if indexlimit is None:
85-
indexlimit = hardlimit
86-
if not indexlimit:
84+
if indexlimit is False:
8785
self.assertRaises(TypeError, setattr, ts, name, Index(minval))
8886
self.assertRaises(TypeError, setattr, ts, name, Index(maxval))
8987
else:
90-
hardminindexval, hardmaxindexval = indexlimit
9188
self._test_write(name, Index(minval), minval)
92-
if minval < hardminindexval:
93-
self._test_write(name, Index(hardminindexval), hardminindexval)
94-
if maxval < hardmaxindexval:
95-
self._test_write(name, Index(maxval), maxval)
96-
else:
97-
self._test_write(name, Index(hardmaxindexval), hardmaxindexval)
98-
self._test_overflow(name, Index(hardminindexval-1))
99-
if name in ('T_UINT', 'T_ULONG'):
100-
self.assertRaises(TypeError, setattr, self.ts, name,
101-
Index(hardmaxindexval+1))
102-
self.assertRaises(TypeError, setattr, self.ts, name,
103-
Index(2**1000))
104-
else:
105-
self._test_overflow(name, Index(hardmaxindexval+1))
106-
self._test_overflow(name, Index(2**1000))
89+
self._test_write(name, Index(maxval), maxval)
90+
self._test_overflow(name, Index(hardminval-1))
91+
self._test_overflow(name, Index(hardmaxval+1))
92+
self._test_overflow(name, Index(2**1000))
10793
self._test_overflow(name, Index(-2**1000))
108-
if hardminindexval < minval and name != 'T_ULONGLONG':
109-
self._test_warn(name, Index(hardminindexval))
110-
self._test_warn(name, Index(minval-1))
111-
if maxval < hardmaxindexval:
112-
self._test_warn(name, Index(maxval+1))
113-
self._test_warn(name, Index(hardmaxindexval))
94+
if hardminval < minval:
95+
self._test_warn(name, Index(hardminval))
96+
self._test_warn(name, Index(minval-1), maxval)
97+
if maxval < hardmaxval:
98+
self._test_warn(name, Index(maxval+1), minval)
99+
self._test_warn(name, Index(hardmaxval))
114100

115101
def test_bool(self):
116102
ts = self.ts
@@ -138,22 +124,20 @@ def test_int(self):
138124
self._test_int_range('T_INT', INT_MIN, INT_MAX,
139125
hardlimit=(LONG_MIN, LONG_MAX))
140126
self._test_int_range('T_UINT', 0, UINT_MAX,
141-
hardlimit=(LONG_MIN, ULONG_MAX),
142-
indexlimit=(LONG_MIN, LONG_MAX))
127+
hardlimit=(LONG_MIN, ULONG_MAX))
143128

144129
def test_long(self):
145130
self._test_int_range('T_LONG', LONG_MIN, LONG_MAX)
146131
self._test_int_range('T_ULONG', 0, ULONG_MAX,
147-
hardlimit=(LONG_MIN, ULONG_MAX),
148-
indexlimit=(LONG_MIN, LONG_MAX))
132+
hardlimit=(LONG_MIN, ULONG_MAX))
149133

150134
def test_py_ssize_t(self):
151135
self._test_int_range('T_PYSSIZET', PY_SSIZE_T_MIN, PY_SSIZE_T_MAX, indexlimit=False)
152136

153137
def test_longlong(self):
154138
self._test_int_range('T_LONGLONG', LLONG_MIN, LLONG_MAX)
155139
self._test_int_range('T_ULONGLONG', 0, ULLONG_MAX,
156-
indexlimit=(LONG_MIN, LONG_MAX))
140+
hardlimit=(LONG_MIN, ULLONG_MAX))
157141

158142
def test_bad_assignments(self):
159143
ts = self.ts
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
@@ -2,6 +2,8 @@
22
/* Map C struct members to Python object attributes */
33

44
#include "Python.h"
5+
#include "pycore_abstract.h" // _PyNumber_Index()
6+
#include "pycore_long.h" // _PyLong_IsNegative()
57

68

79
PyObject *
@@ -200,27 +202,22 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
200202
case Py_T_UINT: {
201203
/* XXX: For compatibility, accept negative int values
202204
as well. */
203-
int overflow;
204-
long long_val = PyLong_AsLongAndOverflow(v, &overflow);
205-
if (long_val == -1 && PyErr_Occurred()) {
206-
return -1;
207-
}
208-
if (overflow < 0) {
209-
PyErr_SetString(PyExc_OverflowError,
210-
"Python int too large to convert to C long");
205+
v = _PyNumber_Index(v);
206+
if (v == NULL) {
211207
return -1;
212208
}
213-
else if (!overflow) {
214-
*(unsigned int *)addr = (unsigned int)(unsigned long)long_val;
215-
if (long_val < 0) {
216-
WARN("Writing negative value into unsigned field");
217-
}
218-
else if ((unsigned long)long_val > UINT_MAX) {
219-
WARN("Truncation of value to unsigned short");
209+
if (_PyLong_IsNegative((PyLongObject *)v)) {
210+
long long_val = PyLong_AsLong(v);
211+
Py_DECREF(v);
212+
if (long_val == -1 && PyErr_Occurred()) {
213+
return -1;
220214
}
215+
*(unsigned int *)addr = (unsigned int)(unsigned long)long_val;
216+
WARN("Writing negative value into unsigned field");
221217
}
222218
else {
223219
unsigned long ulong_val = PyLong_AsUnsignedLong(v);
220+
Py_DECREF(v);
224221
if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
225222
return -1;
226223
}
@@ -240,24 +237,22 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
240237
case Py_T_ULONG: {
241238
/* XXX: For compatibility, accept negative int values
242239
as well. */
243-
int overflow;
244-
long long_val = PyLong_AsLongAndOverflow(v, &overflow);
245-
if (long_val == -1 && PyErr_Occurred()) {
246-
return -1;
247-
}
248-
if (overflow < 0) {
249-
PyErr_SetString(PyExc_OverflowError,
250-
"Python int too large to convert to C long");
240+
v = _PyNumber_Index(v);
241+
if (v == NULL) {
251242
return -1;
252243
}
253-
else if (!overflow) {
254-
*(unsigned long *)addr = (unsigned long)long_val;
255-
if (long_val < 0) {
256-
WARN("Writing negative value into unsigned field");
244+
if (_PyLong_IsNegative((PyLongObject *)v)) {
245+
long long_val = PyLong_AsLong(v);
246+
Py_DECREF(v);
247+
if (long_val == -1 && PyErr_Occurred()) {
248+
return -1;
257249
}
250+
*(unsigned long *)addr = (unsigned long)long_val;
251+
WARN("Writing negative value into unsigned field");
258252
}
259253
else {
260254
unsigned long ulong_val = PyLong_AsUnsignedLong(v);
255+
Py_DECREF(v);
261256
if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
262257
return -1;
263258
}
@@ -313,18 +308,30 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
313308
return -1;
314309
break;
315310
}
316-
case Py_T_ULONGLONG:{
317-
unsigned long long value;
318-
/* ??? PyLong_AsLongLong accepts an int, but PyLong_AsUnsignedLongLong
319-
doesn't ??? */
320-
if (PyLong_Check(v))
321-
*(unsigned long long*)addr = value = PyLong_AsUnsignedLongLong(v);
322-
else
323-
*(unsigned long long*)addr = value = PyLong_AsLong(v);
324-
if ((value == (unsigned long long)-1) && PyErr_Occurred())
311+
case Py_T_ULONGLONG: {
312+
v = _PyNumber_Index(v);
313+
if (v == NULL) {
325314
return -1;
326-
break;
327315
}
316+
if (_PyLong_IsNegative((PyLongObject *)v)) {
317+
long long_val = PyLong_AsLong(v);
318+
Py_DECREF(v);
319+
if (long_val == -1 && PyErr_Occurred()) {
320+
return -1;
321+
}
322+
*(unsigned long long *)addr = (unsigned long long)(long long)long_val;
323+
WARN("Writing negative value into unsigned field");
324+
}
325+
else {
326+
unsigned long long ulonglong_val = PyLong_AsUnsignedLongLong(v);
327+
Py_DECREF(v);
328+
if (ulonglong_val == (unsigned long long)-1 && PyErr_Occurred()) {
329+
return -1;
330+
}
331+
*(unsigned long long*)addr = ulonglong_val;
332+
}
333+
break;
334+
}
328335
default:
329336
PyErr_Format(PyExc_SystemError,
330337
"bad memberdescr type for %s", l->name);

0 commit comments

Comments
 (0)