diff options
author | Mark Dickinson <dickinsm@gmail.com> | 2010-03-07 16:24:45 (GMT) |
---|---|---|
committer | Mark Dickinson <dickinsm@gmail.com> | 2010-03-07 16:24:45 (GMT) |
commit | 154b7ad07e775f15a0a556c7ec3ae5ab5762ae1e (patch) | |
tree | 8102aad436b0b60b7e1a98a376a1257546019898 /Modules | |
parent | c083864fc8a03a045dfc0200e8456996395ad4dc (diff) | |
download | cpython-154b7ad07e775f15a0a556c7ec3ae5ab5762ae1e.zip cpython-154b7ad07e775f15a0a556c7ec3ae5ab5762ae1e.tar.gz cpython-154b7ad07e775f15a0a556c7ec3ae5ab5762ae1e.tar.bz2 |
Issue #1530559: When packing a non-integer with any integer conversion
code using struct.pack, attempt to convert to an integer first using
the argument's __int__ method (if present). Also raise a
DeprecationWarning for any such usage of __int__.
This fixes a regression from 2.6, where some (but not all) integer
conversion codes already used __int__.
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/_struct.c | 64 |
1 files changed, 52 insertions, 12 deletions
diff --git a/Modules/_struct.c b/Modules/_struct.c index 97f2f75..e8fe67d 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -17,7 +17,10 @@ static PyTypeObject PyStructType; typedef int Py_ssize_t; #endif -#define FLOAT_COERCE "integer argument expected, got float" +/* warning messages */ +#define FLOAT_COERCE_WARN "integer argument expected, got float" +#define NON_INTEGER_WARN "integer argument expected, got non-integer " \ + "(implicit conversion using __int__ is deprecated)" /* The translation function for each format character is table driven */ @@ -104,21 +107,58 @@ static char *integer_codes = "bBhHiIlLqQ"; static PyObject * get_pylong(PyObject *v) { + PyObject *r; assert(v != NULL); - if (PyInt_Check(v)) - return PyLong_FromLong(PyInt_AS_LONG(v)); - if (PyLong_Check(v)) { + 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 + Python 3.x. */ + m = Py_TYPE(v)->tp_as_number; + 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)) + return NULL; + } + else { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + NON_INTEGER_WARN, 1)) + return NULL; + } + v = m->nb_int(v); + if (v == NULL) + return NULL; + if (!PyInt_Check(v) && !PyLong_Check(v)) { + PyErr_SetString(PyExc_TypeError, + "__int__ method returned non-integer"); + return NULL; + } + } + else { + PyErr_SetString(StructError, + "cannot convert argument to integer"); + return NULL; + } + } + else + /* Ensure we own a reference to v. */ Py_INCREF(v); - return v; + + if (PyInt_Check(v)) { + r = PyLong_FromLong(PyInt_AS_LONG(v)); + Py_DECREF(v); } - if (PyFloat_Check(v)) { - if (PyErr_WarnEx(PyExc_DeprecationWarning, FLOAT_COERCE, 1)<0) - return NULL; - return PyNumber_Long(v); + else if (PyLong_Check(v)) { + assert(PyLong_Check(v)); + r = v; } - PyErr_SetString(StructError, - "cannot convert argument to long"); - return NULL; + else + assert(0); /* shouldn't ever get here */ + + return r; } /* Helper to convert a Python object to a C long. Sets an exception |