From c593577a4a90b9c806c744b151efcb4e6b052aa4 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 3 Apr 2010 15:54:36 +0000 Subject: Merged revisions 79674 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r79674 | mark.dickinson | 2010-04-03 15:05:10 +0100 (Sat, 03 Apr 2010) | 3 lines Issue #8300: Let struct.pack use __index__ to convert and pack non-integers. Based on a patch by Meador Inge. ........ --- Doc/library/struct.rst | 9 +++++++++ Lib/test/test_struct.py | 17 +++++++++++++++++ Misc/NEWS | 5 +++++ Modules/_struct.c | 23 +++++++++++++++++++---- 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/Doc/library/struct.rst b/Doc/library/struct.rst index bee154f..ee949d9 100644 --- a/Doc/library/struct.rst +++ b/Doc/library/struct.rst @@ -119,6 +119,15 @@ Notes: the platform C compiler supports C :ctype:`long long`, or, on Windows, :ctype:`__int64`. They are always available in standard modes. +(4) + When attempting to pack a non-integer using any of the integer conversion + codes, if the non-integer has a :meth:`__index__` method then that method is + called to convert the argument to an integer before packing. + + .. versionchanged:: 3.2 + Use of the :meth:`__index__` method for non-integers is new in 3.2. + + A format character may be preceded by an integral repeat count. For example, the format string ``'4h'`` means exactly the same as ``'hhhh'``. diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index 39ccb27..3d67abd 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -282,6 +282,23 @@ class StructTest(unittest.TestCase): struct.pack, self.format, NotAnInt) + # 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") + for code in integer_codes: for byteorder in byteorders: if (byteorder in ('', '@') and code in ('q', 'Q') and diff --git a/Misc/NEWS b/Misc/NEWS index a1465d7..a15fa91 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -879,6 +879,11 @@ Library Extension Modules ----------------- +- 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 #8142: Update libffi to the 3.0.9 release. - Issue #6949: Allow the _dbm extension to be built with db 4.8.x. diff --git a/Modules/_struct.c b/Modules/_struct.c index 43321a4..e21487d 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -97,12 +97,27 @@ get_pylong(PyObject *v) { assert(v != NULL); if (!PyLong_Check(v)) { - PyErr_SetString(StructError, - "required argument is not an integer"); - return NULL; + /* Not an integer; try to use __index__ to convert. */ + if (PyIndex_Check(v)) { + v = PyNumber_Index(v); + if (v == NULL) + return NULL; + if (!PyLong_Check(v)) { + PyErr_SetString(PyExc_TypeError, + "__index__ method " + "returned non-integer"); + return NULL; + } + } + else { + PyErr_SetString(StructError, + "required argument is not an integer"); + return NULL; + } } + else + Py_INCREF(v); - Py_INCREF(v); return v; } -- cgit v0.12