[Python-checkins] r78762 - in python/trunk: Doc/library/struct.rst Lib/test/test_struct.py Misc/NEWS Modules/_struct.c

mark.dickinson python-checkins at python.org
Sun Mar 7 17:24:45 CET 2010


Author: mark.dickinson
Date: Sun Mar 7 17:24:45 2010
New Revision: 78762
Log:
Issue #1530559: When packing a non-integer with any integer conversion
code using struct.pack, attempt to convert to an integer first using
the argument's __int__ method (if present). Also raise a
DeprecationWarning for any such usage of __int__.
This fixes a regression from 2.6, where some (but not all) integer
conversion codes already used __int__.
Modified:
 python/trunk/Doc/library/struct.rst
 python/trunk/Lib/test/test_struct.py
 python/trunk/Misc/NEWS
 python/trunk/Modules/_struct.c
Modified: python/trunk/Doc/library/struct.rst
==============================================================================
--- python/trunk/Doc/library/struct.rst	(original)
+++ python/trunk/Doc/library/struct.rst	Sun Mar 7 17:24:45 2010
@@ -123,6 +123,18 @@
 
 .. versionadded:: 2.2
 
+(3)
+ When attempting to pack a non-integer using any of the integer conversion
+ codes, the non-integer's :meth:`__int__` method (if present) will be called
+ to convert to an integer before packing. However, this behaviour is
+ deprecated, and will raise :exc:`DeprecationWarning`.
+
+ .. versionchanged:: 2.7
+ Prior to version 2.7, not all integer conversion codes would use the
+ :meth:`__int__` method to convert, and :exc:`DeprecationWarning` was
+ raised only for float arguments.
+
+
 A format character may be preceded by an integral repeat count. For example,
 the format string ``'4h'`` means exactly the same as ``'hhhh'``.
 
Modified: python/trunk/Lib/test/test_struct.py
==============================================================================
--- python/trunk/Lib/test/test_struct.py	(original)
+++ python/trunk/Lib/test/test_struct.py	Sun Mar 7 17:24:45 2010
@@ -2,10 +2,6 @@
 import unittest
 import struct
 import warnings
-warnings.filterwarnings("ignore", "struct integer overflow masking is deprecated",
- DeprecationWarning)
-
-from functools import wraps
 from test.test_support import run_unittest
 
 import sys
@@ -35,22 +31,27 @@
 class StructTest(unittest.TestCase):
 
 def check_float_coerce(self, format, number):
- # SF bug 1530559. struct.pack raises TypeError where it used to convert.
- with warnings.catch_warnings():
+ # SF bug 1530559. struct.pack raises TypeError where it used
+ # to convert.
+ with warnings.catch_warnings(record=True) as w:
+ # ignore everything except the
+ # DeprecationWarning we're looking for
+ warnings.simplefilter("ignore")
 warnings.filterwarnings(
- "ignore",
- category=DeprecationWarning,
+ "always",
 message=".*integer argument expected, got float",
- module=__name__)
- self.assertEqual(struct.pack(format, number), struct.pack(format, int(number)))
-
- with warnings.catch_warnings():
- warnings.filterwarnings(
- "error",
 category=DeprecationWarning,
- message=".*integer argument expected, got float",
- module="unittest")
- self.assertRaises(DeprecationWarning, struct.pack, format, number)
+ module=__name__
+ )
+ got = struct.pack(format, number)
+ nwarn = len(w)
+ self.assertEqual(nwarn, 1,
+ "expected exactly one warning from "
+ "struct.pack({!r}, {!r}); "
+ "got {} warnings".format(
+ format, number, nwarn))
+ expected = struct.pack(format, int(number))
+ self.assertEqual(got, expected)
 
 def test_isbigendian(self):
 self.assertEqual((struct.pack('=i', 1)[0] == chr(0)), ISBIGENDIAN)
@@ -291,17 +292,41 @@
 
 class NotAnIntOS:
 def __int__(self):
- return 10585
+ return 85
 
 def __long__(self):
 return -163L
 
- for badobject in ("a string", 3+42j, randrange,
- NotAnIntNS(), NotAnIntOS()):
- self.assertRaises(struct.error,
- struct.pack, format,
+ for badobject in ("a string", 3+42j, randrange):
+ self.assertRaises((TypeError, struct.error),
+ struct.pack, self.format,
 badobject)
 
+ # an attempt to convert a non-integer (with an
+ # implicit conversion via __int__) should succeed,
+ # with a DeprecationWarning
+ for nonint in NotAnIntNS(), NotAnIntOS():
+ with warnings.catch_warnings(record=True) as w:
+ # ignore everything except the
+ # DeprecationWarning we're looking for
+ warnings.simplefilter("ignore")
+ warnings.filterwarnings(
+ "always",
+ message=(".*integer argument expected, "
+ "got non-integer.*"),
+ category=DeprecationWarning,
+ module=__name__
+ )
+ got = struct.pack(self.format, nonint)
+ nwarn = len(w)
+ self.assertEqual(nwarn, 1,
+ "expected exactly one warning from "
+ "struct.pack({!r}, {!r}); "
+ "got {} warnings".format(
+ self.format, nonint, nwarn))
+ expected = struct.pack(self.format, int(nonint))
+ self.assertEqual(got, expected)
+
 byteorders = '', '@', '=', '<', '>', '!'
 for code in integer_codes:
 for byteorder in byteorders:
Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Sun Mar 7 17:24:45 2010
@@ -15,6 +15,18 @@
 Library
 -------
 
+Extension Modules
+-----------------
+
+- Issue #1530559: When passing a non-integer argument to struct.pack
+ with *any* integer format code (one of 'bBhHiIlLqQ'), struct.pack
+ attempts to use the argument's __int__ method to convert to an
+ integer before packing. It also produces a DeprecationWarning in
+ this case. (In Python 2.6, the behaviour was inconsistent: __int__
+ was used for some integer codes but not for others, and the set of
+ integer codes for which it was used differed between native packing
+ and standard packing.)
+
 
 What's New in Python 2.7 alpha 4?
 =================================
@@ -2192,6 +2204,10 @@
 TypeError used to be raised (with a confusing error message) for
 'I', 'L', '*B', '*H', '*I', '*L', and struct.error in other cases.
 
+ Note: as of Python 2.7 beta 1, the above is out of date. In 2.7
+ beta 1, any argument with an __int__ method can be packed, but use
+ of this feature triggers a DeprecationWarning.
+
 - Issue #4873: Fix resource leaks in error cases of pwd and grp.
 
 - Issue #4751: For hashlib algorithms provided by OpenSSL, the Python
Modified: python/trunk/Modules/_struct.c
==============================================================================
--- python/trunk/Modules/_struct.c	(original)
+++ python/trunk/Modules/_struct.c	Sun Mar 7 17:24:45 2010
@@ -17,7 +17,10 @@
 typedef int Py_ssize_t;
 #endif
 
-#define FLOAT_COERCE "integer argument expected, got float"
+/* warning messages */
+#define FLOAT_COERCE_WARN "integer argument expected, got float"
+#define NON_INTEGER_WARN "integer argument expected, got non-integer " \
+	"(implicit conversion using __int__ is deprecated)"
 
 
 /* The translation function for each format character is table driven */
@@ -104,21 +107,58 @@
 static PyObject *
 get_pylong(PyObject *v)
 {
+	PyObject *r;
 	assert(v != NULL);
-	if (PyInt_Check(v))
-		return PyLong_FromLong(PyInt_AS_LONG(v));
-	if (PyLong_Check(v)) {
-		Py_INCREF(v);
-		return v;
-	}
-	if (PyFloat_Check(v)) {
-		if (PyErr_WarnEx(PyExc_DeprecationWarning, FLOAT_COERCE, 1)<0)
+	if (!PyInt_Check(v) && !PyLong_Check(v)) {
+		PyNumberMethods *m;
+		/* Not an integer; try to use __int__ to convert to an
+		 integer. This behaviour is deprecated, and is removed in
+		 Python 3.x. */
+		m = Py_TYPE(v)->tp_as_number;
+		if (m != NULL && m->nb_int != NULL) {
+			/* Special case warning message for floats, for
+			 backwards compatibility. */
+			if (PyFloat_Check(v)) {
+				if (PyErr_WarnEx(PyExc_DeprecationWarning,
+						 FLOAT_COERCE_WARN, 1))
+					return NULL;
+			}
+			else {
+				if (PyErr_WarnEx(PyExc_DeprecationWarning,
+						 NON_INTEGER_WARN, 1))
+					return NULL;
+			}
+			v = m->nb_int(v);
+			if (v == NULL)
+				return NULL;
+			if (!PyInt_Check(v) && !PyLong_Check(v)) {
+				PyErr_SetString(PyExc_TypeError,
+				 "__int__ method returned non-integer");
+				return NULL;
+			}
+		}
+		else {
+			PyErr_SetString(StructError,
+					"cannot convert argument to integer");
 			return NULL;
-		return PyNumber_Long(v);
+		}
 	}
-	PyErr_SetString(StructError,
-			"cannot convert argument to long");
-	return NULL;
+	else
+		/* Ensure we own a reference to v. */
+		Py_INCREF(v);
+
+	if (PyInt_Check(v)) {
+		r = PyLong_FromLong(PyInt_AS_LONG(v));
+		Py_DECREF(v);
+	}
+	else if (PyLong_Check(v)) {
+		assert(PyLong_Check(v));
+		r = v;
+	}
+	else
+		assert(0); /* shouldn't ever get here */
+
+	return r;
 }
 
 /* Helper to convert a Python object to a C long. Sets an exception


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /