Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.12] gh-114388: Fix warnings when assign an unsigned integer member (GH-114391) #115001

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions Lib/test/test_capi/test_structmembers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
PY_SSIZE_T_MAX, PY_SSIZE_T_MIN,
)


class Index:
def __init__(self, value):
self.value = value
def __index__(self):
return self.value

# There are two classes: one using <structmember.h> and another using
# `Py_`-prefixed API. They should behave the same in Python

Expand Down Expand Up @@ -72,6 +79,10 @@ def test_int(self):
self.assertEqual(ts.T_INT, INT_MIN)
ts.T_UINT = UINT_MAX
self.assertEqual(ts.T_UINT, UINT_MAX)
ts.T_UINT = Index(0)
self.assertEqual(ts.T_UINT, 0)
ts.T_UINT = Index(INT_MAX)
self.assertEqual(ts.T_UINT, INT_MAX)

def test_long(self):
ts = self.ts
Expand All @@ -81,6 +92,10 @@ def test_long(self):
self.assertEqual(ts.T_LONG, LONG_MIN)
ts.T_ULONG = ULONG_MAX
self.assertEqual(ts.T_ULONG, ULONG_MAX)
ts.T_ULONG = Index(0)
self.assertEqual(ts.T_ULONG, 0)
ts.T_ULONG = Index(LONG_MAX)
self.assertEqual(ts.T_ULONG, LONG_MAX)

def test_py_ssize_t(self):
ts = self.ts
Expand Down Expand Up @@ -173,6 +188,28 @@ def test_ushort_max(self):
with warnings_helper.check_warnings(('', RuntimeWarning)):
ts.T_USHORT = USHRT_MAX+1

def test_int(self):
ts = self.ts
if LONG_MIN < INT_MIN:
with self.assertWarns(RuntimeWarning):
ts.T_INT = INT_MIN-1
if LONG_MAX > INT_MAX:
with self.assertWarns(RuntimeWarning):
ts.T_INT = INT_MAX+1

def test_uint(self):
ts = self.ts
with self.assertWarns(RuntimeWarning):
ts.T_UINT = -1
if ULONG_MAX > UINT_MAX:
with self.assertWarns(RuntimeWarning):
ts.T_UINT = UINT_MAX+1

def test_ulong(self):
ts = self.ts
with self.assertWarns(RuntimeWarning):
ts.T_ULONG = -1

class TestWarnings_OldAPI(TestWarnings, unittest.TestCase):
cls = _test_structmembersType_OldAPI

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Fix a :exc:`RuntimeWarning` emitted when assign an integer-like value that
is not an instance of :class:`int` to an attribute that corresponds to a C
struct member of :ref:`type <PyMemberDef-types>` T_UINT and T_ULONG. Fix a
double :exc:`RuntimeWarning` emitted when assign a negative integer value to
an attribute that corresponds to a C struct member of type T_UINT.
85 changes: 57 additions & 28 deletions Python/structmember.c
Original file line number Diff line number Diff line change
Expand Up @@ -197,45 +197,74 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
WARN("Truncation of value to int");
break;
}
case T_UINT:{
unsigned long ulong_val = PyLong_AsUnsignedLong(v);
if ((ulong_val == (unsigned long)-1) && PyErr_Occurred()) {
/* XXX: For compatibility, accept negative int values
as well. */
PyErr_Clear();
ulong_val = PyLong_AsLong(v);
if ((ulong_val == (unsigned long)-1) &&
PyErr_Occurred())
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");
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");
}
}
else {
unsigned long ulong_val = PyLong_AsUnsignedLong(v);
if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
return -1;
*(unsigned int *)addr = (unsigned int)ulong_val;
WARN("Writing negative value into unsigned field");
} else
*(unsigned int *)addr = (unsigned int)ulong_val;
if (ulong_val > UINT_MAX)
WARN("Truncation of value to unsigned int");
break;
}
*(unsigned int*)addr = (unsigned int)ulong_val;
if (ulong_val > UINT_MAX) {
WARN("Truncation of value to unsigned int");
}
}
break;
}
case T_LONG:{
*(long*)addr = PyLong_AsLong(v);
if ((*(long*)addr == -1) && PyErr_Occurred())
return -1;
break;
}
case T_ULONG:{
*(unsigned long*)addr = PyLong_AsUnsignedLong(v);
if ((*(unsigned long*)addr == (unsigned long)-1)
&& PyErr_Occurred()) {
/* XXX: For compatibility, accept negative int values
as well. */
PyErr_Clear();
*(unsigned long*)addr = PyLong_AsLong(v);
if ((*(unsigned long*)addr == (unsigned long)-1)
&& PyErr_Occurred())
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");
return -1;
}
else if (!overflow) {
*(unsigned long *)addr = (unsigned long)long_val;
if (long_val < 0) {
WARN("Writing negative value into unsigned field");
}
}
else {
unsigned long ulong_val = PyLong_AsUnsignedLong(v);
if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
return -1;
WARN("Writing negative value into unsigned field");
}
*(unsigned long*)addr = ulong_val;
}
break;
}
}
case T_PYSSIZET:{
*(Py_ssize_t*)addr = PyLong_AsSsize_t(v);
if ((*(Py_ssize_t*)addr == (Py_ssize_t)-1)
Expand Down
Loading