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
import unittest
import struct
import warnings
warnings.filterwarnings("ignore", "struct integer overflow masking is deprecated",
DeprecationWarning)
from functools import wraps
from test.support import TestFailed, verbose, run_unittest
......@@ -17,11 +15,9 @@ try:
import _struct
except ImportError:
PY_STRUCT_RANGE_CHECKING = 0
PY_STRUCT_OVERFLOW_MASKING = 1
PY_STRUCT_FLOAT_COERCE = 2
else:
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)
def string_reverse(s):
......@@ -51,8 +47,7 @@ def deprecated_err(func, *args):
except (struct.error, OverflowError):
pass
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))
else:
raise TestFailed("%s%s did not raise error" % (
......@@ -471,11 +466,6 @@ class StructTest(unittest.TestCase):
self.check_float_coerce(endian + fmt, 1.0)
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):
test_string = b'abcd01234'
fmt = '4s'
......
......@@ -620,6 +620,7 @@ Ilya Sandler
Ty Sarna
Ben Sayer
Michael Scharf
Andreas Schawo
Neil Schemenauer
David Scherer
Gregor Schmid
......
......@@ -44,6 +44,13 @@ Library
- Issue #5016: FileIO.seekable() could return False if the file position
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
================================
......
......@@ -12,20 +12,6 @@
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
arguments for integer formats with a warning for backwards
compatibility. */
......@@ -237,107 +223,9 @@ get_ulonglong(PyObject *v, unsigned PY_LONG_LONG *p)
#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)
#endif
/* Floating point helpers */
......@@ -392,26 +280,7 @@ _range_error(const formatdef *f, int is_unsigned)
~ 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;
}
......@@ -673,7 +542,7 @@ np_uint(char *p, PyObject *v, const formatdef *f)
{
unsigned long x;
unsigned int y;
if (get_wrapped_ulong(v, &x) < 0)
if (get_ulong(v, &x) < 0)
return -1;
y = (unsigned int)x;
#if (SIZEOF_LONG > SIZEOF_INT)
......@@ -698,7 +567,7 @@ static int
np_ulong(char *p, PyObject *v, const formatdef *f)
{
unsigned long x;
if (get_wrapped_ulong(v, &x) < 0)
if (get_ulong(v, &x) < 0)
return -1;
memcpy(p, (char *)&x, sizeof x);
return 0;
......@@ -905,7 +774,7 @@ bp_int(char *p, PyObject *v, const formatdef *f)
{
long x;
Py_ssize_t i;
if (get_wrapped_long(v, &x) < 0)
if (get_long(v, &x) < 0)
return -1;
i = f->size;
if (i != SIZEOF_LONG) {
......@@ -914,10 +783,6 @@ bp_int(char *p, PyObject *v, const formatdef *f)
#if (SIZEOF_LONG != 4)
else if ((i == 4) && (x < -2147483648L || x > 2147483647L))
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
}
do {
......@@ -932,7 +797,7 @@ bp_uint(char *p, PyObject *v, const formatdef *f)
{
unsigned long x;
Py_ssize_t i;
if (get_wrapped_ulong(v, &x) < 0)
if (get_ulong(v, &x) < 0)
return -1;
i = f->size;
if (i != SIZEOF_LONG) {
......@@ -1015,14 +880,8 @@ bp_bool(char *p, PyObject *v, const formatdef *f)
static formatdef bigendian_table[] = {
{'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_ubyte, np_ubyte},
#endif
{'c', 1, 0, nu_char, np_char},
{'s', 1, 0, NULL},
{'p', 1, 0, NULL},
......@@ -1133,7 +992,7 @@ lp_int(char *p, PyObject *v, const formatdef *f)
{
long x;
Py_ssize_t i;
if (get_wrapped_long(v, &x) < 0)
if (get_long(v, &x) < 0)
return -1;
i = f->size;
if (i != SIZEOF_LONG) {
......@@ -1142,10 +1001,6 @@ lp_int(char *p, PyObject *v, const formatdef *f)
#if (SIZEOF_LONG != 4)
else if ((i == 4) && (x < -2147483648L || x > 2147483647L))
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
}
do {
......@@ -1160,7 +1015,7 @@ lp_uint(char *p, PyObject *v, const formatdef *f)
{
unsigned long x;
Py_ssize_t i;
if (get_wrapped_ulong(v, &x) < 0)
if (get_ulong(v, &x) < 0)
return -1;
i = f->size;
if (i != SIZEOF_LONG) {
......@@ -1234,14 +1089,8 @@ lp_double(char *p, PyObject *v, const formatdef *f)
static formatdef lilendian_table[] = {
{'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_ubyte, np_ubyte},
#endif
{'c', 1, 0, nu_char, np_char},
{'s', 1, 0, NULL},
{'p', 1, 0, NULL},
......@@ -2125,26 +1974,6 @@ PyInit__struct(void)
if (PyType_Ready(&PyStructType) < 0)
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 */
{
int one = 1;
......@@ -2183,7 +2012,6 @@ PyInit__struct(void)
native++;
}
}
#endif
/* Add some symbolic constants to the module */
if (StructError == NULL) {
......@@ -2201,9 +2029,6 @@ PyInit__struct(void)
PyModule_AddObject(m, "__version__", ver);
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
PyModule_AddIntConstant(m, "_PY_STRUCT_FLOAT_COERCE", 1);
#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