Skip to content

Commit d8346d6

Browse files
[3.12] gh-115011: Improve support of __index__() in setters of members with unsigned integer type (GH-115029) (GH-115294)
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 d65cd8b commit d8346d6

File tree

3 files changed

+62
-67
lines changed

3 files changed

+62
-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: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
#include "Python.h"
55
#include "structmember.h" // PyMemberDef
6+
#include "pycore_abstract.h" // _PyNumber_Index()
7+
#include "pycore_long.h" // _PyLong_IsNegative()
8+
69

710
PyObject *
811
PyMember_GetOne(const char *obj_addr, PyMemberDef *l)
@@ -200,27 +203,22 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
200203
case T_UINT: {
201204
/* XXX: For compatibility, accept negative int values
202205
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");
206+
v = _PyNumber_Index(v);
207+
if (v == NULL) {
211208
return -1;
212209
}
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");
210+
if (_PyLong_IsNegative((PyLongObject *)v)) {
211+
long long_val = PyLong_AsLong(v);
212+
Py_DECREF(v);
213+
if (long_val == -1 && PyErr_Occurred()) {
214+
return -1;
220215
}
216+
*(unsigned int *)addr = (unsigned int)(unsigned long)long_val;
217+
WARN("Writing negative value into unsigned field");
221218
}
222219
else {
223220
unsigned long ulong_val = PyLong_AsUnsignedLong(v);
221+
Py_DECREF(v);
224222
if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
225223
return -1;
226224
}
@@ -240,24 +238,22 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
240238
case T_ULONG: {
241239
/* XXX: For compatibility, accept negative int values
242240
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");
241+
v = _PyNumber_Index(v);
242+
if (v == NULL) {
251243
return -1;
252244
}
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");
245+
if (_PyLong_IsNegative((PyLongObject *)v)) {
246+
long long_val = PyLong_AsLong(v);
247+
Py_DECREF(v);
248+
if (long_val == -1 && PyErr_Occurred()) {
249+
return -1;
257250
}
251+
*(unsigned long *)addr = (unsigned long)long_val;
252+
WARN("Writing negative value into unsigned field");
258253
}
259254
else {
260255
unsigned long ulong_val = PyLong_AsUnsignedLong(v);
256+
Py_DECREF(v);
261257
if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
262258
return -1;
263259
}
@@ -313,18 +309,30 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
313309
return -1;
314310
break;
315311
}
316-
case 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())
312+
case Py_T_ULONGLONG: {
313+
v = _PyNumber_Index(v);
314+
if (v == NULL) {
325315
return -1;
326-
break;
327316
}
317+
if (_PyLong_IsNegative((PyLongObject *)v)) {
318+
long long_val = PyLong_AsLong(v);
319+
Py_DECREF(v);
320+
if (long_val == -1 && PyErr_Occurred()) {
321+
return -1;
322+
}
323+
*(unsigned long long *)addr = (unsigned long long)(long long)long_val;
324+
WARN("Writing negative value into unsigned field");
325+
}
326+
else {
327+
unsigned long long ulonglong_val = PyLong_AsUnsignedLongLong(v);
328+
Py_DECREF(v);
329+
if (ulonglong_val == (unsigned long long)-1 && PyErr_Occurred()) {
330+
return -1;
331+
}
332+
*(unsigned long long*)addr = ulonglong_val;
333+
}
334+
break;
335+
}
328336
default:
329337
PyErr_Format(PyExc_SystemError,
330338
"bad memberdescr type for %s", l->name);

0 commit comments

Comments
 (0)