]> granicus.if.org Git - python/commitdiff
Fix #1530559, struct.pack raises TypeError where it used to convert.
authorBob Ippolito <bob@redivi.com>
Fri, 4 Aug 2006 23:59:21 +0000 (23:59 +0000)
committerBob Ippolito <bob@redivi.com>
Fri, 4 Aug 2006 23:59:21 +0000 (23:59 +0000)
Passing float arguments to struct.pack when integers are expected
now triggers a DeprecationWarning.

Lib/test/test_struct.py
Misc/NEWS
Modules/_struct.c

index aa458e625b606da13147cc413b10c0ef7fa7fd1d..66fd6672f076693b34bc666fc09779ba7aba0c06 100644 (file)
@@ -15,9 +15,11 @@ try:
 except ImportError:
     PY_STRUCT_RANGE_CHECKING = 0
     PY_STRUCT_OVERFLOW_MASKING = 1
+    PY_STRUCT_FLOAT_COERCE = 2
 else:
-    PY_STRUCT_RANGE_CHECKING = _struct._PY_STRUCT_RANGE_CHECKING
-    PY_STRUCT_OVERFLOW_MASKING = _struct._PY_STRUCT_OVERFLOW_MASKING
+    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):
     return "".join(reversed(s))
@@ -46,33 +48,40 @@ def any_err(func, *args):
         raise TestFailed, "%s%s did not raise error" % (
             func.__name__, args)
 
+def with_warning_restore(func):
+    def _with_warning_restore(*args, **kw):
+        # The `warnings` module doesn't have an advertised way to restore
+        # its filter list.  Cheat.
+        save_warnings_filters = warnings.filters[:]
+        # Grrr, we need this function to warn every time.  Without removing
+        # the warningregistry, running test_tarfile then test_struct would fail
+        # on 64-bit platforms.
+        globals = func.func_globals
+        if '__warningregistry__' in globals:
+            del globals['__warningregistry__']
+        warnings.filterwarnings("error", r"""^struct.*""", DeprecationWarning)
+        warnings.filterwarnings("error", r""".*format requires.*""",
+                                DeprecationWarning)
+        try:
+            return func(*args, **kw)
+        finally:
+            warnings.filters[:] = save_warnings_filters[:]
+    return _with_warning_restore
+
 def deprecated_err(func, *args):
-    # The `warnings` module doesn't have an advertised way to restore
-    # its filter list.  Cheat.
-    save_warnings_filters = warnings.filters[:]
-    # Grrr, we need this function to warn every time.  Without removing
-    # the warningregistry, running test_tarfile then test_struct would fail
-    # on 64-bit platforms.
-    globals = func.func_globals
-    if '__warningregistry__' in globals:
-        del globals['__warningregistry__']
-    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_OVERFLOW_MASKING:
-                raise TestFailed, "%s%s expected to raise struct.error" % (
-                    func.__name__, args)
-        else:
-            raise TestFailed, "%s%s did not raise error" % (
+        func(*args)
+    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)
-    finally:
-        warnings.filters[:] = save_warnings_filters[:]
+    else:
+        raise TestFailed, "%s%s did not raise error" % (
+            func.__name__, args)
+deprecated_err = with_warning_restore(deprecated_err)
+
 
 simple_err(struct.calcsize, 'Z')
 
@@ -475,6 +484,9 @@ def test_705836():
 
 test_705836()
 
+###########################################################################
+# SF bug 1229380. No struct.pack exception for some out of range integers
+
 def test_1229380():
     import sys
     for endian in ('', '>', '<'):
@@ -491,6 +503,37 @@ def test_1229380():
 if PY_STRUCT_RANGE_CHECKING:
     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.
index 9373be268d158e3414cc0de8f2cc49d1a62695c8..d3a94425db35faebc1961f817cbc1709a7da6f9f 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -40,6 +40,10 @@ Library
 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
 -----
index 3774dfd6b9e07ead626b52497212f4a56ab2c4b9..22d0e030cfcfe828847d3af2dff86dd37b5c5f51 100644 (file)
@@ -31,6 +31,17 @@ 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. */
+
+#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 */
 typedef struct _formatdef {
        char format;
@@ -135,6 +146,21 @@ get_long(PyObject *v, long *p)
 {
        long x = PyInt_AsLong(v);
        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))
                        PyErr_SetString(StructError,
                                        "required argument is not an integer");
@@ -225,6 +251,21 @@ get_wrapped_long(PyObject *v, long *p)
                        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_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)
                                return -1;
                        wrapped = PyNumber_And(v, pylong_ulong_mask);
@@ -249,6 +290,21 @@ get_wrapped_ulong(PyObject *v, unsigned long *p)
        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_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);
                if (wrapped == NULL)
                        return -1;
@@ -1815,4 +1871,8 @@ init_struct(void)
 #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
+
 }