mirror of
https://github.com/python/cpython.git
synced 2025-07-29 06:05:00 +00:00
Issue #1533: fix inconsistency in range function argument processing:
any non-float non-integer argument is now converted to an integer (if possible) using its __int__ method. Previously, only small arguments were treated this way; larger arguments (those whose __int__ was outside the range of a C long) would produce a TypeError. Patch by Alexander Belopolsky (with minor modifications).
This commit is contained in:
parent
4f96f5ffc6
commit
a8d2668818
3 changed files with 129 additions and 46 deletions
|
@ -1080,6 +1080,56 @@ class BuiltinTest(unittest.TestCase):
|
||||||
self.assertRaises(OverflowError, range, -sys.maxint, sys.maxint)
|
self.assertRaises(OverflowError, range, -sys.maxint, sys.maxint)
|
||||||
self.assertRaises(OverflowError, range, 0, 2*sys.maxint)
|
self.assertRaises(OverflowError, range, 0, 2*sys.maxint)
|
||||||
|
|
||||||
|
bignum = 2*sys.maxint
|
||||||
|
smallnum = 42
|
||||||
|
# Old-style user-defined class with __int__ method
|
||||||
|
class I0:
|
||||||
|
def __init__(self, n):
|
||||||
|
self.n = int(n)
|
||||||
|
def __int__(self):
|
||||||
|
return self.n
|
||||||
|
self.assertEqual(range(I0(bignum), I0(bignum + 1)), [bignum])
|
||||||
|
self.assertEqual(range(I0(smallnum), I0(smallnum + 1)), [smallnum])
|
||||||
|
|
||||||
|
# New-style user-defined class with __int__ method
|
||||||
|
class I1(object):
|
||||||
|
def __init__(self, n):
|
||||||
|
self.n = int(n)
|
||||||
|
def __int__(self):
|
||||||
|
return self.n
|
||||||
|
self.assertEqual(range(I1(bignum), I1(bignum + 1)), [bignum])
|
||||||
|
self.assertEqual(range(I1(smallnum), I1(smallnum + 1)), [smallnum])
|
||||||
|
|
||||||
|
# New-style user-defined class with failing __int__ method
|
||||||
|
class IX(object):
|
||||||
|
def __int__(self):
|
||||||
|
raise RuntimeError
|
||||||
|
self.assertRaises(RuntimeError, range, IX())
|
||||||
|
|
||||||
|
# New-style user-defined class with invalid __int__ method
|
||||||
|
class IN(object):
|
||||||
|
def __int__(self):
|
||||||
|
return "not a number"
|
||||||
|
self.assertRaises(TypeError, range, IN())
|
||||||
|
|
||||||
|
# Exercise various combinations of bad arguments, to check
|
||||||
|
# refcounting logic
|
||||||
|
self.assertRaises(TypeError, range, 0.0)
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, range, 0, 0.0)
|
||||||
|
self.assertRaises(TypeError, range, 0.0, 0)
|
||||||
|
self.assertRaises(TypeError, range, 0.0, 0.0)
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, range, 0, 0, 1.0)
|
||||||
|
self.assertRaises(TypeError, range, 0, 0.0, 1)
|
||||||
|
self.assertRaises(TypeError, range, 0, 0.0, 1.0)
|
||||||
|
self.assertRaises(TypeError, range, 0.0, 0, 1)
|
||||||
|
self.assertRaises(TypeError, range, 0.0, 0, 1.0)
|
||||||
|
self.assertRaises(TypeError, range, 0.0, 0.0, 1)
|
||||||
|
self.assertRaises(TypeError, range, 0.0, 0.0, 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_input_and_raw_input(self):
|
def test_input_and_raw_input(self):
|
||||||
self.write_testfile()
|
self.write_testfile()
|
||||||
fp = open(TESTFN, 'r')
|
fp = open(TESTFN, 'r')
|
||||||
|
|
|
@ -12,6 +12,12 @@ What's New in Python 2.7 beta 2?
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #1533: fix inconsistency in range function argument
|
||||||
|
processing: any non-float non-integer argument is now converted to
|
||||||
|
an integer (if possible) using its __int__ method. Previously, only
|
||||||
|
small arguments were treated this way; larger arguments (those whose
|
||||||
|
__int__ was outside the range of a C long) would produce a TypeError.
|
||||||
|
|
||||||
- Issue #8202: sys.argv[0] is now set to '-m' instead of '-c' when
|
- Issue #8202: sys.argv[0] is now set to '-m' instead of '-c' when
|
||||||
searching for the module file to be executed with the -m command
|
searching for the module file to be executed with the -m command
|
||||||
line option
|
line option
|
||||||
|
|
|
@ -1746,15 +1746,54 @@ get_len_of_range_longs(PyObject *lo, PyObject *hi, PyObject *step)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Helper function for handle_range_longs. If arg is int or long
|
||||||
|
object, returns it with incremented reference count. If arg is
|
||||||
|
float, raises type error. As a last resort, creates a new int by
|
||||||
|
calling arg type's nb_int method if it is defined. Returns NULL
|
||||||
|
and sets exception on error.
|
||||||
|
|
||||||
|
Returns a new reference to an int object. */
|
||||||
|
static PyObject *
|
||||||
|
get_range_long_argument(PyObject *arg, const char *name)
|
||||||
|
{
|
||||||
|
PyObject *v;
|
||||||
|
PyNumberMethods *nb;
|
||||||
|
if (PyInt_Check(arg) || PyLong_Check(arg)) {
|
||||||
|
Py_INCREF(arg);
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
if (PyFloat_Check(arg) ||
|
||||||
|
(nb = Py_TYPE(arg)->tp_as_number) == NULL ||
|
||||||
|
nb->nb_int == NULL) {
|
||||||
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"range() integer %s argument expected, got %s.",
|
||||||
|
name, arg->ob_type->tp_name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
v = nb->nb_int(arg);
|
||||||
|
if (v == NULL)
|
||||||
|
return NULL;
|
||||||
|
if (PyInt_Check(v) || PyLong_Check(v))
|
||||||
|
return v;
|
||||||
|
Py_DECREF(v);
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"__int__ should return int object");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* An extension of builtin_range() that handles the case when PyLong
|
/* An extension of builtin_range() that handles the case when PyLong
|
||||||
* arguments are given. */
|
* arguments are given. */
|
||||||
static PyObject *
|
static PyObject *
|
||||||
handle_range_longs(PyObject *self, PyObject *args)
|
handle_range_longs(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
PyObject *ilow;
|
PyObject *ilow = NULL;
|
||||||
PyObject *ihigh = NULL;
|
PyObject *ihigh = NULL;
|
||||||
PyObject *istep = NULL;
|
PyObject *istep = NULL;
|
||||||
|
|
||||||
|
PyObject *low = NULL;
|
||||||
|
PyObject *high = NULL;
|
||||||
|
PyObject *step = NULL;
|
||||||
|
|
||||||
PyObject *curnum = NULL;
|
PyObject *curnum = NULL;
|
||||||
PyObject *v = NULL;
|
PyObject *v = NULL;
|
||||||
long bign;
|
long bign;
|
||||||
|
@ -1773,7 +1812,7 @@ handle_range_longs(PyObject *self, PyObject *args)
|
||||||
|
|
||||||
/* Figure out which way we were called, supply defaults, and be
|
/* Figure out which way we were called, supply defaults, and be
|
||||||
* sure to incref everything so that the decrefs at the end
|
* sure to incref everything so that the decrefs at the end
|
||||||
* are correct.
|
* are correct. NB: ilow, ihigh and istep are borrowed references.
|
||||||
*/
|
*/
|
||||||
assert(ilow != NULL);
|
assert(ilow != NULL);
|
||||||
if (ihigh == NULL) {
|
if (ihigh == NULL) {
|
||||||
|
@ -1781,47 +1820,35 @@ handle_range_longs(PyObject *self, PyObject *args)
|
||||||
ihigh = ilow;
|
ihigh = ilow;
|
||||||
ilow = NULL;
|
ilow = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* convert ihigh if necessary */
|
||||||
assert(ihigh != NULL);
|
assert(ihigh != NULL);
|
||||||
Py_INCREF(ihigh);
|
high = get_range_long_argument(ihigh, "end");
|
||||||
|
if (high == NULL)
|
||||||
|
goto Fail;
|
||||||
|
|
||||||
/* ihigh correct now; do ilow */
|
/* ihigh correct now; do ilow */
|
||||||
if (ilow == NULL)
|
if (ilow == NULL) {
|
||||||
ilow = zero;
|
Py_INCREF(zero);
|
||||||
Py_INCREF(ilow);
|
low = zero;
|
||||||
|
|
||||||
/* ilow and ihigh correct now; do istep */
|
|
||||||
if (istep == NULL) {
|
|
||||||
istep = PyLong_FromLong(1L);
|
|
||||||
if (istep == NULL)
|
|
||||||
goto Fail;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Py_INCREF(istep);
|
low = get_range_long_argument(ilow, "start");
|
||||||
}
|
if (low == NULL)
|
||||||
|
|
||||||
if (!PyInt_Check(ilow) && !PyLong_Check(ilow)) {
|
|
||||||
PyErr_Format(PyExc_TypeError,
|
|
||||||
"range() integer start argument expected, got %s.",
|
|
||||||
ilow->ob_type->tp_name);
|
|
||||||
goto Fail;
|
goto Fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PyInt_Check(ihigh) && !PyLong_Check(ihigh)) {
|
/* ilow and ihigh correct now; do istep */
|
||||||
PyErr_Format(PyExc_TypeError,
|
if (istep == NULL)
|
||||||
"range() integer end argument expected, got %s.",
|
step = PyLong_FromLong(1);
|
||||||
ihigh->ob_type->tp_name);
|
else
|
||||||
|
step = get_range_long_argument(istep, "step");
|
||||||
|
if (step == NULL)
|
||||||
goto Fail;
|
goto Fail;
|
||||||
}
|
|
||||||
|
|
||||||
if (!PyInt_Check(istep) && !PyLong_Check(istep)) {
|
if (PyObject_Cmp(step, zero, &cmp_result) == -1)
|
||||||
PyErr_Format(PyExc_TypeError,
|
|
||||||
"range() integer step argument expected, got %s.",
|
|
||||||
istep->ob_type->tp_name);
|
|
||||||
goto Fail;
|
goto Fail;
|
||||||
}
|
|
||||||
|
|
||||||
if (PyObject_Cmp(istep, zero, &cmp_result) == -1)
|
|
||||||
goto Fail;
|
|
||||||
if (cmp_result == 0) {
|
if (cmp_result == 0) {
|
||||||
PyErr_SetString(PyExc_ValueError,
|
PyErr_SetString(PyExc_ValueError,
|
||||||
"range() step argument must not be zero");
|
"range() step argument must not be zero");
|
||||||
|
@ -1829,13 +1856,13 @@ handle_range_longs(PyObject *self, PyObject *args)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmp_result > 0)
|
if (cmp_result > 0)
|
||||||
bign = get_len_of_range_longs(ilow, ihigh, istep);
|
bign = get_len_of_range_longs(low, high, step);
|
||||||
else {
|
else {
|
||||||
PyObject *neg_istep = PyNumber_Negative(istep);
|
PyObject *neg_step = PyNumber_Negative(step);
|
||||||
if (neg_istep == NULL)
|
if (neg_step == NULL)
|
||||||
goto Fail;
|
goto Fail;
|
||||||
bign = get_len_of_range_longs(ihigh, ilow, neg_istep);
|
bign = get_len_of_range_longs(high, low, neg_step);
|
||||||
Py_DECREF(neg_istep);
|
Py_DECREF(neg_step);
|
||||||
}
|
}
|
||||||
|
|
||||||
n = (Py_ssize_t)bign;
|
n = (Py_ssize_t)bign;
|
||||||
|
@ -1849,7 +1876,7 @@ handle_range_longs(PyObject *self, PyObject *args)
|
||||||
if (v == NULL)
|
if (v == NULL)
|
||||||
goto Fail;
|
goto Fail;
|
||||||
|
|
||||||
curnum = ilow;
|
curnum = low;
|
||||||
Py_INCREF(curnum);
|
Py_INCREF(curnum);
|
||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
|
@ -1860,24 +1887,24 @@ handle_range_longs(PyObject *self, PyObject *args)
|
||||||
|
|
||||||
PyList_SET_ITEM(v, i, w);
|
PyList_SET_ITEM(v, i, w);
|
||||||
|
|
||||||
tmp_num = PyNumber_Add(curnum, istep);
|
tmp_num = PyNumber_Add(curnum, step);
|
||||||
if (tmp_num == NULL)
|
if (tmp_num == NULL)
|
||||||
goto Fail;
|
goto Fail;
|
||||||
|
|
||||||
Py_DECREF(curnum);
|
Py_DECREF(curnum);
|
||||||
curnum = tmp_num;
|
curnum = tmp_num;
|
||||||
}
|
}
|
||||||
Py_DECREF(ilow);
|
Py_DECREF(low);
|
||||||
Py_DECREF(ihigh);
|
Py_DECREF(high);
|
||||||
Py_DECREF(istep);
|
Py_DECREF(step);
|
||||||
Py_DECREF(zero);
|
Py_DECREF(zero);
|
||||||
Py_DECREF(curnum);
|
Py_DECREF(curnum);
|
||||||
return v;
|
return v;
|
||||||
|
|
||||||
Fail:
|
Fail:
|
||||||
Py_DECREF(ilow);
|
Py_XDECREF(low);
|
||||||
Py_DECREF(ihigh);
|
Py_XDECREF(high);
|
||||||
Py_XDECREF(istep);
|
Py_XDECREF(step);
|
||||||
Py_DECREF(zero);
|
Py_DECREF(zero);
|
||||||
Py_XDECREF(curnum);
|
Py_XDECREF(curnum);
|
||||||
Py_XDECREF(v);
|
Py_XDECREF(v);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue