struct: modulo math plus warning on all endian-explicit formats for compatibility with older struct usage (ugly)
This commit is contained in:
parent
b51b470eb8
commit
2fd3977a9d
|
@ -3,6 +3,7 @@ import test.test_support
|
||||||
import struct
|
import struct
|
||||||
import array
|
import array
|
||||||
import unittest
|
import unittest
|
||||||
|
import warnings
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
ISBIGENDIAN = sys.byteorder == "big"
|
ISBIGENDIAN = sys.byteorder == "big"
|
||||||
|
@ -10,7 +11,14 @@ del sys
|
||||||
verify((struct.pack('=i', 1)[0] == chr(0)) == ISBIGENDIAN,
|
verify((struct.pack('=i', 1)[0] == chr(0)) == ISBIGENDIAN,
|
||||||
"bigendian determination appears wrong")
|
"bigendian determination appears wrong")
|
||||||
|
|
||||||
PY_STRUCT_RANGE_CHECKING = 1
|
try:
|
||||||
|
import _struct
|
||||||
|
except ImportError:
|
||||||
|
PY_STRUCT_RANGE_CHECKING = 0
|
||||||
|
PY_STRUCT_WRAPPING = 1
|
||||||
|
else:
|
||||||
|
PY_STRUCT_RANGE_CHECKING = getattr(_struct, '_PY_STRUCT_RANGE_CHECKING', 0)
|
||||||
|
PY_STRUCT_WRAPPING = getattr(_struct, '_PY_STRUCT_WRAPPING', 0)
|
||||||
|
|
||||||
def string_reverse(s):
|
def string_reverse(s):
|
||||||
chars = list(s)
|
chars = list(s)
|
||||||
|
@ -35,12 +43,29 @@ def simple_err(func, *args):
|
||||||
def any_err(func, *args):
|
def any_err(func, *args):
|
||||||
try:
|
try:
|
||||||
func(*args)
|
func(*args)
|
||||||
except (struct.error, OverflowError, TypeError):
|
except (struct.error, TypeError):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
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):
|
||||||
|
warnings.filterwarnings("error", r"""^struct.*""", DeprecationWarning)
|
||||||
|
warnings.filterwarnings("error", r""".*format requires.*""", DeprecationWarning)
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
func(*args)
|
||||||
|
except (struct.error, TypeError):
|
||||||
|
pass
|
||||||
|
except DeprecationWarning:
|
||||||
|
if not PY_STRUCT_WRAPPING:
|
||||||
|
raise TestFailed, "%s%s expected to raise struct.error" % (
|
||||||
|
func.__name__, args)
|
||||||
|
else:
|
||||||
|
raise TestFailed, "%s%s did not raise error" % (
|
||||||
|
func.__name__, args)
|
||||||
|
finally:
|
||||||
|
warnings.resetwarnings()
|
||||||
|
|
||||||
simple_err(struct.calcsize, 'Z')
|
simple_err(struct.calcsize, 'Z')
|
||||||
|
|
||||||
|
@ -272,8 +297,8 @@ class IntTester:
|
||||||
if verbose:
|
if verbose:
|
||||||
print "Skipping buggy range check for code", code
|
print "Skipping buggy range check for code", code
|
||||||
else:
|
else:
|
||||||
any_err(pack, ">" + code, x)
|
deprecated_err(pack, ">" + code, x)
|
||||||
any_err(pack, "<" + code, x)
|
deprecated_err(pack, "<" + code, x)
|
||||||
|
|
||||||
# Much the same for unsigned.
|
# Much the same for unsigned.
|
||||||
code = self.unsigned_code
|
code = self.unsigned_code
|
||||||
|
@ -327,8 +352,8 @@ class IntTester:
|
||||||
if verbose:
|
if verbose:
|
||||||
print "Skipping buggy range check for code", code
|
print "Skipping buggy range check for code", code
|
||||||
else:
|
else:
|
||||||
any_err(pack, ">" + code, x)
|
deprecated_err(pack, ">" + code, x)
|
||||||
any_err(pack, "<" + code, x)
|
deprecated_err(pack, "<" + code, x)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
from random import randrange
|
from random import randrange
|
||||||
|
@ -448,13 +473,13 @@ def test_1229380():
|
||||||
for endian in ('', '>', '<'):
|
for endian in ('', '>', '<'):
|
||||||
for cls in (int, long):
|
for cls in (int, long):
|
||||||
for fmt in ('B', 'H', 'I', 'L'):
|
for fmt in ('B', 'H', 'I', 'L'):
|
||||||
any_err(struct.pack, endian + fmt, cls(-1))
|
deprecated_err(struct.pack, endian + fmt, cls(-1))
|
||||||
|
|
||||||
any_err(struct.pack, endian + 'B', cls(300))
|
deprecated_err(struct.pack, endian + 'B', cls(300))
|
||||||
any_err(struct.pack, endian + 'H', cls(70000))
|
deprecated_err(struct.pack, endian + 'H', cls(70000))
|
||||||
|
|
||||||
any_err(struct.pack, endian + 'I', sys.maxint * 4L)
|
deprecated_err(struct.pack, endian + 'I', sys.maxint * 4L)
|
||||||
any_err(struct.pack, endian + 'L', sys.maxint * 4L)
|
deprecated_err(struct.pack, endian + 'L', sys.maxint * 4L)
|
||||||
|
|
||||||
if PY_STRUCT_RANGE_CHECKING:
|
if PY_STRUCT_RANGE_CHECKING:
|
||||||
test_1229380()
|
test_1229380()
|
||||||
|
|
|
@ -17,6 +17,20 @@ static PyTypeObject PyStructType;
|
||||||
typedef int Py_ssize_t;
|
typedef int Py_ssize_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* If PY_STRUCT_WRAPPING 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_WRAPPING 1
|
||||||
|
|
||||||
|
#ifdef PY_STRUCT_WRAPPING
|
||||||
|
static PyObject *pylong_ulong_mask = NULL;
|
||||||
|
static PyObject *pyint_zero = NULL;
|
||||||
|
#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;
|
||||||
|
@ -195,6 +209,75 @@ get_ulonglong(PyObject *v, unsigned PY_LONG_LONG *p)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef PY_STRUCT_WRAPPING
|
||||||
|
|
||||||
|
/* Helper routine to get a Python integer and raise the appropriate error
|
||||||
|
if it isn't one */
|
||||||
|
|
||||||
|
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();
|
||||||
|
if (PyErr_Warn(PyExc_DeprecationWarning, "struct integer wrapping is deprecated") < 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();
|
||||||
|
wrapped = PyNumber_And(v, pylong_ulong_mask);
|
||||||
|
if (wrapped == NULL)
|
||||||
|
return -1;
|
||||||
|
if (PyErr_Warn(PyExc_DeprecationWarning, "struct integer wrapping is deprecated") < 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 */
|
/* Floating point helpers */
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -247,6 +330,25 @@ _range_error(const formatdef *f, int is_unsigned)
|
||||||
f->format,
|
f->format,
|
||||||
largest);
|
largest);
|
||||||
}
|
}
|
||||||
|
#ifdef PY_STRUCT_WRAPPING
|
||||||
|
{
|
||||||
|
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_Warn(PyExc_DeprecationWarning, PyString_AS_STRING(msg));
|
||||||
|
Py_DECREF(msg);
|
||||||
|
if (rval == 0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -707,15 +809,19 @@ bp_int(char *p, PyObject *v, const formatdef *f)
|
||||||
{
|
{
|
||||||
long x;
|
long x;
|
||||||
Py_ssize_t i;
|
Py_ssize_t i;
|
||||||
if (get_long(v, &x) < 0)
|
if (get_wrapped_long(v, &x) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
i = f->size;
|
i = f->size;
|
||||||
if (i != SIZEOF_LONG) {
|
if (i != SIZEOF_LONG) {
|
||||||
if ((i == 2) && (x < -32768 || x > 32767))
|
if ((i == 2) && (x < -32768 || x > 32767))
|
||||||
return _range_error(f, 0);
|
RANGE_ERROR(x, f, 0, 0xffffL);
|
||||||
#if (SIZEOF_LONG != 4)
|
#if (SIZEOF_LONG != 4)
|
||||||
else if ((i == 4) && (x < -2147483648L || x > 2147483647L))
|
else if ((i == 4) && (x < -2147483648L || x > 2147483647L))
|
||||||
return _range_error(f, 0);
|
RANGE_ERROR(x, f, 0, 0xffffffffL);
|
||||||
|
#endif
|
||||||
|
#ifdef PY_STRUCT_WRAPPING
|
||||||
|
else if ((i == 1) && (x < -128 || x > 127))
|
||||||
|
RANGE_ERROR(x, f, 0, 0xffL);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
@ -730,14 +836,14 @@ bp_uint(char *p, PyObject *v, const formatdef *f)
|
||||||
{
|
{
|
||||||
unsigned long x;
|
unsigned long x;
|
||||||
Py_ssize_t i;
|
Py_ssize_t i;
|
||||||
if (get_ulong(v, &x) < 0)
|
if (get_wrapped_ulong(v, &x) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
i = f->size;
|
i = f->size;
|
||||||
if (i != SIZEOF_LONG) {
|
if (i != SIZEOF_LONG) {
|
||||||
unsigned long maxint = 1;
|
unsigned long maxint = 1;
|
||||||
maxint <<= (unsigned long)(i * 8);
|
maxint <<= (unsigned long)(i * 8);
|
||||||
if (x >= maxint)
|
if (x >= maxint)
|
||||||
return _range_error(f, 1);
|
RANGE_ERROR(x, f, 1, maxint - 1);
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
p[--i] = (char)x;
|
p[--i] = (char)x;
|
||||||
|
@ -804,8 +910,14 @@ bp_double(char *p, PyObject *v, const formatdef *f)
|
||||||
|
|
||||||
static formatdef bigendian_table[] = {
|
static formatdef bigendian_table[] = {
|
||||||
{'x', 1, 0, NULL},
|
{'x', 1, 0, NULL},
|
||||||
|
#ifdef PY_STRUCT_WRAPPING
|
||||||
|
/* Native packers do range checking without wrapping. */
|
||||||
|
{'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_byte, np_byte},
|
||||||
{'B', 1, 0, nu_ubyte, np_ubyte},
|
{'B', 1, 0, nu_ubyte, np_ubyte},
|
||||||
|
#endif
|
||||||
{'c', 1, 0, nu_char, np_char},
|
{'c', 1, 0, nu_char, np_char},
|
||||||
{'s', 1, 0, NULL},
|
{'s', 1, 0, NULL},
|
||||||
{'p', 1, 0, NULL},
|
{'p', 1, 0, NULL},
|
||||||
|
@ -915,15 +1027,19 @@ lp_int(char *p, PyObject *v, const formatdef *f)
|
||||||
{
|
{
|
||||||
long x;
|
long x;
|
||||||
Py_ssize_t i;
|
Py_ssize_t i;
|
||||||
if (get_long(v, &x) < 0)
|
if (get_wrapped_long(v, &x) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
i = f->size;
|
i = f->size;
|
||||||
if (i != SIZEOF_LONG) {
|
if (i != SIZEOF_LONG) {
|
||||||
if ((i == 2) && (x < -32768 || x > 32767))
|
if ((i == 2) && (x < -32768 || x > 32767))
|
||||||
return _range_error(f, 0);
|
RANGE_ERROR(x, f, 0, 0xffffL);
|
||||||
#if (SIZEOF_LONG != 4)
|
#if (SIZEOF_LONG != 4)
|
||||||
else if ((i == 4) && (x < -2147483648L || x > 2147483647L))
|
else if ((i == 4) && (x < -2147483648L || x > 2147483647L))
|
||||||
return _range_error(f, 0);
|
RANGE_ERROR(x, f, 0, 0xffffffffL);
|
||||||
|
#endif
|
||||||
|
#ifdef PY_STRUCT_WRAPPING
|
||||||
|
else if ((i == 1) && (x < -128 || x > 127))
|
||||||
|
RANGE_ERROR(x, f, 0, 0xffL);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
@ -938,14 +1054,14 @@ lp_uint(char *p, PyObject *v, const formatdef *f)
|
||||||
{
|
{
|
||||||
unsigned long x;
|
unsigned long x;
|
||||||
Py_ssize_t i;
|
Py_ssize_t i;
|
||||||
if (get_ulong(v, &x) < 0)
|
if (get_wrapped_ulong(v, &x) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
i = f->size;
|
i = f->size;
|
||||||
if (i != SIZEOF_LONG) {
|
if (i != SIZEOF_LONG) {
|
||||||
unsigned long maxint = 1;
|
unsigned long maxint = 1;
|
||||||
maxint <<= (unsigned long)(i * 8);
|
maxint <<= (unsigned long)(i * 8);
|
||||||
if (x >= maxint)
|
if (x >= maxint)
|
||||||
return _range_error(f, 1);
|
RANGE_ERROR(x, f, 1, maxint - 1);
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
*p++ = (char)x;
|
*p++ = (char)x;
|
||||||
|
@ -1012,8 +1128,14 @@ lp_double(char *p, PyObject *v, const formatdef *f)
|
||||||
|
|
||||||
static formatdef lilendian_table[] = {
|
static formatdef lilendian_table[] = {
|
||||||
{'x', 1, 0, NULL},
|
{'x', 1, 0, NULL},
|
||||||
|
#ifdef PY_STRUCT_WRAPPING
|
||||||
|
/* Native packers do range checking without wrapping. */
|
||||||
|
{'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_byte, np_byte},
|
||||||
{'B', 1, 0, nu_ubyte, np_ubyte},
|
{'B', 1, 0, nu_ubyte, np_ubyte},
|
||||||
|
#endif
|
||||||
{'c', 1, 0, nu_char, np_char},
|
{'c', 1, 0, nu_char, np_char},
|
||||||
{'s', 1, 0, NULL},
|
{'s', 1, 0, NULL},
|
||||||
{'p', 1, 0, NULL},
|
{'p', 1, 0, NULL},
|
||||||
|
@ -1418,8 +1540,12 @@ s_pack_internal(PyStructObject *soself, PyObject *args, int offset, char* buf)
|
||||||
*res = Py_SAFE_DOWNCAST(n, Py_ssize_t, unsigned char);
|
*res = Py_SAFE_DOWNCAST(n, Py_ssize_t, unsigned char);
|
||||||
} else {
|
} else {
|
||||||
v = PyTuple_GET_ITEM(args, i++);
|
v = PyTuple_GET_ITEM(args, i++);
|
||||||
if (e->pack(res, v, e) < 0)
|
if (e->pack(res, v, e) < 0) {
|
||||||
|
if (PyLong_Check(v) && PyErr_ExceptionMatches(PyExc_OverflowError))
|
||||||
|
PyErr_SetString(StructError,
|
||||||
|
"long too large to convert to int");
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1614,6 +1740,26 @@ init_struct(void)
|
||||||
if (PyType_Ready(&PyStructType) < 0)
|
if (PyType_Ready(&PyStructType) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
#ifdef PY_STRUCT_WRAPPING
|
||||||
|
if (pyint_zero == NULL) {
|
||||||
|
pyint_zero = PyInt_FromLong(0);
|
||||||
|
if (pyint_zero == NULL)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
/* This speed trick can't be used until wrapping goes away, because
|
||||||
|
native endian always raises exceptions instead of wrapping. */
|
||||||
|
|
||||||
/* Check endian and swap in faster functions */
|
/* Check endian and swap in faster functions */
|
||||||
{
|
{
|
||||||
int one = 1;
|
int one = 1;
|
||||||
|
@ -1652,6 +1798,7 @@ init_struct(void)
|
||||||
native++;
|
native++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Add some symbolic constants to the module */
|
/* Add some symbolic constants to the module */
|
||||||
if (StructError == NULL) {
|
if (StructError == NULL) {
|
||||||
|
@ -1665,4 +1812,9 @@ init_struct(void)
|
||||||
|
|
||||||
Py_INCREF((PyObject*)&PyStructType);
|
Py_INCREF((PyObject*)&PyStructType);
|
||||||
PyModule_AddObject(m, "Struct", (PyObject*)&PyStructType);
|
PyModule_AddObject(m, "Struct", (PyObject*)&PyStructType);
|
||||||
|
|
||||||
|
PyModule_AddIntConstant(m, "_PY_STRUCT_RANGE_CHECKING", 1);
|
||||||
|
#ifdef PY_STRUCT_WRAPPING
|
||||||
|
PyModule_AddIntConstant(m, "_PY_STRUCT_WRAPPING", 1);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue