(original) (raw)
Index: Misc/NEWS =================================================================== --- Misc/NEWS (revision 79661) +++ Misc/NEWS (working copy) @@ -270,6 +270,10 @@ Library ------- +- Issue #1530559: Changed the struct module to connvert non-integer types + to an integer type when packing, if the packed object has an '__index__' + method. + - Issue #6906: Tk should not set Unicode environment variables on Windows. - Issue #1054943: Fix ``unicodedata.normalize('NFC', text)`` for the Public Index: Doc/library/struct.rst =================================================================== --- Doc/library/struct.rst (revision 79661) +++ Doc/library/struct.rst (working copy) @@ -135,6 +135,14 @@ raised only for float arguments. +(3) + The type codes ``'b'``, ``'B'``, ``'h'``, ``'H'``, ``'i'``, ``'I'``, + ``'l'``, ``'L'``, ``'q'``, ``'Q'``, and ``'P'`` may be used to pack objects + other than Python integer types if the object being packed has a + :meth:`__index__` method. + + .. versionadded:: 2.7 + A format character may be preceded by an integral repeat count. For example, the format string ``'4h'`` means exactly the same as ``'hhhh'``. Index: Lib/test/test_struct.py =================================================================== --- Lib/test/test_struct.py (revision 79661) +++ Lib/test/test_struct.py (working copy) @@ -304,7 +304,7 @@ # an attempt to convert a non-integer (with an # implicit conversion via __int__) should succeed, # with a DeprecationWarning - for nonint in NotAnIntNS(), NotAnIntOS(): + for nonint in NotAnIntNS(),: #, NotAnIntOS(): with check_warnings((".*integer argument expected, got non" "-integer", DeprecationWarning)) as w: got = struct.pack(self.format, nonint) @@ -315,6 +315,23 @@ 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: Index: Modules/_struct.c =================================================================== --- Modules/_struct.c (revision 79661) +++ Modules/_struct.c (working copy) @@ -111,21 +111,36 @@ 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 an exception, 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)) { + v = PyNumber_Index(v); + if (v == NULL) + return NULL; + if (!PyInt_Check(v) && !PyLong_Check(v)) { + PyErr_SetString(PyExc_TypeError, + "__index__ method returned non-integer"); + return NULL; + } + } + else 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)) + 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 +148,15 @@ 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; } } else { PyErr_SetString(StructError, - "cannot convert argument to integer"); + "cannot convert argument " + "to integer"); return NULL; } }