summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_capi/test_structmembers.py35
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2024-01-21-17-29-32.gh-issue-114388.UVGO4K.rst5
-rw-r--r--Python/structmember.c85
3 files changed, 97 insertions, 28 deletions
diff --git a/Lib/test/test_capi/test_structmembers.py b/Lib/test/test_capi/test_structmembers.py
index 07d2f62..59ed2fb 100644
--- a/Lib/test/test_capi/test_structmembers.py
+++ b/Lib/test/test_capi/test_structmembers.py
@@ -12,6 +12,14 @@ from _testcapi import _test_structmembersType, \
LLONG_MAX, LLONG_MIN, ULLONG_MAX, \
PY_SSIZE_T_MAX, PY_SSIZE_T_MIN
+
+class Index:
+ def __init__(self, value):
+ self.value = value
+ def __index__(self):
+ return self.value
+
+
ts=_test_structmembersType(False, # T_BOOL
1, # T_BYTE
2, # T_UBYTE
@@ -59,6 +67,10 @@ class ReadWriteTests(unittest.TestCase):
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.T_LONG = LONG_MAX
@@ -67,6 +79,10 @@ class ReadWriteTests(unittest.TestCase):
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.T_PYSSIZET = PY_SSIZE_T_MAX
@@ -140,6 +156,25 @@ class TestWarnings(unittest.TestCase):
with warnings_helper.check_warnings(('', RuntimeWarning)):
ts.T_USHORT = USHRT_MAX+1
+ def test_int(self):
+ 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):
+ 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):
+ with self.assertWarns(RuntimeWarning):
+ ts.T_ULONG = -1
+
if __name__ == "__main__":
unittest.main()
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-01-21-17-29-32.gh-issue-114388.UVGO4K.rst b/Misc/NEWS.d/next/Core and Builtins/2024-01-21-17-29-32.gh-issue-114388.UVGO4K.rst
new file mode 100644
index 0000000..97e2d48
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2024-01-21-17-29-32.gh-issue-114388.UVGO4K.rst
@@ -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 type 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.
diff --git a/Python/structmember.c b/Python/structmember.c
index c7e3188..c790656 100644
--- a/Python/structmember.c
+++ b/Python/structmember.c
@@ -187,45 +187,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)