Index: Objects/rangeobject.c =================================================================== --- Objects/rangeobject.c (revision 62481) +++ Objects/rangeobject.c (working copy) @@ -14,6 +14,7 @@ PyObject *start; PyObject *stop; PyObject *step; + PyObject *end; /* one step beyond the stop */ } rangeobject; /* Helper function for validating step. Always returns a new reference or @@ -22,10 +23,6 @@ static PyObject * validate_step(PyObject *step) { - /* No step specified, use a step of 1. */ - if (!step) - return PyLong_FromLong(1); - step = PyNumber_Index(step); if (step) { Py_ssize_t istep = PyNumber_AsSsize_t(step, NULL); @@ -52,7 +49,7 @@ range_new(PyTypeObject *type, PyObject *args, PyObject *kw) { rangeobject *obj = NULL; - PyObject *start = NULL, *stop = NULL, *step = NULL; + PyObject *start = NULL, *stop = NULL, *step = NULL, *end = NULL; if (!_PyArg_NoKeywords("range()", kw)) return NULL; @@ -67,17 +64,59 @@ step = PyLong_FromLong(1); if (!start || !step) goto Fail; + end = stop; + Py_INCREF(end); } else { if (!PyArg_UnpackTuple(args, "range", 2, 3, &start, &stop, &step)) goto Fail; - /* Convert borrowed refs to owned refs */ - start = PyNumber_Index(start); - stop = PyNumber_Index(stop); - step = validate_step(step); - if (!start || !stop || !step) + + if (!step) { + /* Convert borrowed refs to owned refs */ + start = PyNumber_Index(start); + stop = PyNumber_Index(stop); + + /* No step specified, use a step of 1. + */ + step = PyLong_FromLong(1); + if (!start || !stop || !step) + goto Fail; + + end = stop; + Py_INCREF(end); + } + else { + PyObject *diff, *divmod, *mod; + + /* Convert borrowed refs to owned refs */ + start = PyNumber_Index(start); + stop = PyNumber_Index(stop); + step = validate_step(step); + if (!start || !stop || !step) + goto Fail; + + /* end value = stop + (stop-start)%step + */ + diff = PyNumber_Subtract(stop, start); + if (!diff) + goto Fail; + + divmod = PyNumber_Divmod(diff, step); + Py_DECREF(diff); + if (!divmod) + goto Fail; + + mod = PyTuple_GetItem(divmod, 1); /* borrowed reference */ + if (!mod) { + Py_DECREF(divmod); + goto Fail; + } + end = PyNumber_Add(stop, mod); + Py_DECREF(divmod); + } + if (!end) goto Fail; } @@ -87,12 +126,14 @@ obj->start = start; obj->stop = stop; obj->step = step; + obj->end = end; return (PyObject *) obj; Fail: Py_XDECREF(start); Py_XDECREF(stop); Py_XDECREF(step); + Py_XDECREF(end); return NULL; } @@ -107,6 +148,7 @@ Py_DECREF(r->start); Py_DECREF(r->stop); Py_DECREF(r->step); + Py_DECREF(r->end); PyObject_Del(r); } @@ -210,7 +252,7 @@ Py_ssize_t len = range_length(r); PyObject *rem, *incr, *result; - /* XXX(nnorwitz): should negative indices be supported? */ + /* No need to support negative indices */ /* XXX(nnorwitz): should support range[x] where x > PY_SSIZE_T_MAX? */ if (i < 0 || i >= len) { if (!PyErr_Occurred()) @@ -233,6 +275,44 @@ } static PyObject * +range_subscript(rangeobject *r, PyObject *index) +{ + PyObject *incr; + PyObject *result = NULL; + PyObject *zero = NULL; + int compare; + + /* Convert to owned reference */ + index = PyNumber_Index(index); + if (!index) + goto Fail; + + zero = PyLong_FromLong(0); + if (!zero) + goto Fail; + + compare = PyObject_RichCompareBool(index, zero, Py_LT); + if (compare < 0) + return NULL; + + incr = PyNumber_Multiply(index, r->step); + if (!incr) + return NULL; + if (compare == 1) { + /* negative index, start from end */ + result = PyNumber_Add(r->end, incr); + } else { + result = PyNumber_Add(r->start, incr); + } + Py_DECREF(incr); + +Fail: + Py_XDECREF(index); + Py_XDECREF(zero); + return result; +} + +static PyObject * range_repr(rangeobject *r) { Py_ssize_t istep; @@ -260,6 +340,12 @@ 0, /* sq_slice */ }; +static PyMappingMethods range_as_mapping = { + (lenfunc)range_length, /* mp_length */ + (binaryfunc)range_subscript, /* mp_subscript */ + 0, /* mp_ass_subscript */ +}; + static PyObject * range_iter(PyObject *seq); static PyObject * range_reverse(PyObject *seq); @@ -285,7 +371,7 @@ (reprfunc)range_repr, /* tp_repr */ 0, /* tp_as_number */ &range_as_sequence, /* tp_as_sequence */ - 0, /* tp_as_mapping */ + &range_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ @@ -588,8 +674,8 @@ it->len = it->index = NULL; - /* Calculate length: (r->stop - r->start) / r->step */ - tmp = PyNumber_Subtract(r->stop, r->start); + /* Calculate length: (r->end - r->start) / r->step */ + tmp = PyNumber_Subtract(r->end, r->start); if (!tmp) goto create_failure; len = PyNumber_FloorDivide(tmp, r->step); @@ -613,7 +699,6 @@ { rangeobject *range = (rangeobject*) seq; longrangeiterobject *it; - PyObject *one, *sum, *diff, *len = NULL, *product; long lstart, lstop, lstep; /* XXX(nnorwitz): do the calc for the new start/stop first, @@ -646,27 +731,8 @@ if (it == NULL) return NULL; - /* start + (len - 1) * step */ - len = range_length_obj(range); - if (!len) - goto create_failure; - - one = PyLong_FromLong(1); - if (!one) - goto create_failure; - - diff = PyNumber_Subtract(len, one); - Py_DECREF(one); - if (!diff) - goto create_failure; - - product = PyNumber_Multiply(len, range->step); - if (!product) - goto create_failure; - - sum = PyNumber_Add(range->start, product); - Py_DECREF(product); - it->start = sum; + /* end - step */ + it->start = PyNumber_Subtract(range->end, range->step); if (!it->start) goto create_failure; it->step = PyNumber_Negative(range->step); @@ -676,8 +742,7 @@ return NULL; } - /* Steal reference to len. */ - it->len = len; + it->len = range_length_obj(range); it->index = PyLong_FromLong(0); if (!it->index) { @@ -688,7 +753,6 @@ return (PyObject *)it; create_failure: - Py_XDECREF(len); PyObject_Del(it); return NULL; } Index: Lib/test/test_range.py =================================================================== --- Lib/test/test_range.py (revision 62481) +++ Lib/test/test_range.py (working copy) @@ -61,6 +61,19 @@ self.assertEqual(repr(range(1, 2)), 'range(1, 2)') self.assertEqual(repr(range(1, 2, 3)), 'range(1, 2, 3)') + def test_big_ranges(self): + # A not-so-long range with big values + r = range(10**20-10**8, 10**20, 2) + self.assertEqual(len(r), 5e7) + self.assertEqual(r[-1], 10**20 - 2) + + # A really big range + r = range(10**20, 10**21, 10**10) + self.assertRaises(OverflowError, len, r) + self.assertEqual(r[-1], 10**21 - 1 * 10**10) + self.assertEqual(r[-2], 10**21 - 2 * 10**10) + + def test_main(): test.test_support.run_unittest(RangeTest) Index: Lib/test/test_builtin.py =================================================================== --- Lib/test/test_builtin.py (revision 62481) +++ Lib/test/test_builtin.py (working copy) @@ -1539,7 +1539,6 @@ self.assertEqual(list(range(1, 10, 3)), [1, 4, 7]) #self.assertEqual(list(range(5, -5, -3)), [5, 2, -1, -4]) - """ XXX(nnorwitz): # Now test range() with longs self.assertEqual(list(range(-2**100)), []) self.assertEqual(list(range(0, -2**100)), []) @@ -1572,27 +1571,15 @@ self.assertRaises(TypeError, range) self.assertRaises(TypeError, range, 1, 2, 3, 4) self.assertRaises(ValueError, range, 1, 2, 0) - self.assertRaises(ValueError, range, a, a + 1, int(0)) + self.assertRaises(ValueError, range, a, a + 1, 0) - class badzero(int): - def __eq__(self, other): - raise RuntimeError - __ne__ = __lt__ = __gt__ = __le__ = __ge__ = __eq__ - - # XXX This won't (but should!) raise RuntimeError if a is an int... - self.assertRaises(RuntimeError, range, a, a + 1, badzero(1)) - """ - - # Reject floats when it would require PyLongs to represent. - # (smaller floats still accepted, but deprecated) + # Reject all floats. self.assertRaises(TypeError, range, 1e100, 1e101, 1e101) + self.assertRaises(TypeError, range, 1., 2., 3.) self.assertRaises(TypeError, range, 0, "spam") self.assertRaises(TypeError, range, 0, 42, "spam") - #NEAL self.assertRaises(OverflowError, range, -sys.maxsize, sys.maxsize) - #NEAL self.assertRaises(OverflowError, range, 0, 2*sys.maxsize) - self.assertRaises(OverflowError, len, range(0, sys.maxsize**10)) def test_input(self):