Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit b9937a6

Browse filesBrowse files
[3.12] gh-114388: Fix warnings when assign an unsigned integer member (GH-114391) (GH-115001)
* 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. (cherry picked from commit 3ddc515)
1 parent 4548ae7 commit b9937a6
Copy full SHA for b9937a6

File tree

Expand file treeCollapse file tree

3 files changed

+99
-28
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+99
-28
lines changed

‎Lib/test/test_capi/test_structmembers.py

Copy file name to clipboardExpand all lines: Lib/test/test_capi/test_structmembers.py
+37Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414
PY_SSIZE_T_MAX, PY_SSIZE_T_MIN,
1515
)
1616

17+
18+
class Index:
19+
def __init__(self, value):
20+
self.value = value
21+
def __index__(self):
22+
return self.value
23+
1724
# There are two classes: one using <structmember.h> and another using
1825
# `Py_`-prefixed API. They should behave the same in Python
1926

@@ -72,6 +79,10 @@ def test_int(self):
7279
self.assertEqual(ts.T_INT, INT_MIN)
7380
ts.T_UINT = UINT_MAX
7481
self.assertEqual(ts.T_UINT, UINT_MAX)
82+
ts.T_UINT = Index(0)
83+
self.assertEqual(ts.T_UINT, 0)
84+
ts.T_UINT = Index(INT_MAX)
85+
self.assertEqual(ts.T_UINT, INT_MAX)
7586

7687
def test_long(self):
7788
ts = self.ts
@@ -81,6 +92,10 @@ def test_long(self):
8192
self.assertEqual(ts.T_LONG, LONG_MIN)
8293
ts.T_ULONG = ULONG_MAX
8394
self.assertEqual(ts.T_ULONG, ULONG_MAX)
95+
ts.T_ULONG = Index(0)
96+
self.assertEqual(ts.T_ULONG, 0)
97+
ts.T_ULONG = Index(LONG_MAX)
98+
self.assertEqual(ts.T_ULONG, LONG_MAX)
8499

85100
def test_py_ssize_t(self):
86101
ts = self.ts
@@ -173,6 +188,28 @@ def test_ushort_max(self):
173188
with warnings_helper.check_warnings(('', RuntimeWarning)):
174189
ts.T_USHORT = USHRT_MAX+1
175190

191+
def test_int(self):
192+
ts = self.ts
193+
if LONG_MIN < INT_MIN:
194+
with self.assertWarns(RuntimeWarning):
195+
ts.T_INT = INT_MIN-1
196+
if LONG_MAX > INT_MAX:
197+
with self.assertWarns(RuntimeWarning):
198+
ts.T_INT = INT_MAX+1
199+
200+
def test_uint(self):
201+
ts = self.ts
202+
with self.assertWarns(RuntimeWarning):
203+
ts.T_UINT = -1
204+
if ULONG_MAX > UINT_MAX:
205+
with self.assertWarns(RuntimeWarning):
206+
ts.T_UINT = UINT_MAX+1
207+
208+
def test_ulong(self):
209+
ts = self.ts
210+
with self.assertWarns(RuntimeWarning):
211+
ts.T_ULONG = -1
212+
176213
class TestWarnings_OldAPI(TestWarnings, unittest.TestCase):
177214
cls = _test_structmembersType_OldAPI
178215

+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fix a :exc:`RuntimeWarning` emitted when assign an integer-like value that
2+
is not an instance of :class:`int` to an attribute that corresponds to a C
3+
struct member of :ref:`type <PyMemberDef-types>` T_UINT and T_ULONG. Fix a
4+
double :exc:`RuntimeWarning` emitted when assign a negative integer value to
5+
an attribute that corresponds to a C struct member of type T_UINT.

‎Python/structmember.c

Copy file name to clipboardExpand all lines: Python/structmember.c
+57-28Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -197,45 +197,74 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
197197
WARN("Truncation of value to int");
198198
break;
199199
}
200-
case T_UINT:{
201-
unsigned long ulong_val = PyLong_AsUnsignedLong(v);
202-
if ((ulong_val == (unsigned long)-1) && PyErr_Occurred()) {
203-
/* XXX: For compatibility, accept negative int values
204-
as well. */
205-
PyErr_Clear();
206-
ulong_val = PyLong_AsLong(v);
207-
if ((ulong_val == (unsigned long)-1) &&
208-
PyErr_Occurred())
200+
case T_UINT: {
201+
/* XXX: For compatibility, accept negative int values
202+
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");
211+
return -1;
212+
}
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");
220+
}
221+
}
222+
else {
223+
unsigned long ulong_val = PyLong_AsUnsignedLong(v);
224+
if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
209225
return -1;
210-
*(unsigned int *)addr = (unsigned int)ulong_val;
211-
WARN("Writing negative value into unsigned field");
212-
} else
213-
*(unsigned int *)addr = (unsigned int)ulong_val;
214-
if (ulong_val > UINT_MAX)
215-
WARN("Truncation of value to unsigned int");
216-
break;
226+
}
227+
*(unsigned int*)addr = (unsigned int)ulong_val;
228+
if (ulong_val > UINT_MAX) {
229+
WARN("Truncation of value to unsigned int");
230+
}
217231
}
232+
break;
233+
}
218234
case T_LONG:{
219235
*(long*)addr = PyLong_AsLong(v);
220236
if ((*(long*)addr == -1) && PyErr_Occurred())
221237
return -1;
222238
break;
223239
}
224-
case T_ULONG:{
225-
*(unsigned long*)addr = PyLong_AsUnsignedLong(v);
226-
if ((*(unsigned long*)addr == (unsigned long)-1)
227-
&& PyErr_Occurred()) {
228-
/* XXX: For compatibility, accept negative int values
229-
as well. */
230-
PyErr_Clear();
231-
*(unsigned long*)addr = PyLong_AsLong(v);
232-
if ((*(unsigned long*)addr == (unsigned long)-1)
233-
&& PyErr_Occurred())
240+
case T_ULONG: {
241+
/* XXX: For compatibility, accept negative int values
242+
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");
251+
return -1;
252+
}
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");
257+
}
258+
}
259+
else {
260+
unsigned long ulong_val = PyLong_AsUnsignedLong(v);
261+
if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
234262
return -1;
235-
WARN("Writing negative value into unsigned field");
263+
}
264+
*(unsigned long*)addr = ulong_val;
236265
}
237266
break;
238-
}
267+
}
239268
case T_PYSSIZET:{
240269
*(Py_ssize_t*)addr = PyLong_AsSsize_t(v);
241270
if ((*(Py_ssize_t*)addr == (Py_ssize_t)-1)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.