Skip to content
Closed
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
3 changes: 2 additions & 1 deletion Lib/test/test_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import array
import gc
import math
import re
import operator
import unittest
import struct
Expand Down Expand Up @@ -742,7 +743,7 @@ def test_error_msg(prefix, int_type, is_unsigned):
else:
max_ = 2 ** (size * 8 - 1) - 1
min_ = -2 ** (size * 8 - 1)
error_msg = f"'{int_type}' format requires {min_} <= number <= {max_}"
error_msg = re.escape(f"'{int_type}' format requires {min_} <= number <= {max_} (at item 1)")
for number in [int(-1e50), min_ - 1, max_ + 1, int(1e50)]:
with self.subTest(format_str=fmt_str, number=number):
with self.assertRaisesRegex(struct.error, error_msg):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:func:`struct.pack` raises more informative errors when an argument is out of range.
93 changes: 51 additions & 42 deletions Modules/_struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ typedef struct {
PyObject *PyStructType;
PyObject *unpackiter_type;
PyObject *StructError;
Py_ssize_t current_arg;
} _structmodulestate;

static inline _structmodulestate*
Expand Down Expand Up @@ -290,7 +291,7 @@ get_size_t(_structmodulestate *state, PyObject *v, size_t *p)
}


#define RANGE_ERROR(state, f, flag) return _range_error(state, f, flag)
#define RANGE_ERROR(state, f, flag, loc) return _range_error(state, f, flag, loc)


/* Floating-point helpers */
Expand Down Expand Up @@ -347,7 +348,7 @@ unpack_double(const char *p, /* start of 8-byte string */

/* Helper to format the range error exceptions */
static int
_range_error(_structmodulestate *state, const formatdef *f, int is_unsigned)
_range_error(_structmodulestate *state, const formatdef *f, int is_unsigned, Py_ssize_t loc)
{
/* ulargest is the largest unsigned value with f->size bytes.
* Note that the simpler:
Expand All @@ -361,16 +362,18 @@ _range_error(_structmodulestate *state, const formatdef *f, int is_unsigned)
assert(f->size >= 1 && f->size <= SIZEOF_SIZE_T);
if (is_unsigned)
PyErr_Format(state->StructError,
"'%c' format requires 0 <= number <= %zu",
"'%c' format requires 0 <= number <= %zu (at item %zd)",
f->format,
ulargest);
ulargest,
loc);
else {
const Py_ssize_t largest = (Py_ssize_t)(ulargest >> 1);
PyErr_Format(state->StructError,
"'%c' format requires %zd <= number <= %zd",
"'%c' format requires %zd <= number <= %zd (at item %zd)",
f->format,
~ largest,
largest);
largest,
loc);
}

return -1;
Expand Down Expand Up @@ -563,12 +566,12 @@ np_byte(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
long x;
if (get_long(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
RANGE_ERROR(state, f, 0);
RANGE_ERROR(state, f, 0, state->current_arg);
}
return -1;
}
if (x < -128 || x > 127) {
RANGE_ERROR(state, f, 0);
RANGE_ERROR(state, f, 0, state->current_arg);
}
*p = (char)x;
return 0;
Expand All @@ -580,12 +583,12 @@ np_ubyte(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
long x;
if (get_long(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
RANGE_ERROR(state, f, 1);
RANGE_ERROR(state, f, 1, state->current_arg);
}
return -1;
}
if (x < 0 || x > 255) {
RANGE_ERROR(state, f, 1);
RANGE_ERROR(state, f, 1, state->current_arg);
}
*(unsigned char *)p = (unsigned char)x;
return 0;
Expand All @@ -610,12 +613,12 @@ np_short(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
short y;
if (get_long(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
RANGE_ERROR(state, f, 0);
RANGE_ERROR(state, f, 0, state->current_arg);
}
return -1;
}
if (x < SHRT_MIN || x > SHRT_MAX) {
RANGE_ERROR(state, f, 0);
RANGE_ERROR(state, f, 0, state->current_arg);
}
y = (short)x;
memcpy(p, &y, sizeof y);
Expand All @@ -629,12 +632,12 @@ np_ushort(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
unsigned short y;
if (get_long(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
RANGE_ERROR(state, f, 1);
RANGE_ERROR(state, f, 1, state->current_arg);
}
return -1;
}
if (x < 0 || x > USHRT_MAX) {
RANGE_ERROR(state, f, 1);
RANGE_ERROR(state, f, 1, state->current_arg);
}
y = (unsigned short)x;
memcpy(p, &y, sizeof y);
Expand All @@ -648,13 +651,13 @@ np_int(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
int y;
if (get_long(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
RANGE_ERROR(state, f, 0);
RANGE_ERROR(state, f, 0, state->current_arg);
}
return -1;
}
#if (SIZEOF_LONG > SIZEOF_INT)
if ((x < ((long)INT_MIN)) || (x > ((long)INT_MAX)))
RANGE_ERROR(state, f, 0);
RANGE_ERROR(state, f, 0, state->current_arg);
#endif
y = (int)x;
memcpy(p, &y, sizeof y);
Expand All @@ -668,14 +671,14 @@ np_uint(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
unsigned int y;
if (get_ulong(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
RANGE_ERROR(state, f, 1);
RANGE_ERROR(state, f, 1, state->current_arg);
}
return -1;
}
y = (unsigned int)x;
#if (SIZEOF_LONG > SIZEOF_INT)
if (x > ((unsigned long)UINT_MAX))
RANGE_ERROR(state, f, 1);
RANGE_ERROR(state, f, 1, state->current_arg);
#endif
memcpy(p, &y, sizeof y);
return 0;
Expand All @@ -687,7 +690,7 @@ np_long(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
long x;
if (get_long(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
RANGE_ERROR(state, f, 0);
RANGE_ERROR(state, f, 0, state->current_arg);
}
return -1;
}
Expand All @@ -701,7 +704,7 @@ 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)) {
RANGE_ERROR(state, f, 1);
RANGE_ERROR(state, f, 1, state->current_arg);
}
return -1;
}
Expand All @@ -715,7 +718,7 @@ np_ssize_t(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
Py_ssize_t x;
if (get_ssize_t(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
RANGE_ERROR(state, f, 0);
RANGE_ERROR(state, f, 0, state->current_arg);
}
return -1;
}
Expand All @@ -729,7 +732,7 @@ 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)) {
RANGE_ERROR(state, f, 1);
RANGE_ERROR(state, f, 1, state->current_arg);
}
return -1;
}
Expand All @@ -744,10 +747,11 @@ np_longlong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
if (get_longlong(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
PyErr_Format(state->StructError,
"'%c' format requires %lld <= number <= %lld",
"'%c' format requires %lld <= number <= %lld (at item %zd)",
f->format,
LLONG_MIN,
LLONG_MAX);
LLONG_MAX,
state->current_arg);
}
return -1;
}
Expand All @@ -762,9 +766,10 @@ np_ulonglong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f
if (get_ulonglong(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
PyErr_Format(state->StructError,
"'%c' format requires 0 <= number <= %llu",
"'%c' format requires 0 <= number <= %llu (at item %zd)",
f->format,
ULLONG_MAX);
ULLONG_MAX,
state->current_arg);
}
return -1;
}
Expand Down Expand Up @@ -1065,17 +1070,17 @@ bp_int(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
unsigned char *q = (unsigned char *)p;
if (get_long(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
RANGE_ERROR(state, f, 0);
RANGE_ERROR(state, f, 0, state->current_arg);
}
return -1;
}
i = f->size;
if (i != SIZEOF_LONG) {
if ((i == 2) && (x < -32768 || x > 32767))
RANGE_ERROR(state, f, 0);
RANGE_ERROR(state, f, 0, state->current_arg);
#if (SIZEOF_LONG != 4)
else if ((i == 4) && (x < -2147483648L || x > 2147483647L))
RANGE_ERROR(state, f, 0);
RANGE_ERROR(state, f, 0, state->current_arg);
#endif
}
do {
Expand All @@ -1093,7 +1098,7 @@ bp_uint(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
unsigned char *q = (unsigned char *)p;
if (get_ulong(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
RANGE_ERROR(state, f, 1);
RANGE_ERROR(state, f, 1, state->current_arg);
}
return -1;
}
Expand All @@ -1102,7 +1107,7 @@ bp_uint(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
unsigned long maxint = 1;
maxint <<= (unsigned long)(i * 8);
if (x >= maxint)
RANGE_ERROR(state, f, 1);
RANGE_ERROR(state, f, 1, state->current_arg);
}
do {
q[--i] = (unsigned char)(x & 0xffUL);
Expand All @@ -1127,10 +1132,11 @@ bp_longlong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
Py_DECREF(v);
if (res < 0) {
PyErr_Format(state->StructError,
"'%c' format requires %lld <= number <= %lld",
"'%c' format requires %lld <= number <= %lld (at item %zd)",
f->format,
LLONG_MIN,
LLONG_MAX);
LLONG_MAX,
state->current_arg);
return -1;
}
return res;
Expand All @@ -1152,9 +1158,10 @@ bp_ulonglong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f
Py_DECREF(v);
if (res < 0) {
PyErr_Format(state->StructError,
"'%c' format requires 0 <= number <= %llu",
"'%c' format requires 0 <= number <= %llu (at item %zd)",
f->format,
ULLONG_MAX);
ULLONG_MAX,
state->current_arg);
return -1;
}
return res;
Expand Down Expand Up @@ -1398,17 +1405,17 @@ lp_int(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
unsigned char *q = (unsigned char *)p;
if (get_long(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
RANGE_ERROR(state, f, 0);
RANGE_ERROR(state, f, 0, state->current_arg);
}
return -1;
}
i = f->size;
if (i != SIZEOF_LONG) {
if ((i == 2) && (x < -32768 || x > 32767))
RANGE_ERROR(state, f, 0);
RANGE_ERROR(state, f, 0, state->current_arg);
#if (SIZEOF_LONG != 4)
else if ((i == 4) && (x < -2147483648L || x > 2147483647L))
RANGE_ERROR(state, f, 0);
RANGE_ERROR(state, f, 0, state->current_arg);
#endif
}
do {
Expand All @@ -1426,7 +1433,7 @@ lp_uint(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
unsigned char *q = (unsigned char *)p;
if (get_ulong(state, v, &x) < 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
RANGE_ERROR(state, f, 1);
RANGE_ERROR(state, f, 1, state->current_arg);
}
return -1;
}
Expand All @@ -1435,7 +1442,7 @@ lp_uint(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
unsigned long maxint = 1;
maxint <<= (unsigned long)(i * 8);
if (x >= maxint)
RANGE_ERROR(state, f, 1);
RANGE_ERROR(state, f, 1, state->current_arg);
}
do {
*q++ = (unsigned char)(x & 0xffUL);
Expand All @@ -1460,10 +1467,11 @@ lp_longlong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
Py_DECREF(v);
if (res < 0) {
PyErr_Format(state->StructError,
"'%c' format requires %lld <= number <= %lld",
"'%c' format requires %lld <= number <= %lld (at item %zd)",
f->format,
LLONG_MIN,
LLONG_MAX);
LLONG_MAX,
state->current_arg);
return -1;
}
return res;
Expand Down Expand Up @@ -2194,6 +2202,7 @@ s_pack_internal(PyStructObject *soself, PyObject *const *args, int offset,
Py_ssize_t j = code->repeat;
while (j--) {
PyObject *v = args[i++];
state->current_arg = i;
if (e->format == 's') {
Py_ssize_t n;
int isstring;
Expand Down
Loading