[3.11] gh-115011: Improve support of __index__() in setters of members with unsigned integer type (GH-115029) (GH-115295)

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 d9d6909697)
This commit is contained in:
Serhiy Storchaka 2024-02-11 14:03:48 +02:00 committed by GitHub
parent e72255054b
commit 7273a58a85
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 61 additions and 67 deletions

View file

@ -3,6 +3,8 @@
#include "Python.h"
#include "structmember.h" // PyMemberDef
#include "pycore_abstract.h" // _PyNumber_Index()
PyObject *
PyMember_GetOne(const char *obj_addr, PyMemberDef *l)
@ -190,27 +192,22 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
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()) {
v = _PyNumber_Index(v);
if (v == NULL) {
return -1;
}
if (overflow < 0) {
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert to C long");
return -1;
}
else if (!overflow) {
if (Py_SIZE(v) < 0) {
long long_val = PyLong_AsLong(v);
Py_DECREF(v);
if (long_val == -1 && PyErr_Occurred()) {
return -1;
}
*(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");
}
WARN("Writing negative value into unsigned field");
}
else {
unsigned long ulong_val = PyLong_AsUnsignedLong(v);
Py_DECREF(v);
if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
return -1;
}
@ -230,24 +227,22 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
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()) {
v = _PyNumber_Index(v);
if (v == NULL) {
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");
if (Py_SIZE(v) < 0) {
long long_val = PyLong_AsLong(v);
Py_DECREF(v);
if (long_val == -1 && PyErr_Occurred()) {
return -1;
}
*(unsigned long *)addr = (unsigned long)long_val;
WARN("Writing negative value into unsigned field");
}
else {
unsigned long ulong_val = PyLong_AsUnsignedLong(v);
Py_DECREF(v);
if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
return -1;
}
@ -304,18 +299,30 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
return -1;
break;
}
case T_ULONGLONG:{
unsigned long long value;
/* ??? PyLong_AsLongLong accepts an int, but PyLong_AsUnsignedLongLong
doesn't ??? */
if (PyLong_Check(v))
*(unsigned long long*)addr = value = PyLong_AsUnsignedLongLong(v);
else
*(unsigned long long*)addr = value = PyLong_AsLong(v);
if ((value == (unsigned long long)-1) && PyErr_Occurred())
case T_ULONGLONG: {
v = _PyNumber_Index(v);
if (v == NULL) {
return -1;
break;
}
if (Py_SIZE(v) < 0) {
long long_val = PyLong_AsLong(v);
Py_DECREF(v);
if (long_val == -1 && PyErr_Occurred()) {
return -1;
}
*(unsigned long long *)addr = (unsigned long long)(long long)long_val;
WARN("Writing negative value into unsigned field");
}
else {
unsigned long long ulonglong_val = PyLong_AsUnsignedLongLong(v);
Py_DECREF(v);
if (ulonglong_val == (unsigned long long)-1 && PyErr_Occurred()) {
return -1;
}
*(unsigned long long*)addr = ulonglong_val;
}
break;
}
default:
PyErr_Format(PyExc_SystemError,
"bad memberdescr type for %s", l->name);