Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ea835e7

Browse files
committed
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.
1 parent 769ba47 commit ea835e7

3 files changed

Lines changed: 71 additions & 135 deletions

File tree

Lib/test/test_struct.py

Lines changed: 15 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,6 @@
1111
IS32BIT = sys.maxsize == 0x7fffffff
1212
del sys
1313

14-
try:
15-
import _struct
16-
except ImportError:
17-
PY_STRUCT_FLOAT_COERCE = 2
18-
else:
19-
PY_STRUCT_FLOAT_COERCE = getattr(_struct, '_PY_STRUCT_FLOAT_COERCE', 0)
20-
2114
def string_reverse(s):
2215
return s[::-1]
2316

@@ -27,40 +20,7 @@ def bigendian_to_native(value):
2720
else:
2821
return string_reverse(value)
2922

30-
def with_warning_restore(func):
31-
@wraps(func)
32-
def decorator(*args, **kw):
33-
with warnings.catch_warnings():
34-
# We need this function to warn every time, so stick an
35-
# unqualifed 'always' at the head of the filter list
36-
warnings.simplefilter("always")
37-
warnings.filterwarnings("error", category=DeprecationWarning)
38-
return func(*args, **kw)
39-
return decorator
40-
4123
class StructTest(unittest.TestCase):
42-
43-
@with_warning_restore
44-
def check_float_coerce(self, format, number):
45-
# SF bug 1530559. struct.pack raises TypeError where it used to convert.
46-
if PY_STRUCT_FLOAT_COERCE == 2:
47-
# Test for pre-2.5 struct module
48-
packed = struct.pack(format, number)
49-
floored = struct.unpack(format, packed)[0]
50-
self.assertEqual(floored, int(number),
51-
"did not correcly coerce float to int")
52-
return
53-
try:
54-
struct.pack(format, number)
55-
except (struct.error, TypeError):
56-
if PY_STRUCT_FLOAT_COERCE:
57-
self.fail("expected DeprecationWarning for float coerce")
58-
except DeprecationWarning:
59-
if not PY_STRUCT_FLOAT_COERCE:
60-
self.fail("expected to raise struct.error for float coerce")
61-
else:
62-
self.fail("did not raise error for float coerce")
63-
6424
def test_isbigendian(self):
6525
self.assertEqual((struct.pack('=i', 1)[0] == 0), ISBIGENDIAN)
6626

@@ -270,10 +230,8 @@ def test_one(self, x, pack=struct.pack,
270230

271231
else:
272232
# x is out of range -- verify pack realizes that.
273-
self.assertRaises((struct.error, OverflowError),
274-
pack, ">" + code, x)
275-
self.assertRaises((struct.error, OverflowError),
276-
pack, "<" + code, x)
233+
self.assertRaises(struct.error, pack, ">" + code, x)
234+
self.assertRaises(struct.error, pack, "<" + code, x)
277235

278236
# Much the same for unsigned.
279237
code = self.unsigned_code
@@ -317,10 +275,8 @@ def test_one(self, x, pack=struct.pack,
317275

318276
else:
319277
# x is out of range -- verify pack realizes that.
320-
self.assertRaises((struct.error, OverflowError),
321-
pack, ">" + code, x)
322-
self.assertRaises((struct.error, OverflowError),
323-
pack, "<" + code, x)
278+
self.assertRaises(struct.error, pack, ">" + code, x)
279+
self.assertRaises(struct.error, pack, "<" + code, x)
324280

325281
def run(self):
326282
from random import randrange
@@ -353,10 +309,10 @@ def run(self):
353309
# Some error cases.
354310
for direction in "<>":
355311
for code in self.formatpair:
356-
for badobject in "a string", 3+42j, randrange:
357-
self.assertRaises((struct.error, TypeError),
358-
struct.pack, direction + code,
359-
badobject)
312+
for badobject in "a string", 3+42j, randrange, -1729.0:
313+
self.assertRaises(struct.error,
314+
struct.pack, direction + code,
315+
badobject)
360316

361317
for args in [("bB", 1),
362318
("hH", 2),
@@ -437,13 +393,14 @@ def test_1229380(self):
437393
self.assertRaises((struct.error, OverflowError), struct.pack,
438394
endian + 'L', sys.maxsize * 4)
439395

440-
def XXXtest_1530559(self):
441-
# XXX This is broken: see the bug report
442-
# SF bug 1530559. struct.pack raises TypeError where it used to convert.
396+
def test_1530559(self):
443397
for endian in ('', '>', '<'):
444-
for fmt in ('B', 'H', 'I', 'L', 'b', 'h', 'i', 'l'):
445-
self.check_float_coerce(endian + fmt, 1.0)
446-
self.check_float_coerce(endian + fmt, 1.5)
398+
for fmt in ('B', 'H', 'I', 'L', 'Q', 'b', 'h', 'i', 'l', 'q'):
399+
self.assertRaises(struct.error, struct.pack, endian + fmt, 1.0)
400+
self.assertRaises(struct.error, struct.pack, endian + fmt, 1.5)
401+
self.assertRaises(struct.error, struct.pack, 'P', 1.0)
402+
self.assertRaises(struct.error, struct.pack, 'P', 1.5)
403+
447404

448405
def test_unpack_from(self):
449406
test_string = b'abcd01234'

Misc/NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ Library
8787
Extension Modules
8888
-----------------
8989

90+
- Issue #5463: In struct module, remove deprecated float coercion
91+
for integer type codes: struct.pack('L', 0.3) should now raise
92+
an error. The _PY_STRUCT_FLOAT_COERCE constant has been removed.
93+
The version number has been bumped to 0.3.
94+
9095
- Issue #5359: Readd the Berkley-DB detection code to allow _dbm be built
9196
using Berkley-DB.
9297

Modules/_struct.c

Lines changed: 51 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,6 @@
1212

1313
static PyTypeObject PyStructType;
1414

15-
/* If PY_STRUCT_FLOAT_COERCE is defined, the struct module will allow float
16-
arguments for integer formats with a warning for backwards
17-
compatibility. */
18-
19-
#define PY_STRUCT_FLOAT_COERCE 1
20-
21-
#ifdef PY_STRUCT_FLOAT_COERCE
22-
#define FLOAT_COERCE "integer argument expected, got float"
23-
#endif
24-
25-
2615
/* The translation function for each format character is table driven */
2716
typedef struct _formatdef {
2817
char format;
@@ -100,58 +89,40 @@ typedef struct { char c; _Bool x; } s_bool;
10089
#pragma options align=reset
10190
#endif
10291

103-
/* Helper to get a PyLongObject by hook or by crook. Caller should decref. */
92+
/* Helper to get a PyLongObject. Caller should decref. */
10493

10594
static PyObject *
10695
get_pylong(PyObject *v)
10796
{
108-
PyNumberMethods *m;
109-
11097
assert(v != NULL);
111-
if (PyLong_Check(v)) {
112-
Py_INCREF(v);
113-
return v;
114-
}
115-
m = Py_TYPE(v)->tp_as_number;
116-
if (m != NULL && m->nb_int != NULL) {
117-
v = m->nb_int(v);
118-
if (v == NULL)
119-
return NULL;
120-
if (PyLong_Check(v))
121-
return v;
122-
Py_DECREF(v);
98+
if (!PyLong_Check(v)) {
99+
PyErr_SetString(StructError,
100+
"required argument is not an integer");
101+
return NULL;
123102
}
124-
PyErr_SetString(StructError,
125-
"cannot convert argument to long");
126-
return NULL;
103+
104+
Py_INCREF(v);
105+
return v;
127106
}
128107

129-
/* Helper routine to get a Python integer and raise the appropriate error
130-
if it isn't one */
108+
/* Helper routine to get a C long and raise the appropriate error if it isn't
109+
one */
131110

132111
static int
133112
get_long(PyObject *v, long *p)
134113
{
135-
long x = PyLong_AsLong(v);
114+
long x;
115+
116+
if (!PyLong_Check(v)) {
117+
PyErr_SetString(StructError,
118+
"required argument is not an integer");
119+
return -1;
120+
}
121+
x = PyLong_AsLong(v);
136122
if (x == -1 && PyErr_Occurred()) {
137-
#ifdef PY_STRUCT_FLOAT_COERCE
138-
if (PyFloat_Check(v)) {
139-
PyObject *o;
140-
int res;
141-
PyErr_Clear();
142-
if (PyErr_WarnEx(PyExc_DeprecationWarning, FLOAT_COERCE, 2) < 0)
143-
return -1;
144-
o = PyNumber_Long(v);
145-
if (o == NULL)
146-
return -1;
147-
res = get_long(o, p);
148-
Py_DECREF(o);
149-
return res;
150-
}
151-
#endif
152-
if (PyErr_ExceptionMatches(PyExc_TypeError))
123+
if (PyErr_ExceptionMatches(PyExc_OverflowError))
153124
PyErr_SetString(StructError,
154-
"required argument is not an integer");
125+
"argument out of range");
155126
return -1;
156127
}
157128
*p = x;
@@ -164,20 +135,21 @@ get_long(PyObject *v, long *p)
164135
static int
165136
get_ulong(PyObject *v, unsigned long *p)
166137
{
167-
if (PyLong_Check(v)) {
168-
unsigned long x = PyLong_AsUnsignedLong(v);
169-
if (x == (unsigned long)(-1) && PyErr_Occurred())
170-
return -1;
171-
*p = x;
172-
return 0;
173-
}
174-
if (get_long(v, (long *)p) < 0)
175-
return -1;
176-
if (((long)*p) < 0) {
138+
unsigned long x;
139+
140+
if (!PyLong_Check(v)) {
177141
PyErr_SetString(StructError,
178-
"unsigned argument is < 0");
142+
"required argument is not an integer");
143+
return -1;
144+
}
145+
x = PyLong_AsUnsignedLong(v);
146+
if (x == (unsigned long)-1 && PyErr_Occurred()) {
147+
if (PyErr_ExceptionMatches(PyExc_OverflowError))
148+
PyErr_SetString(StructError,
149+
"argument out of range");
179150
return -1;
180151
}
152+
*p = x;
181153
return 0;
182154
}
183155

@@ -189,15 +161,18 @@ static int
189161
get_longlong(PyObject *v, PY_LONG_LONG *p)
190162
{
191163
PY_LONG_LONG x;
192-
193-
v = get_pylong(v);
194-
if (v == NULL)
164+
if (!PyLong_Check(v)) {
165+
PyErr_SetString(StructError,
166+
"required argument is not an integer");
195167
return -1;
196-
assert(PyLong_Check(v));
168+
}
197169
x = PyLong_AsLongLong(v);
198-
Py_DECREF(v);
199-
if (x == (PY_LONG_LONG)-1 && PyErr_Occurred())
170+
if (x == -1 && PyErr_Occurred()) {
171+
if (PyErr_ExceptionMatches(PyExc_OverflowError))
172+
PyErr_SetString(StructError,
173+
"argument out of range");
200174
return -1;
175+
}
201176
*p = x;
202177
return 0;
203178
}
@@ -208,15 +183,18 @@ static int
208183
get_ulonglong(PyObject *v, unsigned PY_LONG_LONG *p)
209184
{
210185
unsigned PY_LONG_LONG x;
211-
212-
v = get_pylong(v);
213-
if (v == NULL)
186+
if (!PyLong_Check(v)) {
187+
PyErr_SetString(StructError,
188+
"required argument is not an integer");
214189
return -1;
215-
assert(PyLong_Check(v));
190+
}
216191
x = PyLong_AsUnsignedLongLong(v);
217-
Py_DECREF(v);
218-
if (x == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred())
192+
if (x == -1 && PyErr_Occurred()) {
193+
if (PyErr_ExceptionMatches(PyExc_OverflowError))
194+
PyErr_SetString(StructError,
195+
"argument out of range");
219196
return -1;
197+
}
220198
*p = x;
221199
return 0;
222200
}
@@ -1962,7 +1940,7 @@ PyInit__struct(void)
19621940
{
19631941
PyObject *ver, *m;
19641942

1965-
ver = PyBytes_FromString("0.2");
1943+
ver = PyBytes_FromString("0.3");
19661944
if (ver == NULL)
19671945
return NULL;
19681946

@@ -2028,9 +2006,5 @@ PyInit__struct(void)
20282006

20292007
PyModule_AddObject(m, "__version__", ver);
20302008

2031-
#ifdef PY_STRUCT_FLOAT_COERCE
2032-
PyModule_AddIntConstant(m, "_PY_STRUCT_FLOAT_COERCE", 1);
2033-
#endif
20342009
return m;
2035-
20362010
}

0 commit comments

Comments
 (0)