Commit efbbb1c6 authored by Guido van Rossum's avatar Guido van Rossum

Patch by Chad Netzer (with significant change):

- range() now works even if the arguments are longs with magnitude
  larger than sys.maxint, as long as the total length of the sequence
  fits.  E.g., range(2**100, 2**101, 2**100) is the following list:
  [1267650600228229401496703205376L].  (SF patch #707427.)
parent a1ce93f8
...@@ -6,6 +6,8 @@ from test.test_support import fcmp, have_unicode, TESTFN, unlink ...@@ -6,6 +6,8 @@ from test.test_support import fcmp, have_unicode, TESTFN, unlink
import sys, warnings, cStringIO import sys, warnings, cStringIO
warnings.filterwarnings("ignore", "hex../oct.. of negative int", warnings.filterwarnings("ignore", "hex../oct.. of negative int",
FutureWarning, __name__) FutureWarning, __name__)
warnings.filterwarnings("ignore", "integer argument expected",
DeprecationWarning, "unittest")
class Squares: class Squares:
...@@ -925,10 +927,43 @@ class BuiltinTest(unittest.TestCase): ...@@ -925,10 +927,43 @@ class BuiltinTest(unittest.TestCase):
self.assertEqual(range(1, 10, 3), [1, 4, 7]) self.assertEqual(range(1, 10, 3), [1, 4, 7])
self.assertEqual(range(5, -5, -3), [5, 2, -1, -4]) self.assertEqual(range(5, -5, -3), [5, 2, -1, -4])
# Now test range() with longs
self.assertEqual(range(-2**100), [])
self.assertEqual(range(0, -2**100), [])
self.assertEqual(range(0, 2**100, -1), [])
self.assertEqual(range(0, 2**100, -1), [])
a = long(10 * sys.maxint)
b = long(100 * sys.maxint)
c = long(50 * sys.maxint)
self.assertEqual(range(a, a+2), [a, a+1])
self.assertEqual(range(a+2, a, -1L), [a+2, a+1])
self.assertEqual(range(a+4, a, -2), [a+4, a+2])
seq = range(a, b, c)
self.assert_(a in seq)
self.assert_(b not in seq)
self.assertEqual(len(seq), 2)
seq = range(b, a, -c)
self.assert_(b in seq)
self.assert_(a not in seq)
self.assertEqual(len(seq), 2)
seq = range(-a, -b, -c)
self.assert_(-a in seq)
self.assert_(-b not in seq)
self.assertEqual(len(seq), 2)
self.assertRaises(TypeError, range) self.assertRaises(TypeError, range)
self.assertRaises(TypeError, range, 1, 2, 3, 4) self.assertRaises(TypeError, range, 1, 2, 3, 4)
self.assertRaises(ValueError, range, 1, 2, 0) self.assertRaises(ValueError, range, 1, 2, 0)
# Reject floats when it would require PyLongs to represent.
# (smaller floats still accepted, but deprecated)
self.assertRaises(ValueError, range, 1e100, 1e101, 1e101)
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,11 @@ What's New in Python 2.3 beta 1? ...@@ -12,6 +12,11 @@ What's New in Python 2.3 beta 1?
Core and builtins Core and builtins
----------------- -----------------
- range() now works even if the arguments are longs with magnitude
larger than sys.maxint, as long as the total length of the sequence
fits. E.g., range(2**100, 2**101, 2**100) is the following list:
[1267650600228229401496703205376L]. (SF patch #707427.)
- Some horridly obscure problems were fixed involving interaction - Some horridly obscure problems were fixed involving interaction
between garbage collection and old-style classes with "ambitious" between garbage collection and old-style classes with "ambitious"
getattr hooks. If an old-style instance didn't have a __del__ method, getattr hooks. If an old-style instance didn't have a __del__ method,
......
...@@ -1252,6 +1252,186 @@ With two arguments, equivalent to x**y. With three arguments,\n\ ...@@ -1252,6 +1252,186 @@ With two arguments, equivalent to x**y. With three arguments,\n\
equivalent to (x**y) % z, but may be more efficient (e.g. for longs)."); equivalent to (x**y) % z, but may be more efficient (e.g. for longs).");
/* Return number of items in range (lo, hi, step), when arguments are
* PyInt or PyLong objects. step > 0 required. Return a value < 0 if
* & only if the true value is too large to fit in a signed long.
* Arguments MUST return 1 with either PyInt_Check() or
* PyLong_Check(). Return -1 when there is an error.
*/
static long
get_len_of_range_longs(PyObject *lo, PyObject *hi, PyObject *step)
{
/* -------------------------------------------------------------
Algorithm is equal to that of get_len_of_range(), but it operates
on PyObjects (which are assumed to be PyLong or PyInt objects).
---------------------------------------------------------------*/
long n;
PyObject *diff = NULL;
PyObject *one = NULL;
PyObject *tmp1 = NULL, *tmp2 = NULL, *tmp3 = NULL;
/* holds sub-expression evaluations */
/* if (lo >= hi), return length of 0. */
if (PyObject_Compare(lo, hi) >= 0)
return 0;
if ((one = PyLong_FromLong(1L)) == NULL)
goto Fail;
if ((tmp1 = PyNumber_Subtract(hi, lo)) == NULL)
goto Fail;
if ((diff = PyNumber_Subtract(tmp1, one)) == NULL)
goto Fail;
if ((tmp2 = PyNumber_FloorDivide(diff, step)) == NULL)
goto Fail;
if ((tmp3 = PyNumber_Add(tmp2, one)) == NULL)
goto Fail;
n = PyLong_AsLong(tmp3);
if (PyErr_Occurred()) { /* Check for Overflow */
PyErr_Clear();
goto Fail;
}
Py_DECREF(tmp3);
Py_DECREF(tmp2);
Py_DECREF(diff);
Py_DECREF(tmp1);
Py_DECREF(one);
return n;
Fail:
Py_XDECREF(tmp3);
Py_XDECREF(tmp2);
Py_XDECREF(diff);
Py_XDECREF(tmp1);
Py_XDECREF(one);
return -1;
}
/* 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 *ihigh;
PyObject *zero = NULL;
PyObject *istep = NULL;
PyObject *curnum = NULL;
PyObject *v = NULL;
long bign;
int i, n;
int cmp_result;
zero = PyLong_FromLong(0L);
if (zero == NULL)
return NULL;
ilow = zero; /* Default lower bound */
if (!PyArg_ParseTuple(args, "O", &ihigh, &istep)) {
PyErr_Clear();
if (!PyArg_ParseTuple(args,
"OO|O;range() requires 1-3 int arguments",
&ilow, &ihigh, &istep))
goto Fail;
}
if (!PyInt_Check(ilow) && !PyLong_Check(ilow)) {
PyErr_SetString(PyExc_ValueError,
"integer start argument expected, got float.");
goto Fail;
return NULL;
}
if (!PyInt_Check(ihigh) && !PyLong_Check(ihigh)) {
PyErr_SetString(PyExc_ValueError,
"integer end argument expected, got float.");
goto Fail;
return NULL;
}
/* If no istep was supplied, default to 1. */
if (istep == NULL) {
istep = PyLong_FromLong(1L);
if (istep == NULL)
goto Fail;
}
else {
if (!PyInt_Check(istep) && !PyLong_Check(istep)) {
PyErr_SetString(PyExc_ValueError,
"integer step argument expected, got float.");
goto Fail;
}
Py_INCREF(istep);
}
if (PyObject_Cmp(istep, zero, &cmp_result) == -1) {
goto Fail;
}
if (cmp_result == 0) {
PyErr_SetString(PyExc_ValueError,
"range() arg 3 must not be zero");
goto Fail;
}
if (cmp_result > 0)
bign = get_len_of_range_longs(ilow, ihigh, istep);
else {
PyObject *neg_istep = PyNumber_Negative(istep);
if (neg_istep == NULL)
goto Fail;
bign = get_len_of_range_longs(ihigh, ilow, neg_istep);
Py_DECREF(neg_istep);
}
n = (int)bign;
if (bign < 0 || (long)n != bign) {
PyErr_SetString(PyExc_OverflowError,
"range() result has too many items");
goto Fail;
}
v = PyList_New(n);
if (v == NULL)
goto Fail;
curnum = ilow;
Py_INCREF(curnum);
for (i = 0; i < n; i++) {
PyObject *w = PyNumber_Long(curnum);
PyObject *tmp_num;
if (w == NULL)
goto Fail;
PyList_SET_ITEM(v, i, w);
tmp_num = PyNumber_Add(curnum, istep);
if (tmp_num == NULL)
goto Fail;
Py_DECREF(curnum);
curnum = tmp_num;
}
Py_DECREF(curnum);
Py_DECREF(istep);
Py_DECREF(zero);
return v;
Fail:
Py_XDECREF(curnum);
Py_XDECREF(istep);
Py_XDECREF(zero);
Py_XDECREF(v);
return NULL;
}
/* Return number of items in range/xrange (lo, hi, step). step > 0 /* Return number of items in range/xrange (lo, hi, step). step > 0
* required. Return a value < 0 if & only if the true value is too * required. Return a value < 0 if & only if the true value is too
* large to fit in a signed long. * large to fit in a signed long.
...@@ -1293,17 +1473,22 @@ builtin_range(PyObject *self, PyObject *args) ...@@ -1293,17 +1473,22 @@ builtin_range(PyObject *self, PyObject *args)
if (PyTuple_Size(args) <= 1) { if (PyTuple_Size(args) <= 1) {
if (!PyArg_ParseTuple(args, if (!PyArg_ParseTuple(args,
"l;range() requires 1-3 int arguments", "l;range() requires 1-3 int arguments",
&ihigh)) &ihigh)) {
return NULL; PyErr_Clear();
return handle_range_longs(self, args);
}
} }
else { else {
if (!PyArg_ParseTuple(args, if (!PyArg_ParseTuple(args,
"ll|l;range() requires 1-3 int arguments", "ll|l;range() requires 1-3 int arguments",
&ilow, &ihigh, &istep)) &ilow, &ihigh, &istep)) {
return NULL; PyErr_Clear();
return handle_range_longs(self, args);
}
} }
if (istep == 0) { if (istep == 0) {
PyErr_SetString(PyExc_ValueError, "range() arg 3 must not be zero"); PyErr_SetString(PyExc_ValueError,
"range() arg 3 must not be zero");
return NULL; return NULL;
} }
if (istep > 0) if (istep > 0)
......
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