mirror of
https://github.com/python/cpython.git
synced 2025-11-24 20:30:18 +00:00
Fix #1530559, struct.pack raises TypeError where it used to convert.
Passing float arguments to struct.pack when integers are expected now triggers a DeprecationWarning.
This commit is contained in:
parent
b1ccc4d409
commit
e6c9f982a0
3 changed files with 134 additions and 27 deletions
|
|
@ -15,9 +15,11 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
PY_STRUCT_RANGE_CHECKING = 0
|
PY_STRUCT_RANGE_CHECKING = 0
|
||||||
PY_STRUCT_OVERFLOW_MASKING = 1
|
PY_STRUCT_OVERFLOW_MASKING = 1
|
||||||
|
PY_STRUCT_FLOAT_COERCE = 2
|
||||||
else:
|
else:
|
||||||
PY_STRUCT_RANGE_CHECKING = _struct._PY_STRUCT_RANGE_CHECKING
|
PY_STRUCT_RANGE_CHECKING = getattr(_struct, '_PY_STRUCT_RANGE_CHECKING', 0)
|
||||||
PY_STRUCT_OVERFLOW_MASKING = _struct._PY_STRUCT_OVERFLOW_MASKING
|
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):
|
def string_reverse(s):
|
||||||
return "".join(reversed(s))
|
return "".join(reversed(s))
|
||||||
|
|
@ -46,33 +48,40 @@ def any_err(func, *args):
|
||||||
raise TestFailed, "%s%s did not raise error" % (
|
raise TestFailed, "%s%s did not raise error" % (
|
||||||
func.__name__, args)
|
func.__name__, args)
|
||||||
|
|
||||||
def deprecated_err(func, *args):
|
def with_warning_restore(func):
|
||||||
# The `warnings` module doesn't have an advertised way to restore
|
def _with_warning_restore(*args, **kw):
|
||||||
# its filter list. Cheat.
|
# The `warnings` module doesn't have an advertised way to restore
|
||||||
save_warnings_filters = warnings.filters[:]
|
# its filter list. Cheat.
|
||||||
# Grrr, we need this function to warn every time. Without removing
|
save_warnings_filters = warnings.filters[:]
|
||||||
# the warningregistry, running test_tarfile then test_struct would fail
|
# Grrr, we need this function to warn every time. Without removing
|
||||||
# on 64-bit platforms.
|
# the warningregistry, running test_tarfile then test_struct would fail
|
||||||
globals = func.func_globals
|
# on 64-bit platforms.
|
||||||
if '__warningregistry__' in globals:
|
globals = func.func_globals
|
||||||
del globals['__warningregistry__']
|
if '__warningregistry__' in globals:
|
||||||
warnings.filterwarnings("error", r"""^struct.*""", DeprecationWarning)
|
del globals['__warningregistry__']
|
||||||
warnings.filterwarnings("error", r""".*format requires.*""",
|
warnings.filterwarnings("error", r"""^struct.*""", DeprecationWarning)
|
||||||
DeprecationWarning)
|
warnings.filterwarnings("error", r""".*format requires.*""",
|
||||||
try:
|
DeprecationWarning)
|
||||||
try:
|
try:
|
||||||
func(*args)
|
return func(*args, **kw)
|
||||||
except (struct.error, TypeError):
|
finally:
|
||||||
pass
|
warnings.filters[:] = save_warnings_filters[:]
|
||||||
except DeprecationWarning:
|
return _with_warning_restore
|
||||||
if not PY_STRUCT_OVERFLOW_MASKING:
|
|
||||||
raise TestFailed, "%s%s expected to raise struct.error" % (
|
def deprecated_err(func, *args):
|
||||||
func.__name__, args)
|
try:
|
||||||
else:
|
func(*args)
|
||||||
raise TestFailed, "%s%s did not raise error" % (
|
except (struct.error, TypeError):
|
||||||
|
pass
|
||||||
|
except DeprecationWarning:
|
||||||
|
if not PY_STRUCT_OVERFLOW_MASKING:
|
||||||
|
raise TestFailed, "%s%s expected to raise struct.error" % (
|
||||||
func.__name__, args)
|
func.__name__, args)
|
||||||
finally:
|
else:
|
||||||
warnings.filters[:] = save_warnings_filters[:]
|
raise TestFailed, "%s%s did not raise error" % (
|
||||||
|
func.__name__, args)
|
||||||
|
deprecated_err = with_warning_restore(deprecated_err)
|
||||||
|
|
||||||
|
|
||||||
simple_err(struct.calcsize, 'Z')
|
simple_err(struct.calcsize, 'Z')
|
||||||
|
|
||||||
|
|
@ -475,6 +484,9 @@ def test_705836():
|
||||||
|
|
||||||
test_705836()
|
test_705836()
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
# SF bug 1229380. No struct.pack exception for some out of range integers
|
||||||
|
|
||||||
def test_1229380():
|
def test_1229380():
|
||||||
import sys
|
import sys
|
||||||
for endian in ('', '>', '<'):
|
for endian in ('', '>', '<'):
|
||||||
|
|
@ -491,6 +503,37 @@ def test_1229380():
|
||||||
if PY_STRUCT_RANGE_CHECKING:
|
if PY_STRUCT_RANGE_CHECKING:
|
||||||
test_1229380()
|
test_1229380()
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
# SF bug 1530559. struct.pack raises TypeError where it used to convert.
|
||||||
|
|
||||||
|
def check_float_coerce(format, number):
|
||||||
|
if PY_STRUCT_FLOAT_COERCE == 2:
|
||||||
|
# Test for pre-2.5 struct module
|
||||||
|
packed = struct.pack(format, number)
|
||||||
|
floored = struct.unpack(format, packed)[0]
|
||||||
|
if floored != int(number):
|
||||||
|
raise TestFailed("did not correcly coerce float to int")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
func(*args)
|
||||||
|
except (struct.error, TypeError):
|
||||||
|
if PY_STRUCT_FLOAT_COERCE:
|
||||||
|
raise TestFailed("expected DeprecationWarning for float coerce")
|
||||||
|
except DeprecationWarning:
|
||||||
|
if not PY_STRUCT_FLOAT_COERCE:
|
||||||
|
raise TestFailed("expected to raise struct.error for float coerce")
|
||||||
|
else:
|
||||||
|
raise TestFailed("did not raise error for float coerce")
|
||||||
|
|
||||||
|
check_float_coerce = with_warning_restore(deprecated_err)
|
||||||
|
|
||||||
|
def test_1530559():
|
||||||
|
for endian in ('', '>', '<'):
|
||||||
|
for fmt in ('B', 'H', 'I', 'L', 'b', 'h', 'i', 'l'):
|
||||||
|
check_float_coerce(endian + fmt, 1.0)
|
||||||
|
check_float_coerce(endian + fmt, 1.5)
|
||||||
|
|
||||||
|
test_1530559()
|
||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
# Packing and unpacking to/from buffers.
|
# Packing and unpacking to/from buffers.
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,10 @@ Library
|
||||||
Extension Modules
|
Extension Modules
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Bug #1530559, struct.pack raises TypeError where it used to convert.
|
||||||
|
Passing float arguments to struct.pack when integers are expected
|
||||||
|
now triggers a DeprecationWarning.
|
||||||
|
|
||||||
|
|
||||||
Tests
|
Tests
|
||||||
-----
|
-----
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,17 @@ static PyObject *pylong_ulong_mask = NULL;
|
||||||
static PyObject *pyint_zero = NULL;
|
static PyObject *pyint_zero = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
@ -135,6 +146,21 @@ get_long(PyObject *v, long *p)
|
||||||
{
|
{
|
||||||
long x = PyInt_AsLong(v);
|
long x = PyInt_AsLong(v);
|
||||||
if (x == -1 && PyErr_Occurred()) {
|
if (x == -1 && PyErr_Occurred()) {
|
||||||
|
#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_Int(v);
|
||||||
|
if (o == NULL)
|
||||||
|
return -1;
|
||||||
|
res = get_long(o, p);
|
||||||
|
Py_DECREF(o);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (PyErr_ExceptionMatches(PyExc_TypeError))
|
if (PyErr_ExceptionMatches(PyExc_TypeError))
|
||||||
PyErr_SetString(StructError,
|
PyErr_SetString(StructError,
|
||||||
"required argument is not an integer");
|
"required argument is not an integer");
|
||||||
|
|
@ -225,6 +251,21 @@ get_wrapped_long(PyObject *v, long *p)
|
||||||
PyObject *wrapped;
|
PyObject *wrapped;
|
||||||
long x;
|
long x;
|
||||||
PyErr_Clear();
|
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_Int(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)
|
if (PyErr_WarnEx(PyExc_DeprecationWarning, INT_OVERFLOW, 2) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
wrapped = PyNumber_And(v, pylong_ulong_mask);
|
wrapped = PyNumber_And(v, pylong_ulong_mask);
|
||||||
|
|
@ -249,6 +290,21 @@ get_wrapped_ulong(PyObject *v, unsigned long *p)
|
||||||
if (x == -1 && PyErr_Occurred()) {
|
if (x == -1 && PyErr_Occurred()) {
|
||||||
PyObject *wrapped;
|
PyObject *wrapped;
|
||||||
PyErr_Clear();
|
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_Int(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);
|
wrapped = PyNumber_And(v, pylong_ulong_mask);
|
||||||
if (wrapped == NULL)
|
if (wrapped == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -1815,4 +1871,8 @@ init_struct(void)
|
||||||
#ifdef PY_STRUCT_OVERFLOW_MASKING
|
#ifdef PY_STRUCT_OVERFLOW_MASKING
|
||||||
PyModule_AddIntConstant(m, "_PY_STRUCT_OVERFLOW_MASKING", 1);
|
PyModule_AddIntConstant(m, "_PY_STRUCT_OVERFLOW_MASKING", 1);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef PY_STRUCT_FLOAT_COERCE
|
||||||
|
PyModule_AddIntConstant(m, "_PY_STRUCT_FLOAT_COERCE", 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue