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

mark.dickinson python-checkins at python.org
Sat Apr 3 16:05:10 CEST 2010


Author: mark.dickinson
Date: Sat Apr  3 16:05:10 2010
New Revision: 79674

Log:
Issue #8300:  Let struct.pack use __index__ to convert and pack non-integers.
Based on a patch by Meador Inge.


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	Sat Apr  3 16:05:10 2010
@@ -125,9 +125,14 @@
 
 (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`.
+   codes, if the non-integer has a :meth:`__index__` method then that method is
+   called to convert the argument to an integer before packing.  If no
+   :meth:`__index__` method exists, or the call to :meth:`__index__` raises
+   :exc:`TypeError`, then the :meth:`__int__` method is tried.  However, the use
+   of `__int__` is deprecated, and will raise :exc:`DeprecationWarning`.
+
+   .. versionchanged:: 2.7
+      Use of the :meth:`__index__` method for non-integers is new in 2.7.
 
    .. versionchanged:: 2.7
       Prior to version 2.7, not all integer conversion codes would use the

Modified: python/trunk/Lib/test/test_struct.py
==============================================================================
--- python/trunk/Lib/test/test_struct.py	(original)
+++ python/trunk/Lib/test/test_struct.py	Sat Apr  3 16:05:10 2010
@@ -315,6 +315,24 @@
                     expected = struct.pack(self.format, int(nonint))
                     self.assertEqual(got, expected)
 
+                # Objects with an '__index__' method should be allowed
+                # to pack as integers.
+                class Indexable(object):
+                    def __init__(self, value):
+                        self._value = value
+
+                    def __index__(self):
+                        return self._value
+
+                for obj in (Indexable(0), Indexable(10), Indexable(17),
+                            Indexable(42), Indexable(100), Indexable(127)):
+                    try:
+                        struct.pack(format, obj)
+                    except:
+                        self.fail("integer code pack failed on object "
+                                  "with '__index__' method")
+
+
         byteorders = '', '@', '=', '<', '>', '!'
         for code in integer_codes:
             for byteorder in byteorders:

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Sat Apr  3 16:05:10 2010
@@ -173,6 +173,11 @@
 
 - Issue #8142: Update libffi to the 3.0.9 release.
 
+- Issue #8300: When passing a non-integer argument to struct.pack with any
+  integer format code, struct.pack first attempts to convert the non-integer
+  using its __index__ method.  If that method is non-existent or raises
+  TypeError it goes on to try the __int__ method, as described below.
+
 - 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

Modified: python/trunk/Modules/_struct.c
==============================================================================
--- python/trunk/Modules/_struct.c	(original)
+++ python/trunk/Modules/_struct.c	Sat Apr  3 16:05:10 2010
@@ -107,25 +107,50 @@
 static PyObject *
 get_pylong(PyObject *v)
 {
-	PyObject *r;
+	PyObject *r, *w;
+	int converted = 0;
 	assert(v != NULL);
 	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
+		/* Not an integer; first try to use __index__ to
+		   convert to an integer.  If the __index__ method
+		   doesn't exist, or raises a TypeError, try __int__.
+		   Use of the latter is deprecated, and will fail in
 		   Python 3.x. */
+
 		m = Py_TYPE(v)->tp_as_number;
-		if (m != NULL && m->nb_int != NULL) {
+		if (PyIndex_Check(v)) {
+			w = PyNumber_Index(v);
+			if (w != NULL) {
+				v = w;
+				if (!PyInt_Check(v) && !PyLong_Check(v)) {
+					PyErr_SetString(PyExc_TypeError,
+							"__index__ method "
+							"returned non-integer");
+					return NULL;
+				}
+				/* successfully converted to an integer */
+				converted = 1;
+			}
+			else if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+				PyErr_Clear();
+			}
+			else
+				return NULL;
+		}
+		if (!converted && 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))
+				if (PyErr_WarnEx(
+					    PyExc_DeprecationWarning,
+					    FLOAT_COERCE_WARN, 1))
 					return NULL;
 			}
 			else {
-				if (PyErr_WarnEx(PyExc_DeprecationWarning,
-						 NON_INTEGER_WARN, 1))
+				if (PyErr_WarnEx(
+					    PyExc_DeprecationWarning,
+					    NON_INTEGER_WARN, 1))
 					return NULL;
 			}
 			v = m->nb_int(v);
@@ -133,13 +158,16 @@
 				return NULL;
 			if (!PyInt_Check(v) && !PyLong_Check(v)) {
 				PyErr_SetString(PyExc_TypeError,
-				  "__int__ method returned non-integer");
+						"__int__ method returned "
+						"non-integer");
 				return NULL;
 			}
+			converted = 1;
 		}
-		else {
+		if (!converted) {
 			PyErr_SetString(StructError,
-					"cannot convert argument to integer");
+					"cannot convert argument "
+					"to integer");
 			return NULL;
 		}
 	}


More information about the Python-checkins mailing list