Commit ae681df4 authored by Mark Dickinson's avatar Mark Dickinson

- Issue #5463: In struct module, remove deprecated overflow wrapping

  when packing an integer: for example, struct.pack('=L', -1) now
  raises struct.error instead of returning b'\xff\xff\xff\xff'.

  Thanks Andreas Schawo for the patch.
parent e43b060b
...@@ -2,8 +2,6 @@ import array ...@@ -2,8 +2,6 @@ import array
import unittest import unittest
import struct import struct
import warnings import warnings
warnings.filterwarnings("ignore", "struct integer overflow masking is deprecated",
DeprecationWarning)
from functools import wraps from functools import wraps
from test.support import TestFailed, verbose, run_unittest from test.support import TestFailed, verbose, run_unittest
...@@ -17,11 +15,9 @@ try: ...@@ -17,11 +15,9 @@ try:
import _struct import _struct
except ImportError: except ImportError:
PY_STRUCT_RANGE_CHECKING = 0 PY_STRUCT_RANGE_CHECKING = 0
PY_STRUCT_OVERFLOW_MASKING = 1
PY_STRUCT_FLOAT_COERCE = 2 PY_STRUCT_FLOAT_COERCE = 2
else: else:
PY_STRUCT_RANGE_CHECKING = getattr(_struct, '_PY_STRUCT_RANGE_CHECKING', 0) PY_STRUCT_RANGE_CHECKING = getattr(_struct, '_PY_STRUCT_RANGE_CHECKING', 0)
PY_STRUCT_OVERFLOW_MASKING = getattr(_struct, '_PY_STRUCT_OVERFLOW_MASKING', 0)
PY_STRUCT_FLOAT_COERCE = getattr(_struct, '_PY_STRUCT_FLOAT_COERCE', 0) PY_STRUCT_FLOAT_COERCE = getattr(_struct, '_PY_STRUCT_FLOAT_COERCE', 0)
def string_reverse(s): def string_reverse(s):
...@@ -51,8 +47,7 @@ def deprecated_err(func, *args): ...@@ -51,8 +47,7 @@ def deprecated_err(func, *args):
except (struct.error, OverflowError): except (struct.error, OverflowError):
pass pass
except DeprecationWarning: except DeprecationWarning:
if not PY_STRUCT_OVERFLOW_MASKING: raise TestFailed("%s%s expected to raise DeprecationWarning" % (
raise TestFailed("%s%s expected to raise DeprecationWarning" % (
func.__name__, args)) func.__name__, args))
else: else:
raise TestFailed("%s%s did not raise error" % ( raise TestFailed("%s%s did not raise error" % (
...@@ -471,11 +466,6 @@ class StructTest(unittest.TestCase): ...@@ -471,11 +466,6 @@ class StructTest(unittest.TestCase):
self.check_float_coerce(endian + fmt, 1.0) self.check_float_coerce(endian + fmt, 1.0)
self.check_float_coerce(endian + fmt, 1.5) self.check_float_coerce(endian + fmt, 1.5)
def test_issue4228(self):
# Packing a long may yield either 32 or 64 bits
x = struct.pack('L', -1)[:4]
self.assertEqual(x, b'\xff'*4)
def test_unpack_from(self): def test_unpack_from(self):
test_string = b'abcd01234' test_string = b'abcd01234'
fmt = '4s' fmt = '4s'
......
...@@ -620,6 +620,7 @@ Ilya Sandler ...@@ -620,6 +620,7 @@ Ilya Sandler
Ty Sarna Ty Sarna
Ben Sayer Ben Sayer
Michael Scharf Michael Scharf
Andreas Schawo
Neil Schemenauer Neil Schemenauer
David Scherer David Scherer
Gregor Schmid Gregor Schmid
......
...@@ -44,6 +44,13 @@ Library ...@@ -44,6 +44,13 @@ Library
- Issue #5016: FileIO.seekable() could return False if the file position - Issue #5016: FileIO.seekable() could return False if the file position
was negative when truncated to a C int. Patch by Victor Stinner. was negative when truncated to a C int. Patch by Victor Stinner.
Extension Modules
-----------------
- Issue #5463: In struct module, remove deprecated overflow wrapping
when packing an integer: struct.pack('=L', -1) now raises
struct.error instead of returning b'\xff\xff\xff\xff'.
What's New in Python 3.1 alpha 1 What's New in Python 3.1 alpha 1
================================ ================================
......
...@@ -12,20 +12,6 @@ ...@@ -12,20 +12,6 @@
static PyTypeObject PyStructType; static PyTypeObject PyStructType;
/* If PY_STRUCT_OVERFLOW_MASKING is defined, the struct module will wrap all input
numbers for explicit endians such that they fit in the given type, much
like explicit casting in C. A warning will be raised if the number did
not originally fit within the range of the requested type. If it is
not defined, then all range errors and overflow will be struct.error
exceptions. */
#define PY_STRUCT_OVERFLOW_MASKING 1
#ifdef PY_STRUCT_OVERFLOW_MASKING
static PyObject *pylong_ulong_mask = NULL;
static PyObject *pyint_zero = NULL;
#endif
/* If PY_STRUCT_FLOAT_COERCE is defined, the struct module will allow float /* If PY_STRUCT_FLOAT_COERCE is defined, the struct module will allow float
arguments for integer formats with a warning for backwards arguments for integer formats with a warning for backwards
compatibility. */ compatibility. */
...@@ -237,107 +223,9 @@ get_ulonglong(PyObject *v, unsigned PY_LONG_LONG *p) ...@@ -237,107 +223,9 @@ get_ulonglong(PyObject *v, unsigned PY_LONG_LONG *p)
#endif #endif
#ifdef PY_STRUCT_OVERFLOW_MASKING
/* Helper routine to get a Python integer and raise the appropriate error
if it isn't one */
#define INT_OVERFLOW "struct integer overflow masking is deprecated"
static int
get_wrapped_long(PyObject *v, long *p)
{
if (get_long(v, p) < 0) {
if (PyLong_Check(v) &&
PyErr_ExceptionMatches(PyExc_OverflowError)) {
PyObject *wrapped;
long x;
PyErr_Clear();
#ifdef PY_STRUCT_FLOAT_COERCE
if (PyFloat_Check(v)) {
PyObject *o;
int res;
PyErr_Clear();
if (PyErr_WarnEx(PyExc_DeprecationWarning, FLOAT_COERCE, 2) < 0)
return -1;
o = PyNumber_Long(v);
if (o == NULL)
return -1;
res = get_wrapped_long(o, p);
Py_DECREF(o);
return res;
}
#endif
if (PyErr_WarnEx(PyExc_DeprecationWarning, INT_OVERFLOW, 2) < 0)
return -1;
wrapped = PyNumber_And(v, pylong_ulong_mask);
if (wrapped == NULL)
return -1;
x = (long)PyLong_AsUnsignedLong(wrapped);
Py_DECREF(wrapped);
if (x == -1 && PyErr_Occurred())
return -1;
*p = x;
} else {
return -1;
}
}
return 0;
}
static int
get_wrapped_ulong(PyObject *v, unsigned long *p)
{
long x = (long)PyLong_AsUnsignedLong(v);
if (x == -1 && PyErr_Occurred()) {
PyObject *wrapped;
PyErr_Clear();
#ifdef PY_STRUCT_FLOAT_COERCE
if (PyFloat_Check(v)) {
PyObject *o;
int res;
PyErr_Clear();
if (PyErr_WarnEx(PyExc_DeprecationWarning, FLOAT_COERCE, 2) < 0)
return -1;
o = PyNumber_Long(v);
if (o == NULL)
return -1;
res = get_wrapped_ulong(o, p);
Py_DECREF(o);
return res;
}
#endif
wrapped = PyNumber_And(v, pylong_ulong_mask);
if (wrapped == NULL)
return -1;
if (PyErr_WarnEx(PyExc_DeprecationWarning, INT_OVERFLOW, 2) < 0) {
Py_DECREF(wrapped);
return -1;
}
x = (long)PyLong_AsUnsignedLong(wrapped);
Py_DECREF(wrapped);
if (x == -1 && PyErr_Occurred())
return -1;
}
*p = (unsigned long)x;
return 0;
}
#define RANGE_ERROR(x, f, flag, mask) \
do { \
if (_range_error(f, flag) < 0) \
return -1; \
else \
(x) &= (mask); \
} while (0)
#else
#define get_wrapped_long get_long
#define get_wrapped_ulong get_ulong
#define RANGE_ERROR(x, f, flag, mask) return _range_error(f, flag) #define RANGE_ERROR(x, f, flag, mask) return _range_error(f, flag)
#endif
/* Floating point helpers */ /* Floating point helpers */
...@@ -392,26 +280,7 @@ _range_error(const formatdef *f, int is_unsigned) ...@@ -392,26 +280,7 @@ _range_error(const formatdef *f, int is_unsigned)
~ largest, ~ largest,
largest); largest);
} }
#ifdef PY_STRUCT_OVERFLOW_MASKING
{
PyObject *ptype, *pvalue, *ptraceback;
PyObject *msg;
int rval;
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
assert(pvalue != NULL);
msg = PyObject_Str(pvalue);
Py_XDECREF(ptype);
Py_XDECREF(pvalue);
Py_XDECREF(ptraceback);
if (msg == NULL)
return -1;
rval = PyErr_WarnEx(PyExc_DeprecationWarning,
_PyUnicode_AsString(msg), 2);
Py_DECREF(msg);
if (rval == 0)
return 0;
}
#endif
return -1; return -1;
} }
...@@ -673,7 +542,7 @@ np_uint(char *p, PyObject *v, const formatdef *f) ...@@ -673,7 +542,7 @@ np_uint(char *p, PyObject *v, const formatdef *f)
{ {
unsigned long x; unsigned long x;
unsigned int y; unsigned int y;
if (get_wrapped_ulong(v, &x) < 0) if (get_ulong(v, &x) < 0)
return -1; return -1;
y = (unsigned int)x; y = (unsigned int)x;
#if (SIZEOF_LONG > SIZEOF_INT) #if (SIZEOF_LONG > SIZEOF_INT)
...@@ -698,7 +567,7 @@ static int ...@@ -698,7 +567,7 @@ static int
np_ulong(char *p, PyObject *v, const formatdef *f) np_ulong(char *p, PyObject *v, const formatdef *f)
{ {
unsigned long x; unsigned long x;
if (get_wrapped_ulong(v, &x) < 0) if (get_ulong(v, &x) < 0)
return -1; return -1;
memcpy(p, (char *)&x, sizeof x); memcpy(p, (char *)&x, sizeof x);
return 0; return 0;
...@@ -905,7 +774,7 @@ bp_int(char *p, PyObject *v, const formatdef *f) ...@@ -905,7 +774,7 @@ bp_int(char *p, PyObject *v, const formatdef *f)
{ {
long x; long x;
Py_ssize_t i; Py_ssize_t i;
if (get_wrapped_long(v, &x) < 0) if (get_long(v, &x) < 0)
return -1; return -1;
i = f->size; i = f->size;
if (i != SIZEOF_LONG) { if (i != SIZEOF_LONG) {
...@@ -914,10 +783,6 @@ bp_int(char *p, PyObject *v, const formatdef *f) ...@@ -914,10 +783,6 @@ bp_int(char *p, PyObject *v, const formatdef *f)
#if (SIZEOF_LONG != 4) #if (SIZEOF_LONG != 4)
else if ((i == 4) && (x < -2147483648L || x > 2147483647L)) else if ((i == 4) && (x < -2147483648L || x > 2147483647L))
RANGE_ERROR(x, f, 0, 0xffffffffL); RANGE_ERROR(x, f, 0, 0xffffffffL);
#endif
#ifdef PY_STRUCT_OVERFLOW_MASKING
else if ((i == 1) && (x < -128 || x > 127))
RANGE_ERROR(x, f, 0, 0xffL);
#endif #endif
} }
do { do {
...@@ -932,7 +797,7 @@ bp_uint(char *p, PyObject *v, const formatdef *f) ...@@ -932,7 +797,7 @@ bp_uint(char *p, PyObject *v, const formatdef *f)
{ {
unsigned long x; unsigned long x;
Py_ssize_t i; Py_ssize_t i;
if (get_wrapped_ulong(v, &x) < 0) if (get_ulong(v, &x) < 0)
return -1; return -1;
i = f->size; i = f->size;
if (i != SIZEOF_LONG) { if (i != SIZEOF_LONG) {
...@@ -1015,14 +880,8 @@ bp_bool(char *p, PyObject *v, const formatdef *f) ...@@ -1015,14 +880,8 @@ bp_bool(char *p, PyObject *v, const formatdef *f)
static formatdef bigendian_table[] = { static formatdef bigendian_table[] = {
{'x', 1, 0, NULL}, {'x', 1, 0, NULL},
#ifdef PY_STRUCT_OVERFLOW_MASKING
/* Native packers do range checking without overflow masking. */
{'b', 1, 0, nu_byte, bp_int},
{'B', 1, 0, nu_ubyte, bp_uint},
#else
{'b', 1, 0, nu_byte, np_byte}, {'b', 1, 0, nu_byte, np_byte},
{'B', 1, 0, nu_ubyte, np_ubyte}, {'B', 1, 0, nu_ubyte, np_ubyte},
#endif
{'c', 1, 0, nu_char, np_char}, {'c', 1, 0, nu_char, np_char},
{'s', 1, 0, NULL}, {'s', 1, 0, NULL},
{'p', 1, 0, NULL}, {'p', 1, 0, NULL},
...@@ -1133,7 +992,7 @@ lp_int(char *p, PyObject *v, const formatdef *f) ...@@ -1133,7 +992,7 @@ lp_int(char *p, PyObject *v, const formatdef *f)
{ {
long x; long x;
Py_ssize_t i; Py_ssize_t i;
if (get_wrapped_long(v, &x) < 0) if (get_long(v, &x) < 0)
return -1; return -1;
i = f->size; i = f->size;
if (i != SIZEOF_LONG) { if (i != SIZEOF_LONG) {
...@@ -1142,10 +1001,6 @@ lp_int(char *p, PyObject *v, const formatdef *f) ...@@ -1142,10 +1001,6 @@ lp_int(char *p, PyObject *v, const formatdef *f)
#if (SIZEOF_LONG != 4) #if (SIZEOF_LONG != 4)
else if ((i == 4) && (x < -2147483648L || x > 2147483647L)) else if ((i == 4) && (x < -2147483648L || x > 2147483647L))
RANGE_ERROR(x, f, 0, 0xffffffffL); RANGE_ERROR(x, f, 0, 0xffffffffL);
#endif
#ifdef PY_STRUCT_OVERFLOW_MASKING
else if ((i == 1) && (x < -128 || x > 127))
RANGE_ERROR(x, f, 0, 0xffL);
#endif #endif
} }
do { do {
...@@ -1160,7 +1015,7 @@ lp_uint(char *p, PyObject *v, const formatdef *f) ...@@ -1160,7 +1015,7 @@ lp_uint(char *p, PyObject *v, const formatdef *f)
{ {
unsigned long x; unsigned long x;
Py_ssize_t i; Py_ssize_t i;
if (get_wrapped_ulong(v, &x) < 0) if (get_ulong(v, &x) < 0)
return -1; return -1;
i = f->size; i = f->size;
if (i != SIZEOF_LONG) { if (i != SIZEOF_LONG) {
...@@ -1234,14 +1089,8 @@ lp_double(char *p, PyObject *v, const formatdef *f) ...@@ -1234,14 +1089,8 @@ lp_double(char *p, PyObject *v, const formatdef *f)
static formatdef lilendian_table[] = { static formatdef lilendian_table[] = {
{'x', 1, 0, NULL}, {'x', 1, 0, NULL},
#ifdef PY_STRUCT_OVERFLOW_MASKING
/* Native packers do range checking without overflow masking. */
{'b', 1, 0, nu_byte, lp_int},
{'B', 1, 0, nu_ubyte, lp_uint},
#else
{'b', 1, 0, nu_byte, np_byte}, {'b', 1, 0, nu_byte, np_byte},
{'B', 1, 0, nu_ubyte, np_ubyte}, {'B', 1, 0, nu_ubyte, np_ubyte},
#endif
{'c', 1, 0, nu_char, np_char}, {'c', 1, 0, nu_char, np_char},
{'s', 1, 0, NULL}, {'s', 1, 0, NULL},
{'p', 1, 0, NULL}, {'p', 1, 0, NULL},
...@@ -2125,26 +1974,6 @@ PyInit__struct(void) ...@@ -2125,26 +1974,6 @@ PyInit__struct(void)
if (PyType_Ready(&PyStructType) < 0) if (PyType_Ready(&PyStructType) < 0)
return NULL; return NULL;
#ifdef PY_STRUCT_OVERFLOW_MASKING
if (pyint_zero == NULL) {
pyint_zero = PyLong_FromLong(0);
if (pyint_zero == NULL)
return NULL;
}
if (pylong_ulong_mask == NULL) {
#if (SIZEOF_LONG == 4)
pylong_ulong_mask = PyLong_FromString("FFFFFFFF", NULL, 16);
#else
pylong_ulong_mask = PyLong_FromString("FFFFFFFFFFFFFFFF", NULL, 16);
#endif
if (pylong_ulong_mask == NULL)
return NULL;
}
#else
/* This speed trick can't be used until overflow masking goes away, because
native endian always raises exceptions instead of overflow masking. */
/* Check endian and swap in faster functions */ /* Check endian and swap in faster functions */
{ {
int one = 1; int one = 1;
...@@ -2183,7 +2012,6 @@ PyInit__struct(void) ...@@ -2183,7 +2012,6 @@ PyInit__struct(void)
native++; native++;
} }
} }
#endif
/* Add some symbolic constants to the module */ /* Add some symbolic constants to the module */
if (StructError == NULL) { if (StructError == NULL) {
...@@ -2201,9 +2029,6 @@ PyInit__struct(void) ...@@ -2201,9 +2029,6 @@ PyInit__struct(void)
PyModule_AddObject(m, "__version__", ver); PyModule_AddObject(m, "__version__", ver);
PyModule_AddIntConstant(m, "_PY_STRUCT_RANGE_CHECKING", 1); PyModule_AddIntConstant(m, "_PY_STRUCT_RANGE_CHECKING", 1);
#ifdef PY_STRUCT_OVERFLOW_MASKING
PyModule_AddIntConstant(m, "_PY_STRUCT_OVERFLOW_MASKING", 1);
#endif
#ifdef PY_STRUCT_FLOAT_COERCE #ifdef PY_STRUCT_FLOAT_COERCE
PyModule_AddIntConstant(m, "_PY_STRUCT_FLOAT_COERCE", 1); PyModule_AddIntConstant(m, "_PY_STRUCT_FLOAT_COERCE", 1);
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment