mirror of
https://github.com/python/cpython.git
synced 2025-07-29 14:15:07 +00:00
Issue #8300: Let struct.pack use __index__ to convert and pack non-integers.
Based on a patch by Meador Inge.
This commit is contained in:
parent
ae509520de
commit
4846a8e828
4 changed files with 70 additions and 14 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue