Commit 4846a8e8 authored by Mark Dickinson's avatar Mark Dickinson

Issue #8300: Let struct.pack use __index__ to convert and pack non-integers.

Based on a patch by Meador Inge.
parent ae509520
......@@ -125,9 +125,14 @@ Notes:
(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
......
......@@ -315,6 +315,24 @@ class StructTest(unittest.TestCase):
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:
......
......@@ -173,6 +173,11 @@ Extension Modules
- 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
......
......@@ -107,25 +107,50 @@ static char *integer_codes = "bBhHiIlLqQ";
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 @@ get_pylong(PyObject *v)
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;
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment