Commit 7635eccf authored by Mark Dickinson's avatar Mark Dickinson

Issue #5463: Remove deprecated float coercion from struct module, along

with the _PY_STRUCT_FLOAT_COERCE constant.  Simplify tests accordingly,
and reenable (now-fixed) broken tests.
parent 5b67661e
...@@ -11,13 +11,6 @@ ISBIGENDIAN = sys.byteorder == "big" ...@@ -11,13 +11,6 @@ ISBIGENDIAN = sys.byteorder == "big"
IS32BIT = sys.maxsize == 0x7fffffff IS32BIT = sys.maxsize == 0x7fffffff
del sys del sys
try:
import _struct
except ImportError:
PY_STRUCT_FLOAT_COERCE = 2
else:
PY_STRUCT_FLOAT_COERCE = getattr(_struct, '_PY_STRUCT_FLOAT_COERCE', 0)
def string_reverse(s): def string_reverse(s):
return s[::-1] return s[::-1]
...@@ -27,40 +20,7 @@ def bigendian_to_native(value): ...@@ -27,40 +20,7 @@ def bigendian_to_native(value):
else: else:
return string_reverse(value) return string_reverse(value)
def with_warning_restore(func):
@wraps(func)
def decorator(*args, **kw):
with warnings.catch_warnings():
# We need this function to warn every time, so stick an
# unqualifed 'always' at the head of the filter list
warnings.simplefilter("always")
warnings.filterwarnings("error", category=DeprecationWarning)
return func(*args, **kw)
return decorator
class StructTest(unittest.TestCase): class StructTest(unittest.TestCase):
@with_warning_restore
def check_float_coerce(self, format, number):
# SF bug 1530559. struct.pack raises TypeError where it used to convert.
if PY_STRUCT_FLOAT_COERCE == 2:
# Test for pre-2.5 struct module
packed = struct.pack(format, number)
floored = struct.unpack(format, packed)[0]
self.assertEqual(floored, int(number),
"did not correcly coerce float to int")
return
try:
struct.pack(format, number)
except (struct.error, TypeError):
if PY_STRUCT_FLOAT_COERCE:
self.fail("expected DeprecationWarning for float coerce")
except DeprecationWarning:
if not PY_STRUCT_FLOAT_COERCE:
self.fail("expected to raise struct.error for float coerce")
else:
self.fail("did not raise error for float coerce")
def test_isbigendian(self): def test_isbigendian(self):
self.assertEqual((struct.pack('=i', 1)[0] == 0), ISBIGENDIAN) self.assertEqual((struct.pack('=i', 1)[0] == 0), ISBIGENDIAN)
...@@ -270,10 +230,8 @@ class StructTest(unittest.TestCase): ...@@ -270,10 +230,8 @@ class StructTest(unittest.TestCase):
else: else:
# x is out of range -- verify pack realizes that. # x is out of range -- verify pack realizes that.
self.assertRaises((struct.error, OverflowError), self.assertRaises(struct.error, pack, ">" + code, x)
pack, ">" + code, x) self.assertRaises(struct.error, pack, "<" + code, x)
self.assertRaises((struct.error, OverflowError),
pack, "<" + code, x)
# Much the same for unsigned. # Much the same for unsigned.
code = self.unsigned_code code = self.unsigned_code
...@@ -317,10 +275,8 @@ class StructTest(unittest.TestCase): ...@@ -317,10 +275,8 @@ class StructTest(unittest.TestCase):
else: else:
# x is out of range -- verify pack realizes that. # x is out of range -- verify pack realizes that.
self.assertRaises((struct.error, OverflowError), self.assertRaises(struct.error, pack, ">" + code, x)
pack, ">" + code, x) self.assertRaises(struct.error, pack, "<" + code, x)
self.assertRaises((struct.error, OverflowError),
pack, "<" + code, x)
def run(self): def run(self):
from random import randrange from random import randrange
...@@ -353,8 +309,8 @@ class StructTest(unittest.TestCase): ...@@ -353,8 +309,8 @@ class StructTest(unittest.TestCase):
# Some error cases. # Some error cases.
for direction in "<>": for direction in "<>":
for code in self.formatpair: for code in self.formatpair:
for badobject in "a string", 3+42j, randrange: for badobject in "a string", 3+42j, randrange, -1729.0:
self.assertRaises((struct.error, TypeError), self.assertRaises(struct.error,
struct.pack, direction + code, struct.pack, direction + code,
badobject) badobject)
...@@ -437,13 +393,14 @@ class StructTest(unittest.TestCase): ...@@ -437,13 +393,14 @@ class StructTest(unittest.TestCase):
self.assertRaises((struct.error, OverflowError), struct.pack, self.assertRaises((struct.error, OverflowError), struct.pack,
endian + 'L', sys.maxsize * 4) endian + 'L', sys.maxsize * 4)
def XXXtest_1530559(self): def test_1530559(self):
# XXX This is broken: see the bug report
# SF bug 1530559. struct.pack raises TypeError where it used to convert.
for endian in ('', '>', '<'): for endian in ('', '>', '<'):
for fmt in ('B', 'H', 'I', 'L', 'b', 'h', 'i', 'l'): for fmt in ('B', 'H', 'I', 'L', 'Q', 'b', 'h', 'i', 'l', 'q'):
self.check_float_coerce(endian + fmt, 1.0) self.assertRaises(struct.error, struct.pack, endian + fmt, 1.0)
self.check_float_coerce(endian + fmt, 1.5) self.assertRaises(struct.error, struct.pack, endian + fmt, 1.5)
self.assertRaises(struct.error, struct.pack, 'P', 1.0)
self.assertRaises(struct.error, struct.pack, 'P', 1.5)
def test_unpack_from(self): def test_unpack_from(self):
test_string = b'abcd01234' test_string = b'abcd01234'
......
...@@ -87,6 +87,11 @@ Library ...@@ -87,6 +87,11 @@ Library
Extension Modules Extension Modules
----------------- -----------------
- Issue #5463: In struct module, remove deprecated float coercion
for integer type codes: struct.pack('L', 0.3) should now raise
an error. The _PY_STRUCT_FLOAT_COERCE constant has been removed.
The version number has been bumped to 0.3.
- Issue #5359: Readd the Berkley-DB detection code to allow _dbm be built - Issue #5359: Readd the Berkley-DB detection code to allow _dbm be built
using Berkley-DB. using Berkley-DB.
......
...@@ -12,17 +12,6 @@ ...@@ -12,17 +12,6 @@
static PyTypeObject PyStructType; static PyTypeObject PyStructType;
/* If PY_STRUCT_FLOAT_COERCE is defined, the struct module will allow float
arguments for integer formats with a warning for backwards
compatibility. */
#define PY_STRUCT_FLOAT_COERCE 1
#ifdef PY_STRUCT_FLOAT_COERCE
#define FLOAT_COERCE "integer argument expected, got float"
#endif
/* The translation function for each format character is table driven */ /* The translation function for each format character is table driven */
typedef struct _formatdef { typedef struct _formatdef {
char format; char format;
...@@ -100,58 +89,40 @@ typedef struct { char c; _Bool x; } s_bool; ...@@ -100,58 +89,40 @@ typedef struct { char c; _Bool x; } s_bool;
#pragma options align=reset #pragma options align=reset
#endif #endif
/* Helper to get a PyLongObject by hook or by crook. Caller should decref. */ /* Helper to get a PyLongObject. Caller should decref. */
static PyObject * static PyObject *
get_pylong(PyObject *v) get_pylong(PyObject *v)
{ {
PyNumberMethods *m;
assert(v != NULL); assert(v != NULL);
if (PyLong_Check(v)) { if (!PyLong_Check(v)) {
Py_INCREF(v);
return v;
}
m = Py_TYPE(v)->tp_as_number;
if (m != NULL && m->nb_int != NULL) {
v = m->nb_int(v);
if (v == NULL)
return NULL;
if (PyLong_Check(v))
return v;
Py_DECREF(v);
}
PyErr_SetString(StructError, PyErr_SetString(StructError,
"cannot convert argument to long"); "required argument is not an integer");
return NULL; return NULL;
}
Py_INCREF(v);
return v;
} }
/* Helper routine to get a Python integer and raise the appropriate error /* Helper routine to get a C long and raise the appropriate error if it isn't
if it isn't one */ one */
static int static int
get_long(PyObject *v, long *p) get_long(PyObject *v, long *p)
{ {
long x = PyLong_AsLong(v); long x;
if (x == -1 && PyErr_Occurred()) {
#ifdef PY_STRUCT_FLOAT_COERCE if (!PyLong_Check(v)) {
if (PyFloat_Check(v)) { PyErr_SetString(StructError,
PyObject *o; "required argument is not an integer");
int res;
PyErr_Clear();
if (PyErr_WarnEx(PyExc_DeprecationWarning, FLOAT_COERCE, 2) < 0)
return -1;
o = PyNumber_Long(v);
if (o == NULL)
return -1; return -1;
res = get_long(o, p);
Py_DECREF(o);
return res;
} }
#endif x = PyLong_AsLong(v);
if (PyErr_ExceptionMatches(PyExc_TypeError)) if (x == -1 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_OverflowError))
PyErr_SetString(StructError, PyErr_SetString(StructError,
"required argument is not an integer"); "argument out of range");
return -1; return -1;
} }
*p = x; *p = x;
...@@ -164,20 +135,21 @@ get_long(PyObject *v, long *p) ...@@ -164,20 +135,21 @@ get_long(PyObject *v, long *p)
static int static int
get_ulong(PyObject *v, unsigned long *p) get_ulong(PyObject *v, unsigned long *p)
{ {
if (PyLong_Check(v)) { unsigned long x;
unsigned long x = PyLong_AsUnsignedLong(v);
if (x == (unsigned long)(-1) && PyErr_Occurred()) if (!PyLong_Check(v)) {
PyErr_SetString(StructError,
"required argument is not an integer");
return -1; return -1;
*p = x;
return 0;
} }
if (get_long(v, (long *)p) < 0) x = PyLong_AsUnsignedLong(v);
return -1; if (x == (unsigned long)-1 && PyErr_Occurred()) {
if (((long)*p) < 0) { if (PyErr_ExceptionMatches(PyExc_OverflowError))
PyErr_SetString(StructError, PyErr_SetString(StructError,
"unsigned argument is < 0"); "argument out of range");
return -1; return -1;
} }
*p = x;
return 0; return 0;
} }
...@@ -189,15 +161,18 @@ static int ...@@ -189,15 +161,18 @@ static int
get_longlong(PyObject *v, PY_LONG_LONG *p) get_longlong(PyObject *v, PY_LONG_LONG *p)
{ {
PY_LONG_LONG x; PY_LONG_LONG x;
if (!PyLong_Check(v)) {
v = get_pylong(v); PyErr_SetString(StructError,
if (v == NULL) "required argument is not an integer");
return -1; return -1;
assert(PyLong_Check(v)); }
x = PyLong_AsLongLong(v); x = PyLong_AsLongLong(v);
Py_DECREF(v); if (x == -1 && PyErr_Occurred()) {
if (x == (PY_LONG_LONG)-1 && PyErr_Occurred()) if (PyErr_ExceptionMatches(PyExc_OverflowError))
PyErr_SetString(StructError,
"argument out of range");
return -1; return -1;
}
*p = x; *p = x;
return 0; return 0;
} }
...@@ -208,15 +183,18 @@ static int ...@@ -208,15 +183,18 @@ static int
get_ulonglong(PyObject *v, unsigned PY_LONG_LONG *p) get_ulonglong(PyObject *v, unsigned PY_LONG_LONG *p)
{ {
unsigned PY_LONG_LONG x; unsigned PY_LONG_LONG x;
if (!PyLong_Check(v)) {
v = get_pylong(v); PyErr_SetString(StructError,
if (v == NULL) "required argument is not an integer");
return -1; return -1;
assert(PyLong_Check(v)); }
x = PyLong_AsUnsignedLongLong(v); x = PyLong_AsUnsignedLongLong(v);
Py_DECREF(v); if (x == -1 && PyErr_Occurred()) {
if (x == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) if (PyErr_ExceptionMatches(PyExc_OverflowError))
PyErr_SetString(StructError,
"argument out of range");
return -1; return -1;
}
*p = x; *p = x;
return 0; return 0;
} }
...@@ -1962,7 +1940,7 @@ PyInit__struct(void) ...@@ -1962,7 +1940,7 @@ PyInit__struct(void)
{ {
PyObject *ver, *m; PyObject *ver, *m;
ver = PyBytes_FromString("0.2"); ver = PyBytes_FromString("0.3");
if (ver == NULL) if (ver == NULL)
return NULL; return NULL;
...@@ -2028,9 +2006,5 @@ PyInit__struct(void) ...@@ -2028,9 +2006,5 @@ PyInit__struct(void)
PyModule_AddObject(m, "__version__", ver); PyModule_AddObject(m, "__version__", ver);
#ifdef PY_STRUCT_FLOAT_COERCE
PyModule_AddIntConstant(m, "_PY_STRUCT_FLOAT_COERCE", 1);
#endif
return m; return m;
} }
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