- 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.
This commit is contained in:
Mark Dickinson 2009-03-21 10:26:31 +00:00
parent e43b060b05
commit ae681df4d8
4 changed files with 16 additions and 193 deletions

View File

@ -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'

View File

@ -620,6 +620,7 @@ Ilya Sandler
Ty Sarna
Ben Sayer
Michael Scharf
Andreas Schawo
Neil Schemenauer
David Scherer
Gregor Schmid

View File

@ -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
================================

View File

@ -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