[Python-checkins] r51119 - in python/trunk: Lib/test/test_struct.py Misc/NEWS Modules/_struct.c

bob.ippolito python-checkins at python.org
Sat Aug 5 01:59:22 CEST 2006


Author: bob.ippolito
Date: Sat Aug  5 01:59:21 2006
New Revision: 51119

Modified:
   python/trunk/Lib/test/test_struct.py
   python/trunk/Misc/NEWS
   python/trunk/Modules/_struct.c
Log:
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.



Modified: python/trunk/Lib/test/test_struct.py
==============================================================================
--- python/trunk/Lib/test/test_struct.py	(original)
+++ python/trunk/Lib/test/test_struct.py	Sat Aug  5 01:59:21 2006
@@ -15,9 +15,11 @@
 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 @@
         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 @@
 
 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 @@
 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.

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Sat Aug  5 01:59:21 2006
@@ -40,6 +40,10 @@
 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
 -----

Modified: python/trunk/Modules/_struct.c
==============================================================================
--- python/trunk/Modules/_struct.c	(original)
+++ python/trunk/Modules/_struct.c	Sat Aug  5 01:59:21 2006
@@ -31,6 +31,17 @@
 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 @@
 {
 	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 @@
 			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 @@
 	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 @@
 #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
+
 }


More information about the Python-checkins mailing list