Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
4 changes: 4 additions & 0 deletions Doc/c-api/arg.rst
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@ Numbers
Convert a nonnegative Python integer to an unsigned tiny int, stored in a C
:c:expr:`unsigned char`.

.. versionchanged:: 3.14
A negative Python integer now raises :exc:`ValueError`,
not :exc:`OverflowError`.

``B`` (:class:`int`) [unsigned char]
Convert a Python integer to a tiny int without overflow checking, stored in a C
:c:expr:`unsigned char`.
Expand Down
21 changes: 15 additions & 6 deletions Doc/c-api/long.rst
Original file line number Diff line number Diff line change
Expand Up @@ -257,12 +257,15 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
Return a C :c:expr:`unsigned long` representation of *pylong*. *pylong*
must be an instance of :c:type:`PyLongObject`.

Raise :exc:`OverflowError` if the value of *pylong* is out of range for a
:c:expr:`unsigned long`.
Raise :exc:`ValueError` if the value of *pylong* is negative and
:exc:`OverflowError` if it is out of range for a :c:expr:`unsigned long`.

Returns ``(unsigned long)-1`` on error.
Use :c:func:`PyErr_Occurred` to disambiguate.

.. versionchanged:: 3.14
A negative *pylong* now raises :exc:`ValueError`, not :exc:`OverflowError`.


.. c:function:: size_t PyLong_AsSize_t(PyObject *pylong)

Expand All @@ -273,12 +276,15 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
Return a C :c:type:`size_t` representation of *pylong*. *pylong* must be
an instance of :c:type:`PyLongObject`.

Raise :exc:`OverflowError` if the value of *pylong* is out of range for a
:c:type:`size_t`.
Raise :exc:`ValueError` if the value of *pylong* is negative and
:exc:`OverflowError` if it is out of range for a :c:type:`size_t`.

Returns ``(size_t)-1`` on error.
Use :c:func:`PyErr_Occurred` to disambiguate.

.. versionchanged:: 3.14
A negative *pylong* now raises :exc:`ValueError`, not :exc:`OverflowError`.


.. c:function:: unsigned long long PyLong_AsUnsignedLongLong(PyObject *pylong)

Expand All @@ -288,15 +294,18 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
Return a C :c:expr:`unsigned long long` representation of *pylong*. *pylong*
must be an instance of :c:type:`PyLongObject`.

Raise :exc:`OverflowError` if the value of *pylong* is out of range for an
:c:expr:`unsigned long long`.
Raise :exc:`ValueError` if the value of *pylong* is negative and
:exc:`OverflowError` if it is out of range for a :c:expr:`unsigned long long`.

Returns ``(unsigned long long)-1`` on error.
Use :c:func:`PyErr_Occurred` to disambiguate.

.. versionchanged:: 3.1
A negative *pylong* now raises :exc:`OverflowError`, not :exc:`TypeError`.

.. versionchanged:: 3.14
A negative *pylong* now raises :exc:`ValueError`, not :exc:`OverflowError`.


.. c:function:: unsigned long PyLong_AsUnsignedLongMask(PyObject *obj)

Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,10 @@ Others
integer must implement either :meth:`~object.__int__` or
:meth:`~object.__index__`. (Contributed by Mark Dickinson in :gh:`119743`.)

* Converting negative Python integer to a C unsigned integer type now raises
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will need to be moved to 3.15 and marked as an incompatible change.

:exc:`ValueError`, not :exc:`OverflowError`.
(Contributed by Serhiy Storchaka in :gh:`74020`.)


Porting to Python 3.14
======================
Expand Down
5 changes: 3 additions & 2 deletions Lib/test/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -1326,8 +1326,9 @@ def check_overflow(self, lower, upper):
a = array.array(self.typecode, [lower])
a[0] = lower
# should overflow assigning less than lower limit
self.assertRaises(OverflowError, array.array, self.typecode, [lower-1])
self.assertRaises(OverflowError, a.__setitem__, 0, lower-1)
exc = ValueError if int(lower) == 0 else OverflowError
self.assertRaises(exc, array.array, self.typecode, [lower-1])
self.assertRaises(exc, a.__setitem__, 0, lower-1)
# should not overflow assigning upper limit
a = array.array(self.typecode, [upper])
a[0] = upper
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_capi/test_getargs.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ def test_b(self):
self.assertRaises(TypeError, getargs_b, BadInt2())
self.assertEqual(0, getargs_b(BadInt3()))

self.assertRaises(OverflowError, getargs_b, -1)
self.assertRaises(ValueError, getargs_b, -1)
self.assertEqual(0, getargs_b(0))
self.assertEqual(UCHAR_MAX, getargs_b(UCHAR_MAX))
self.assertRaises(OverflowError, getargs_b, UCHAR_MAX + 1)
Expand Down
6 changes: 3 additions & 3 deletions Lib/test/test_capi/test_long.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ def test_long_asunsignedlong(self):
self.assertRaises(TypeError, asunsignedlong, Index(42))
self.assertRaises(TypeError, asunsignedlong, MyIndexAndInt())

self.assertRaises(OverflowError, asunsignedlong, -1)
self.assertRaises(ValueError, asunsignedlong, -1)
self.assertRaises(OverflowError, asunsignedlong, ULONG_MAX + 1)
self.assertRaises(TypeError, asunsignedlong, 1.0)
self.assertRaises(TypeError, asunsignedlong, b'2')
Expand Down Expand Up @@ -314,7 +314,7 @@ def test_long_asunsignedlonglong(self):
self.assertRaises(TypeError, asunsignedlonglong, Index(42))
self.assertRaises(TypeError, asunsignedlonglong, MyIndexAndInt())

self.assertRaises(OverflowError, asunsignedlonglong, -1)
self.assertRaises(ValueError, asunsignedlonglong, -1)
self.assertRaises(OverflowError, asunsignedlonglong, ULLONG_MAX + 1)
self.assertRaises(TypeError, asunsignedlonglong, 1.0)
self.assertRaises(TypeError, asunsignedlonglong, b'2')
Expand Down Expand Up @@ -374,7 +374,7 @@ def test_long_as_size_t(self):
self.assertRaises(TypeError, as_size_t, Index(42))
self.assertRaises(TypeError, as_size_t, MyIndexAndInt())

self.assertRaises(OverflowError, as_size_t, -1)
self.assertRaises(ValueError, as_size_t, -1)
self.assertRaises(OverflowError, as_size_t, SIZE_MAX + 1)
self.assertRaises(TypeError, as_size_t, 1.0)
self.assertRaises(TypeError, as_size_t, b'2')
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_long.py
Original file line number Diff line number Diff line change
Expand Up @@ -1316,8 +1316,8 @@ def equivalent_python(n, length, byteorder, signed=False):
self.assertRaises(OverflowError, (256).to_bytes, 1, 'big', signed=True)
self.assertRaises(OverflowError, (256).to_bytes, 1, 'little', signed=False)
self.assertRaises(OverflowError, (256).to_bytes, 1, 'little', signed=True)
self.assertRaises(OverflowError, (-1).to_bytes, 2, 'big', signed=False)
self.assertRaises(OverflowError, (-1).to_bytes, 2, 'little', signed=False)
self.assertRaises(ValueError, (-1).to_bytes, 2, 'big', signed=False)
self.assertRaises(ValueError, (-1).to_bytes, 2, 'little', signed=False)
Comment on lines +1380 to +1381
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should adjust int.to_bytes() docs (both docstring and sphinx).

BTW, I think there could be a test with a negative input and signed=True.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated docs.

There is a test with a negative input and signed=True above.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a test with a negative input and signed=True above.

Hmm, maybe. I don't see it.

self.assertEqual((0).to_bytes(0, 'big'), b'')
self.assertEqual((1).to_bytes(5, 'big'), b'\x00\x00\x00\x00\x01')
self.assertEqual((0).to_bytes(5, 'big'), b'\x00\x00\x00\x00\x00')
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_lzma.py
Original file line number Diff line number Diff line change
Expand Up @@ -647,9 +647,9 @@ def test_init_bad_preset(self):
LZMAFile(BytesIO(), "w", preset=10)
with self.assertRaises(LZMAError):
LZMAFile(BytesIO(), "w", preset=23)
with self.assertRaises(OverflowError):
with self.assertRaises(ValueError):
LZMAFile(BytesIO(), "w", preset=-1)
with self.assertRaises(OverflowError):
with self.assertRaises(ValueError):
LZMAFile(BytesIO(), "w", preset=-7)
with self.assertRaises(TypeError):
LZMAFile(BytesIO(), "w", preset="foo")
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -3445,7 +3445,7 @@ def test_waitstatus_to_exitcode_windows(self):
# invalid values
with self.assertRaises(ValueError):
os.waitstatus_to_exitcode((max_exitcode + 1) << 8)
with self.assertRaises(OverflowError):
with self.assertRaises(ValueError):
os.waitstatus_to_exitcode(-1)

# Skip the test on Windows
Expand Down
13 changes: 9 additions & 4 deletions Lib/test/test_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -1121,7 +1121,7 @@ def testInterfaceNameIndex(self):
'socket.if_indextoname() not available.')
def testInvalidInterfaceIndexToName(self):
self.assertRaises(OSError, socket.if_indextoname, 0)
self.assertRaises(OverflowError, socket.if_indextoname, -1)
self.assertRaises(ValueError, socket.if_indextoname, -1)
self.assertRaises(OverflowError, socket.if_indextoname, 2**1000)
self.assertRaises(TypeError, socket.if_indextoname, '_DEADBEEF')
if hasattr(socket, 'if_nameindex'):
Expand Down Expand Up @@ -1182,18 +1182,23 @@ def testNtoHErrors(self):
import _testcapi
s_good_values = [0, 1, 2, 0xffff]
l_good_values = s_good_values + [0xffffffff]
l_bad_values = [-1, -2, 1<<32, 1<<1000]
neg_values = [-1, -2, -(1<<15)-1, -(1<<31)-1, -(1<<63)-1, -1<<1000]
l_bad_values = [1<<32, 1<<1000]
s_bad_values = (
l_bad_values +
[_testcapi.INT_MIN-1, _testcapi.INT_MAX+1] +
[1 << 16, _testcapi.INT_MAX]
[1 << 16, _testcapi.INT_MAX, _testcapi.INT_MAX+1]
)
for k in s_good_values:
socket.ntohs(k)
socket.htons(k)
for k in l_good_values:
socket.ntohl(k)
socket.htonl(k)
for k in neg_values:
self.assertRaises(ValueError, socket.ntohs, k)
self.assertRaises(ValueError, socket.htons, k)
self.assertRaises(ValueError, socket.ntohl, k)
self.assertRaises(ValueError, socket.htonl, k)
for k in s_bad_values:
self.assertRaises(OverflowError, socket.ntohs, k)
self.assertRaises(OverflowError, socket.htons, k)
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -997,7 +997,7 @@ def test_options(self):
self.assertEqual(0, ctx.options & ~ssl.OP_NO_SSLv3)

# invalid options
with self.assertRaises(OverflowError):
with self.assertRaises(ValueError):
ctx.options = -1
with self.assertRaises(OverflowError):
ctx.options = 2 ** 100
Expand Down
3 changes: 2 additions & 1 deletion Lib/test/test_winreg.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,9 @@ def test_setvalueex_negative_one_check(self):
# the value set was not -1.
try:
with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck:
with self.assertRaises(OverflowError):
with self.assertRaises(ValueError):
SetValueEx(ck, "test_name_dword", None, REG_DWORD, -1)
with self.assertRaises(ValueError):
SetValueEx(ck, "test_name_qword", None, REG_QWORD, -1)
self.assertRaises(FileNotFoundError, QueryValueEx, ck, "test_name_dword")
self.assertRaises(FileNotFoundError, QueryValueEx, ck, "test_name_qword")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Converting negative Python integer to a C unsigned integer type now raises
:exc:`ValueError`, not :exc:`OverflowError`.
30 changes: 23 additions & 7 deletions Modules/_struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,9 @@ np_uint(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
unsigned long x;
unsigned int y;
if (get_ulong(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
if (PyErr_ExceptionMatches(PyExc_OverflowError) ||
PyErr_ExceptionMatches(PyExc_ValueError))
{
RANGE_ERROR(state, f, 1);
}
return -1;
Expand Down Expand Up @@ -669,7 +671,9 @@ np_ulong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
{
unsigned long x;
if (get_ulong(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
if (PyErr_ExceptionMatches(PyExc_OverflowError) ||
PyErr_ExceptionMatches(PyExc_ValueError))
{
RANGE_ERROR(state, f, 1);
}
return -1;
Expand Down Expand Up @@ -697,7 +701,9 @@ np_size_t(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
{
size_t x;
if (get_size_t(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
if (PyErr_ExceptionMatches(PyExc_OverflowError) ||
PyErr_ExceptionMatches(PyExc_ValueError))
{
RANGE_ERROR(state, f, 1);
}
return -1;
Expand Down Expand Up @@ -729,7 +735,9 @@ np_ulonglong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f
{
unsigned long long x;
if (get_ulonglong(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
if (PyErr_ExceptionMatches(PyExc_OverflowError) ||
PyErr_ExceptionMatches(PyExc_ValueError))
{
PyErr_Format(state->StructError,
"'%c' format requires 0 <= number <= %llu",
f->format,
Expand Down Expand Up @@ -970,7 +978,9 @@ bp_uint(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
Py_ssize_t i;
unsigned char *q = (unsigned char *)p;
if (get_ulong(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
if (PyErr_ExceptionMatches(PyExc_OverflowError) ||
PyErr_ExceptionMatches(PyExc_ValueError))
{
RANGE_ERROR(state, f, 1);
}
return -1;
Expand Down Expand Up @@ -1232,7 +1242,9 @@ lp_uint(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
Py_ssize_t i;
unsigned char *q = (unsigned char *)p;
if (get_ulong(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
if (PyErr_ExceptionMatches(PyExc_OverflowError) ||
PyErr_ExceptionMatches(PyExc_ValueError))
{
RANGE_ERROR(state, f, 1);
}
return -1;
Expand Down Expand Up @@ -1991,9 +2003,13 @@ s_pack_internal(PyStructObject *soself, PyObject *const *args, int offset,
*res = Py_SAFE_DOWNCAST(n, Py_ssize_t, unsigned char);
} else {
if (e->pack(state, res, v, e) < 0) {
if (PyLong_Check(v) && PyErr_ExceptionMatches(PyExc_OverflowError))
if (PyLong_Check(v) &&
(PyErr_ExceptionMatches(PyExc_OverflowError) ||
PyErr_ExceptionMatches(PyExc_ValueError)))
{
PyErr_SetString(state->StructError,
"int too large to convert");
}
return -1;
}
}
Expand Down
4 changes: 2 additions & 2 deletions Modules/_testlimitedcapi/testcapi_long.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ TESTNAME(PyObject *error(const char*))
if (uout != (unsigned TYPENAME)-1 || !PyErr_Occurred())
return error(
"PyLong_AsUnsignedXXX(-1) didn't complain");
if (!PyErr_ExceptionMatches(PyExc_OverflowError))
if (!PyErr_ExceptionMatches(PyExc_ValueError))
return error(
"PyLong_AsUnsignedXXX(-1) raised "
"something other than OverflowError");
"something other than ValueError");
PyErr_Clear();
UNBIND(x);

Expand Down
43 changes: 25 additions & 18 deletions Modules/arraymodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,20 +211,23 @@ b_getitem(arrayobject *ap, Py_ssize_t i)
static int
b_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
{
short x;
/* PyArg_Parse's 'b' formatter is for an unsigned char, therefore
must use the next size up that is signed ('h') and manually do
the overflow checking */
if (!PyArg_Parse(v, "h;array item must be integer", &x))
int overflow;
long x = PyLong_AsLongAndOverflow(v, &overflow);
if (x == -1 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_SetString(PyExc_TypeError,
"array item must be integer");
}
return -1;
else if (x < -128) {
}
if (overflow > 0 || x > 127) {
PyErr_SetString(PyExc_OverflowError,
"signed char is less than minimum");
"signed char is greater than maximum");
return -1;
}
else if (x > 127) {
if (overflow < 0 || x < -128) {
PyErr_SetString(PyExc_OverflowError,
"signed char is greater than maximum");
"signed char is less than minimum");
return -1;
}
if (i >= 0)
Expand Down Expand Up @@ -352,21 +355,25 @@ HH_getitem(arrayobject *ap, Py_ssize_t i)
static int
HH_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
{
int x;
/* PyArg_Parse's 'h' formatter is for a signed short, therefore
must use the next size up and manually do the overflow checking */
if (!PyArg_Parse(v, "i;array item must be integer", &x))
return -1;
else if (x < 0) {
PyErr_SetString(PyExc_OverflowError,
"unsigned short is less than minimum");
int overflow;
long x = PyLong_AsLongAndOverflow(v, &overflow);
if (x == -1 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_SetString(PyExc_TypeError,
"array item must be integer");
}
return -1;
}
else if (x > USHRT_MAX) {
if (overflow > 0 || x > USHRT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"unsigned short is greater than maximum");
return -1;
}
if (overflow < 0 || x < 0) {
PyErr_SetString(PyExc_ValueError,
"unsigned short is less than minimum");
return -1;
}
if (i >= 0)
((short *)ap->ob_item)[i] = (short)x;
return 0;
Expand Down
Loading