import platform
import test.test_support, unittest
from test.test_support import fcmp, have_unicode, TESTFN, unlink, \
- run_unittest, run_with_locale
+ run_unittest, run_with_locale, check_warnings
from operator import neg
import sys, warnings, cStringIO, random, fractions, UserDict
# Reject floats when it would require PyLongs to represent.
# (smaller floats still accepted, but deprecated)
self.assertRaises(TypeError, range, 1e100, 1e101, 1e101)
+ with check_warnings() as w:
+ warnings.simplefilter("always")
+ self.assertEqual(range(1.0), [0])
+ self.assertEqual(w.category, DeprecationWarning)
self.assertRaises(TypeError, range, 0, "spam")
self.assertRaises(TypeError, range, 0, 42, "spam")
self.assertRaises(OverflowError, range, -sys.maxint, 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, 1e100)
+
+ self.assertRaises(TypeError, range, 0, 1e100)
+ self.assertRaises(TypeError, range, 1e100, 0)
+ self.assertRaises(TypeError, range, 1e100, 1e100)
+
+ self.assertRaises(TypeError, range, 0, 0, 1e100)
+ self.assertRaises(TypeError, range, 0, 1e100, 1)
+ self.assertRaises(TypeError, range, 0, 1e100, 1e100)
+ self.assertRaises(TypeError, range, 1e100, 0, 1)
+ self.assertRaises(TypeError, range, 1e100, 0, 1e100)
+ self.assertRaises(TypeError, range, 1e100, 1e100, 1)
+ self.assertRaises(TypeError, range, 1e100, 1e100, 1e100)
+
def test_input_and_raw_input(self):
self.write_testfile()
fp = open(TESTFN, 'r')
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 #8417: Raise an OverflowError when an integer larger than sys.maxsize is
passed to bytearray.
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
* arguments are given. */
static PyObject *
handle_range_longs(PyObject *self, PyObject *args)
{
- PyObject *ilow;
+ PyObject *ilow = NULL;
PyObject *ihigh = NULL;
PyObject *istep = NULL;
+ PyObject *low = NULL;
+ PyObject *high = NULL;
+ PyObject *step = NULL;
+
PyObject *curnum = NULL;
PyObject *v = NULL;
long bign;
/* Figure out which way we were called, supply defaults, and be
* 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);
if (ihigh == NULL) {
ihigh = ilow;
ilow = NULL;
}
+
+ /* convert ihigh if necessary */
assert(ihigh != NULL);
- Py_INCREF(ihigh);
+ high = get_range_long_argument(ihigh, "end");
+ if (high == NULL)
+ goto Fail;
/* ihigh correct now; do ilow */
- if (ilow == NULL)
- ilow = zero;
- Py_INCREF(ilow);
-
- /* ilow and ihigh correct now; do istep */
- if (istep == NULL) {
- istep = PyLong_FromLong(1L);
- if (istep == NULL)
- goto Fail;
+ if (ilow == NULL) {
+ Py_INCREF(zero);
+ low = zero;
}
else {
- Py_INCREF(istep);
- }
-
- 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;
+ low = get_range_long_argument(ilow, "start");
+ if (low == NULL)
+ goto Fail;
}
- if (!PyInt_Check(ihigh) && !PyLong_Check(ihigh)) {
- PyErr_Format(PyExc_TypeError,
- "range() integer end argument expected, got %s.",
- ihigh->ob_type->tp_name);
+ /* ilow and ihigh correct now; do istep */
+ if (istep == NULL)
+ step = PyLong_FromLong(1);
+ else
+ step = get_range_long_argument(istep, "step");
+ if (step == NULL)
goto Fail;
- }
- if (!PyInt_Check(istep) && !PyLong_Check(istep)) {
- PyErr_Format(PyExc_TypeError,
- "range() integer step argument expected, got %s.",
- istep->ob_type->tp_name);
+ if (PyObject_Cmp(step, zero, &cmp_result) == -1)
goto Fail;
- }
- if (PyObject_Cmp(istep, zero, &cmp_result) == -1)
- goto Fail;
if (cmp_result == 0) {
PyErr_SetString(PyExc_ValueError,
"range() step argument must not be zero");
}
if (cmp_result > 0)
- bign = get_len_of_range_longs(ilow, ihigh, istep);
+ bign = get_len_of_range_longs(low, high, step);
else {
- PyObject *neg_istep = PyNumber_Negative(istep);
- if (neg_istep == NULL)
+ PyObject *neg_step = PyNumber_Negative(step);
+ if (neg_step == NULL)
goto Fail;
- bign = get_len_of_range_longs(ihigh, ilow, neg_istep);
- Py_DECREF(neg_istep);
+ bign = get_len_of_range_longs(high, low, neg_step);
+ Py_DECREF(neg_step);
}
n = (int)bign;
if (v == NULL)
goto Fail;
- curnum = ilow;
+ curnum = low;
Py_INCREF(curnum);
for (i = 0; i < n; i++) {
PyList_SET_ITEM(v, i, w);
- tmp_num = PyNumber_Add(curnum, istep);
+ tmp_num = PyNumber_Add(curnum, step);
if (tmp_num == NULL)
goto Fail;
Py_DECREF(curnum);
curnum = tmp_num;
}
- Py_DECREF(ilow);
- Py_DECREF(ihigh);
- Py_DECREF(istep);
+ Py_DECREF(low);
+ Py_DECREF(high);
+ Py_DECREF(step);
Py_DECREF(zero);
Py_DECREF(curnum);
return v;
Fail:
- Py_DECREF(ilow);
- Py_DECREF(ihigh);
- Py_XDECREF(istep);
+ Py_XDECREF(low);
+ Py_XDECREF(high);
+ Py_XDECREF(step);
Py_DECREF(zero);
Py_XDECREF(curnum);
Py_XDECREF(v);