[Python-checkins] gh-98248: Normalizing the error messages in function struct.pack (GH-98252)

mdickinson webhook-mailer at python.org
Sun Dec 4 15:24:24 EST 2022


https://github.com/python/cpython/commit/854a878e4f09cd961ba5135567f7a5b5f86d7be9
commit: 854a878e4f09cd961ba5135567f7a5b5f86d7be9
branch: main
author: Felix Ye <29754475+yanjs at users.noreply.github.com>
committer: mdickinson <dickinsm at gmail.com>
date: 2022-12-04T20:24:18Z
summary:

gh-98248: Normalizing the error messages in function struct.pack (GH-98252)

Provide consistent and informative error messages in function struct.pack when its integral arguments are not in range.

files:
A Misc/NEWS.d/next/Library/2022-10-13-22-13-54.gh-issue-98248.lwyygy.rst
M Lib/test/test_struct.py
M Modules/_struct.c

diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py
index b0f11af1a789..6b1f22f66fd1 100644
--- a/Lib/test/test_struct.py
+++ b/Lib/test/test_struct.py
@@ -723,23 +723,56 @@ def test_issue35714(self):
                 struct.calcsize(s)
 
     @support.cpython_only
-    def test_issue45034_unsigned(self):
-        _testcapi = import_helper.import_module('_testcapi')
-        error_msg = f'ushort format requires 0 <= number <= {_testcapi.USHRT_MAX}'
-        with self.assertRaisesRegex(struct.error, error_msg):
-            struct.pack('H', 70000)  # too large
-        with self.assertRaisesRegex(struct.error, error_msg):
-            struct.pack('H', -1)  # too small
+    def test_issue98248(self):
+        def test_error_msg(prefix, int_type, is_unsigned):
+            fmt_str = prefix + int_type
+            size = struct.calcsize(fmt_str)
+            if is_unsigned:
+                max_ = 2 ** (size * 8) - 1
+                min_ = 0
+            else:
+                max_ = 2 ** (size * 8 - 1) - 1
+                min_ = -2 ** (size * 8 - 1)
+            error_msg = f"'{int_type}' format requires {min_} <= number <= {max_}"
+            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):
+                        struct.pack(fmt_str, number)
+            error_msg = "required argument is not an integer"
+            not_number = ""
+            with self.subTest(format_str=fmt_str, number=not_number):
+                with self.assertRaisesRegex(struct.error, error_msg):
+                    struct.pack(fmt_str, not_number)
+
+        for prefix in '@=<>':
+            for int_type in 'BHILQ':
+                test_error_msg(prefix, int_type, True)
+            for int_type in 'bhilq':
+                test_error_msg(prefix, int_type, False)
+
+        int_type = 'N'
+        test_error_msg('@', int_type, True)
+
+        int_type = 'n'
+        test_error_msg('@', int_type, False)
 
     @support.cpython_only
-    def test_issue45034_signed(self):
-        _testcapi = import_helper.import_module('_testcapi')
-        error_msg = f'short format requires {_testcapi.SHRT_MIN} <= number <= {_testcapi.SHRT_MAX}'
-        with self.assertRaisesRegex(struct.error, error_msg):
-            struct.pack('h', 70000)  # too large
-        with self.assertRaisesRegex(struct.error, error_msg):
-            struct.pack('h', -70000)  # too small
-
+    def test_issue98248_error_propagation(self):
+        class Div0:
+            def __index__(self):
+                1 / 0
+
+        def test_error_propagation(fmt_str):
+            with self.subTest(format_str=fmt_str, exception="ZeroDivisionError"):
+                with self.assertRaises(ZeroDivisionError):
+                    struct.pack(fmt_str, Div0())
+
+        for prefix in '@=<>':
+            for int_type in 'BHILQbhilq':
+                test_error_propagation(prefix + int_type)
+
+        test_error_propagation('N')
+        test_error_propagation('n')
 
 class UnpackIteratorTest(unittest.TestCase):
     """
diff --git a/Misc/NEWS.d/next/Library/2022-10-13-22-13-54.gh-issue-98248.lwyygy.rst b/Misc/NEWS.d/next/Library/2022-10-13-22-13-54.gh-issue-98248.lwyygy.rst
new file mode 100644
index 000000000000..347f6e160335
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-10-13-22-13-54.gh-issue-98248.lwyygy.rst
@@ -0,0 +1 @@
+Provide informative error messages in :func:`struct.pack` when its integral arguments are not in range.
diff --git a/Modules/_struct.c b/Modules/_struct.c
index 0cf34fbf9a3a..3db7b991acd0 100644
--- a/Modules/_struct.c
+++ b/Modules/_struct.c
@@ -167,9 +167,6 @@ get_long(_structmodulestate *state, PyObject *v, long *p)
     x = PyLong_AsLong(v);
     Py_DECREF(v);
     if (x == (long)-1 && PyErr_Occurred()) {
-        if (PyErr_ExceptionMatches(PyExc_OverflowError))
-            PyErr_SetString(state->StructError,
-                            "argument out of range");
         return -1;
     }
     *p = x;
@@ -191,9 +188,6 @@ get_ulong(_structmodulestate *state, PyObject *v, unsigned long *p)
     x = PyLong_AsUnsignedLong(v);
     Py_DECREF(v);
     if (x == (unsigned long)-1 && PyErr_Occurred()) {
-        if (PyErr_ExceptionMatches(PyExc_OverflowError))
-            PyErr_SetString(state->StructError,
-                            "argument out of range");
         return -1;
     }
     *p = x;
@@ -214,9 +208,6 @@ get_longlong(_structmodulestate *state, PyObject *v, long long *p)
     x = PyLong_AsLongLong(v);
     Py_DECREF(v);
     if (x == (long long)-1 && PyErr_Occurred()) {
-        if (PyErr_ExceptionMatches(PyExc_OverflowError))
-            PyErr_SetString(state->StructError,
-                            "argument out of range");
         return -1;
     }
     *p = x;
@@ -237,9 +228,6 @@ get_ulonglong(_structmodulestate *state, PyObject *v, unsigned long long *p)
     x = PyLong_AsUnsignedLongLong(v);
     Py_DECREF(v);
     if (x == (unsigned long long)-1 && PyErr_Occurred()) {
-        if (PyErr_ExceptionMatches(PyExc_OverflowError))
-            PyErr_SetString(state->StructError,
-                            "argument out of range");
         return -1;
     }
     *p = x;
@@ -260,9 +248,6 @@ get_ssize_t(_structmodulestate *state, PyObject *v, Py_ssize_t *p)
     x = PyLong_AsSsize_t(v);
     Py_DECREF(v);
     if (x == (Py_ssize_t)-1 && PyErr_Occurred()) {
-        if (PyErr_ExceptionMatches(PyExc_OverflowError))
-            PyErr_SetString(state->StructError,
-                            "argument out of range");
         return -1;
     }
     *p = x;
@@ -283,9 +268,6 @@ get_size_t(_structmodulestate *state, PyObject *v, size_t *p)
     x = PyLong_AsSize_t(v);
     Py_DECREF(v);
     if (x == (size_t)-1 && PyErr_Occurred()) {
-        if (PyErr_ExceptionMatches(PyExc_OverflowError))
-            PyErr_SetString(state->StructError,
-                            "argument out of range");
         return -1;
     }
     *p = x;
@@ -293,7 +275,7 @@ get_size_t(_structmodulestate *state, PyObject *v, size_t *p)
 }
 
 
-#define RANGE_ERROR(state, x, f, flag, mask) return _range_error(state, f, flag)
+#define RANGE_ERROR(state, f, flag) return _range_error(state, f, flag)
 
 
 /* Floating point helpers */
@@ -545,12 +527,14 @@ static int
 np_byte(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
 {
     long x;
-    if (get_long(state, v, &x) < 0)
+    if (get_long(state, v, &x) < 0) {
+        if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+            RANGE_ERROR(state, f, 0);
+        }
         return -1;
+    }
     if (x < -128 || x > 127) {
-        PyErr_SetString(state->StructError,
-                        "byte format requires -128 <= number <= 127");
-        return -1;
+        RANGE_ERROR(state, f, 0);
     }
     *p = (char)x;
     return 0;
@@ -560,12 +544,14 @@ static int
 np_ubyte(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
 {
     long x;
-    if (get_long(state, v, &x) < 0)
+    if (get_long(state, v, &x) < 0) {
+        if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+            RANGE_ERROR(state, f, 1);
+        }
         return -1;
+    }
     if (x < 0 || x > 255) {
-        PyErr_SetString(state->StructError,
-                        "ubyte format requires 0 <= number <= 255");
-        return -1;
+        RANGE_ERROR(state, f, 1);
     }
     *(unsigned char *)p = (unsigned char)x;
     return 0;
@@ -588,13 +574,14 @@ np_short(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
 {
     long x;
     short y;
-    if (get_long(state, v, &x) < 0)
+    if (get_long(state, v, &x) < 0) {
+        if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+            RANGE_ERROR(state, f, 0);
+        }
         return -1;
+    }
     if (x < SHRT_MIN || x > SHRT_MAX) {
-        PyErr_Format(state->StructError,
-                     "short format requires %d <= number <= %d",
-                     (int)SHRT_MIN, (int)SHRT_MAX);
-        return -1;
+        RANGE_ERROR(state, f, 0);
     }
     y = (short)x;
     memcpy(p, (char *)&y, sizeof y);
@@ -606,13 +593,14 @@ np_ushort(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
 {
     long x;
     unsigned short y;
-    if (get_long(state, v, &x) < 0)
+    if (get_long(state, v, &x) < 0) {
+        if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+            RANGE_ERROR(state, f, 1);
+        }
         return -1;
+    }
     if (x < 0 || x > USHRT_MAX) {
-        PyErr_Format(state->StructError,
-                     "ushort format requires 0 <= number <= %u",
-                     (unsigned int)USHRT_MAX);
-        return -1;
+        RANGE_ERROR(state, f, 1);
     }
     y = (unsigned short)x;
     memcpy(p, (char *)&y, sizeof y);
@@ -624,11 +612,15 @@ np_int(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
 {
     long x;
     int y;
-    if (get_long(state, v, &x) < 0)
+    if (get_long(state, v, &x) < 0) {
+        if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+            RANGE_ERROR(state, f, 0);
+        }
         return -1;
+    }
 #if (SIZEOF_LONG > SIZEOF_INT)
     if ((x < ((long)INT_MIN)) || (x > ((long)INT_MAX)))
-        RANGE_ERROR(state, x, f, 0, -1);
+        RANGE_ERROR(state, f, 0);
 #endif
     y = (int)x;
     memcpy(p, (char *)&y, sizeof y);
@@ -640,12 +632,16 @@ 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 (get_ulong(state, v, &x) < 0) {
+        if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+            RANGE_ERROR(state, f, 1);
+        }
         return -1;
+    }
     y = (unsigned int)x;
 #if (SIZEOF_LONG > SIZEOF_INT)
     if (x > ((unsigned long)UINT_MAX))
-        RANGE_ERROR(state, y, f, 1, -1);
+        RANGE_ERROR(state, f, 1);
 #endif
     memcpy(p, (char *)&y, sizeof y);
     return 0;
@@ -655,8 +651,12 @@ static int
 np_long(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
 {
     long x;
-    if (get_long(state, v, &x) < 0)
+    if (get_long(state, v, &x) < 0) {
+        if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+            RANGE_ERROR(state, f, 0);
+        }
         return -1;
+    }
     memcpy(p, (char *)&x, sizeof x);
     return 0;
 }
@@ -665,8 +665,12 @@ static int
 np_ulong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
 {
     unsigned long x;
-    if (get_ulong(state, v, &x) < 0)
+    if (get_ulong(state, v, &x) < 0) {
+        if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+            RANGE_ERROR(state, f, 1);
+        }
         return -1;
+    }
     memcpy(p, (char *)&x, sizeof x);
     return 0;
 }
@@ -675,8 +679,12 @@ static int
 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 (get_ssize_t(state, v, &x) < 0) {
+        if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+            RANGE_ERROR(state, f, 0);
+        }
         return -1;
+    }
     memcpy(p, (char *)&x, sizeof x);
     return 0;
 }
@@ -685,8 +693,12 @@ static int
 np_size_t(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
 {
     size_t x;
-    if (get_size_t(state, v, &x) < 0)
+    if (get_size_t(state, v, &x) < 0) {
+        if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+            RANGE_ERROR(state, f, 1);
+        }
         return -1;
+    }
     memcpy(p, (char *)&x, sizeof x);
     return 0;
 }
@@ -695,8 +707,16 @@ static int
 np_longlong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
 {
     long long x;
-    if (get_longlong(state, v, &x) < 0)
+    if (get_longlong(state, v, &x) < 0) {
+        if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+            PyErr_Format(state->StructError,
+                         "'%c' format requires %lld <= number <= %lld",
+                         f->format,
+                         LLONG_MIN,
+                         LLONG_MAX);
+        }
         return -1;
+    }
     memcpy(p, (char *)&x, sizeof x);
     return 0;
 }
@@ -705,8 +725,15 @@ static int
 np_ulonglong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
 {
     unsigned long long x;
-    if (get_ulonglong(state, v, &x) < 0)
+    if (get_ulonglong(state, v, &x) < 0) {
+        if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+            PyErr_Format(state->StructError,
+                         "'%c' format requires 0 <= number <= %llu",
+                         f->format,
+                         ULLONG_MAX);
+        }
         return -1;
+    }
     memcpy(p, (char *)&x, sizeof x);
     return 0;
 }
@@ -911,15 +938,19 @@ bp_int(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
     long x;
     Py_ssize_t i;
     unsigned char *q = (unsigned char *)p;
-    if (get_long(state, v, &x) < 0)
+    if (get_long(state, v, &x) < 0) {
+        if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+            RANGE_ERROR(state, f, 0);
+        }
         return -1;
+    }
     i = f->size;
     if (i != SIZEOF_LONG) {
         if ((i == 2) && (x < -32768 || x > 32767))
-            RANGE_ERROR(state, x, f, 0, 0xffffL);
+            RANGE_ERROR(state, f, 0);
 #if (SIZEOF_LONG != 4)
         else if ((i == 4) && (x < -2147483648L || x > 2147483647L))
-            RANGE_ERROR(state, x, f, 0, 0xffffffffL);
+            RANGE_ERROR(state, f, 0);
 #endif
     }
     do {
@@ -935,14 +966,18 @@ bp_uint(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
     unsigned long x;
     Py_ssize_t i;
     unsigned char *q = (unsigned char *)p;
-    if (get_ulong(state, v, &x) < 0)
+    if (get_ulong(state, v, &x) < 0) {
+        if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+            RANGE_ERROR(state, f, 1);
+        }
         return -1;
+    }
     i = f->size;
     if (i != SIZEOF_LONG) {
         unsigned long maxint = 1;
         maxint <<= (unsigned long)(i * 8);
         if (x >= maxint)
-            RANGE_ERROR(state, x, f, 1, maxint - 1);
+            RANGE_ERROR(state, f, 1);
     }
     do {
         q[--i] = (unsigned char)(x & 0xffUL);
@@ -964,6 +999,14 @@ bp_longlong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
                               0, /* little_endian */
                               1  /* signed */);
     Py_DECREF(v);
+    if (res == -1 && PyErr_Occurred()) {
+        PyErr_Format(state->StructError,
+                     "'%c' format requires %lld <= number <= %lld",
+                     f->format,
+                     LLONG_MIN,
+                     LLONG_MAX);
+        return -1;
+    }
     return res;
 }
 
@@ -980,6 +1023,13 @@ bp_ulonglong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f
                               0, /* little_endian */
                               0  /* signed */);
     Py_DECREF(v);
+    if (res == -1 && PyErr_Occurred()) {
+        PyErr_Format(state->StructError,
+                     "'%c' format requires 0 <= number <= %llu",
+                     f->format,
+                     ULLONG_MAX);
+        return -1;
+    }
     return res;
 }
 
@@ -1148,15 +1198,19 @@ lp_int(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
     long x;
     Py_ssize_t i;
     unsigned char *q = (unsigned char *)p;
-    if (get_long(state, v, &x) < 0)
+    if (get_long(state, v, &x) < 0) {
+        if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+            RANGE_ERROR(state, f, 0);
+        }
         return -1;
+    }
     i = f->size;
     if (i != SIZEOF_LONG) {
         if ((i == 2) && (x < -32768 || x > 32767))
-            RANGE_ERROR(state, x, f, 0, 0xffffL);
+            RANGE_ERROR(state, f, 0);
 #if (SIZEOF_LONG != 4)
         else if ((i == 4) && (x < -2147483648L || x > 2147483647L))
-            RANGE_ERROR(state, x, f, 0, 0xffffffffL);
+            RANGE_ERROR(state, f, 0);
 #endif
     }
     do {
@@ -1172,14 +1226,18 @@ lp_uint(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
     unsigned long x;
     Py_ssize_t i;
     unsigned char *q = (unsigned char *)p;
-    if (get_ulong(state, v, &x) < 0)
+    if (get_ulong(state, v, &x) < 0) {
+        if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+            RANGE_ERROR(state, f, 1);
+        }
         return -1;
+    }
     i = f->size;
     if (i != SIZEOF_LONG) {
         unsigned long maxint = 1;
         maxint <<= (unsigned long)(i * 8);
         if (x >= maxint)
-            RANGE_ERROR(state, x, f, 1, maxint - 1);
+            RANGE_ERROR(state, f, 1);
     }
     do {
         *q++ = (unsigned char)(x & 0xffUL);
@@ -1201,6 +1259,14 @@ lp_longlong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
                               1, /* little_endian */
                               1  /* signed */);
     Py_DECREF(v);
+    if (res == -1 && PyErr_Occurred()) {
+        PyErr_Format(state->StructError,
+                     "'%c' format requires %lld <= number <= %lld",
+                     f->format,
+                     LLONG_MIN,
+                     LLONG_MAX);
+        return -1;
+    }
     return res;
 }
 
@@ -1217,6 +1283,13 @@ lp_ulonglong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f
                               1, /* little_endian */
                               0  /* signed */);
     Py_DECREF(v);
+    if (res == -1 && PyErr_Occurred()) {
+        PyErr_Format(state->StructError,
+                     "'%c' format requires 0 <= number <= %llu",
+                     f->format,
+                     ULLONG_MAX);
+        return -1;
+    }
     return res;
 }
 



More information about the Python-checkins mailing list