mirror of
https://github.com/python/cpython
synced 2024-09-06 02:48:33 +00:00
gh-115011: Improve support of __index__() in setters of members with unsigned integer type (GH-115029)
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.
This commit is contained in:
parent
d2c4baa41f
commit
d9d6909697
|
@ -81,36 +81,22 @@ def _test_int_range(self, name, minval, maxval, *, hardlimit=None,
|
|||
self._test_warn(name, maxval+1, minval)
|
||||
self._test_warn(name, hardmaxval)
|
||||
|
||||
if indexlimit is None:
|
||||
indexlimit = hardlimit
|
||||
if not indexlimit:
|
||||
if indexlimit is False:
|
||||
self.assertRaises(TypeError, setattr, ts, name, Index(minval))
|
||||
self.assertRaises(TypeError, setattr, ts, name, Index(maxval))
|
||||
else:
|
||||
hardminindexval, hardmaxindexval = indexlimit
|
||||
self._test_write(name, Index(minval), minval)
|
||||
if minval < hardminindexval:
|
||||
self._test_write(name, Index(hardminindexval), hardminindexval)
|
||||
if maxval < hardmaxindexval:
|
||||
self._test_write(name, Index(maxval), maxval)
|
||||
else:
|
||||
self._test_write(name, Index(hardmaxindexval), hardmaxindexval)
|
||||
self._test_overflow(name, Index(hardminindexval-1))
|
||||
if name in ('T_UINT', 'T_ULONG'):
|
||||
self.assertRaises(TypeError, setattr, self.ts, name,
|
||||
Index(hardmaxindexval+1))
|
||||
self.assertRaises(TypeError, setattr, self.ts, name,
|
||||
Index(2**1000))
|
||||
else:
|
||||
self._test_overflow(name, Index(hardmaxindexval+1))
|
||||
self._test_overflow(name, Index(hardminval-1))
|
||||
self._test_overflow(name, Index(hardmaxval+1))
|
||||
self._test_overflow(name, Index(2**1000))
|
||||
self._test_overflow(name, Index(-2**1000))
|
||||
if hardminindexval < minval and name != 'T_ULONGLONG':
|
||||
self._test_warn(name, Index(hardminindexval))
|
||||
self._test_warn(name, Index(minval-1))
|
||||
if maxval < hardmaxindexval:
|
||||
self._test_warn(name, Index(maxval+1))
|
||||
self._test_warn(name, Index(hardmaxindexval))
|
||||
if hardminval < minval:
|
||||
self._test_warn(name, Index(hardminval))
|
||||
self._test_warn(name, Index(minval-1), maxval)
|
||||
if maxval < hardmaxval:
|
||||
self._test_warn(name, Index(maxval+1), minval)
|
||||
self._test_warn(name, Index(hardmaxval))
|
||||
|
||||
def test_bool(self):
|
||||
ts = self.ts
|
||||
|
@ -138,14 +124,12 @@ def test_int(self):
|
|||
self._test_int_range('T_INT', INT_MIN, INT_MAX,
|
||||
hardlimit=(LONG_MIN, LONG_MAX))
|
||||
self._test_int_range('T_UINT', 0, UINT_MAX,
|
||||
hardlimit=(LONG_MIN, ULONG_MAX),
|
||||
indexlimit=(LONG_MIN, LONG_MAX))
|
||||
hardlimit=(LONG_MIN, ULONG_MAX))
|
||||
|
||||
def test_long(self):
|
||||
self._test_int_range('T_LONG', LONG_MIN, LONG_MAX)
|
||||
self._test_int_range('T_ULONG', 0, ULONG_MAX,
|
||||
hardlimit=(LONG_MIN, ULONG_MAX),
|
||||
indexlimit=(LONG_MIN, LONG_MAX))
|
||||
hardlimit=(LONG_MIN, ULONG_MAX))
|
||||
|
||||
def test_py_ssize_t(self):
|
||||
self._test_int_range('T_PYSSIZET', PY_SSIZE_T_MIN, PY_SSIZE_T_MAX, indexlimit=False)
|
||||
|
@ -153,7 +137,7 @@ def test_py_ssize_t(self):
|
|||
def test_longlong(self):
|
||||
self._test_int_range('T_LONGLONG', LLONG_MIN, LLONG_MAX)
|
||||
self._test_int_range('T_ULONGLONG', 0, ULLONG_MAX,
|
||||
indexlimit=(LONG_MIN, LONG_MAX))
|
||||
hardlimit=(LONG_MIN, ULLONG_MAX))
|
||||
|
||||
def test_bad_assignments(self):
|
||||
ts = self.ts
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Setters for members with an unsigned integer type now support the same range
|
||||
of valid values for objects that has a :meth:`~object.__index__` method as
|
||||
for :class:`int`.
|
|
@ -2,6 +2,8 @@
|
|||
/* Map C struct members to Python object attributes */
|
||||
|
||||
#include "Python.h"
|
||||
#include "pycore_abstract.h" // _PyNumber_Index()
|
||||
#include "pycore_long.h" // _PyLong_IsNegative()
|
||||
|
||||
|
||||
PyObject *
|
||||
|
@ -200,27 +202,22 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
|
|||
case Py_T_UINT: {
|
||||
/* XXX: For compatibility, accept negative int values
|
||||
as well. */
|
||||
int overflow;
|
||||
long long_val = PyLong_AsLongAndOverflow(v, &overflow);
|
||||
v = _PyNumber_Index(v);
|
||||
if (v == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (_PyLong_IsNegative((PyLongObject *)v)) {
|
||||
long long_val = PyLong_AsLong(v);
|
||||
Py_DECREF(v);
|
||||
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);
|
||||
Py_DECREF(v);
|
||||
if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -240,24 +237,22 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
|
|||
case Py_T_ULONG: {
|
||||
/* XXX: For compatibility, accept negative int values
|
||||
as well. */
|
||||
int overflow;
|
||||
long long_val = PyLong_AsLongAndOverflow(v, &overflow);
|
||||
v = _PyNumber_Index(v);
|
||||
if (v == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (_PyLong_IsNegative((PyLongObject *)v)) {
|
||||
long long_val = PyLong_AsLong(v);
|
||||
Py_DECREF(v);
|
||||
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);
|
||||
Py_DECREF(v);
|
||||
if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -313,16 +308,28 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
|
|||
return -1;
|
||||
break;
|
||||
}
|
||||
case Py_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 Py_T_ULONGLONG: {
|
||||
v = _PyNumber_Index(v);
|
||||
if (v == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (_PyLong_IsNegative((PyLongObject *)v)) {
|
||||
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:
|
||||
|
|
Loading…
Reference in a new issue