Index: Python/pythonrun.c =================================================================== --- Python/pythonrun.c (revision 51564) +++ Python/pythonrun.c (working copy) @@ -229,6 +229,8 @@ _PyImport_FixupExtension("__builtin__", "__builtin__"); _PyImportHooks_Init(); + if (_PyBuiltin_InitWithImports(interp->builtins) == NULL) + Py_FatalError("Py_Initialize: can't initialize builtins #2"); if (install_sigs) initsigs(); /* Signal handling stuff, including initintr() */ Index: Python/bltinmodule.c =================================================================== --- Python/bltinmodule.c (revision 51564) +++ Python/bltinmodule.c (working copy) @@ -2307,6 +2307,7 @@ #define ADD_TO_ALL(OBJECT) (void)0 #endif + /* XXX(nnorwitz): doesn't this leak mod? */ #define SETBUILTIN(NAME, OBJECT) \ if (PyDict_SetItemString(dict, NAME, (PyObject *)OBJECT) < 0) \ return NULL; \ @@ -2342,7 +2343,6 @@ SETBUILTIN("super", &PySuper_Type); SETBUILTIN("tuple", &PyTuple_Type); SETBUILTIN("type", &PyType_Type); - SETBUILTIN("xrange", &PyRange_Type); #ifdef Py_USING_UNICODE SETBUILTIN("unicode", &PyUnicode_Type); #endif @@ -2354,8 +2354,45 @@ Py_XDECREF(debug); return mod; +} + +PyObject * +_PyBuiltin_InitWithImports(PyObject *dict) +{ + PyObject *xrange_module, *xrange_dict, *xrange; + extern PyTypeObject Pyrangeiter_Type; + + assert(dict); + + /* xrange is special since it is implemented in Python. + However, we still need to inject the int iterator + implemented in C into the Python xrange. + */ + /* XXX(nnorwitz): this probably has some leaks currently. */ + /* XXX(nnorwitz): how should errors be handled? FatalError? */ + xrange_module = PyImport_ImportModule("xrange"); + if (!xrange_module) + goto error; + + xrange_dict = PyModule_GetDict(xrange_module); + if (!xrange_dict) + goto error; + + xrange = PyDict_GetItemString(xrange_dict, "xrange"); + if (!xrange) + goto error; /* XXX(nnorwitz): need to set error here? */ + + if (PyObject_SetAttrString(xrange, "_int_iter", + (PyObject*)&Pyrangeiter_Type) < 0) + goto error; + + SETBUILTIN("xrange", xrange); + return dict; #undef ADD_TO_ALL #undef SETBUILTIN +error: + PyErr_WriteUnraisable(PyErr_Occurred()); + return NULL; } /* Helper for filter(): filter a tuple through a function */ Index: Include/pythonrun.h =================================================================== --- Include/pythonrun.h (revision 51564) +++ Include/pythonrun.h (working copy) @@ -116,6 +116,7 @@ /* Internal -- various one-time initializations */ PyAPI_FUNC(PyObject *) _PyBuiltin_Init(void); +PyAPI_FUNC(PyObject *) _PyBuiltin_InitWithImports(PyObject *builtin_dict); PyAPI_FUNC(PyObject *) _PySys_Init(void); PyAPI_FUNC(void) _PyImport_Init(void); PyAPI_FUNC(void) _PyExc_Init(void); Index: Objects/rangeobject.c =================================================================== --- Objects/rangeobject.c (revision 51564) +++ Objects/rangeobject.c (working copy) @@ -2,13 +2,117 @@ #include "Python.h" +/* XXX(nnorwitz): should we support objects whose length is > PY_SSIZE_T_MAX. + We can do it for normal iteration, but not for range[x] or len(range) + + This could be sped up for small PyLongs if they fit in an Py_ssize_t. + This only matters on Win64. Though we could use PY_LONG_LONG which + would presumably help perf. +*/ + +/* + In order to be backwards compatible with Python 2.5 and before, we + still accept objects with an __int__. This should be removed for Py3k. +*/ + +/* start/len/step must all be the same type, either int or long */ +/* XXX(nnorwitz): would it be better to use a union of longs vs PyObject's? */ typedef struct { PyObject_HEAD - long start; - long step; - long len; + PyObject *start; + PyObject *len; + PyObject *step; } rangeobject; +/* Utility function to get a proper integer value from an argument. + Generally, we want any numeric type, except a float. However, + for b/w compatability we accept floats in 2.x. Support for floats + needs to be removed for Py3k. +*/ +static PyObject * +get_int(PyObject *obj, const char *variable) +{ + PyObject *index = PyNumber_Index(obj); + Py_ssize_t long_value; + if (!index) { + /* Call PyInt_AsLong just for b/w compatability (float). */ + PyErr_Clear(); + long_value = PyInt_AsLong(obj); + if (! (long_value == -1 && PyErr_Occurred())) { + char errmsg[256]; + PyOS_snprintf(errmsg, sizeof(errmsg), + "invalid type '%.200s' for %s value", + obj->ob_type->tp_name, variable); + if (PyErr_Warn(PyExc_DeprecationWarning, errmsg) < 0) + return NULL; + index = PyInt_FromLong(long_value); + } + } + + return index; +} + +/* Helper function for validating step. Returns NULL on error. + Step may be input as a borrowed ref. The returned value is an owned ref. +*/ +static PyObject * +validate_step(PyObject *step) +{ + /* No step specified, use a step of 1. */ + if (!step) + return PyInt_FromLong(1); + + step = get_int(step, "step"); + if (step) { + Py_ssize_t istep = PyNumber_AsSsize_t(step, NULL); + if (istep == -1 && PyErr_Occurred()) { + /* Ignore OverflowError, we know the value isn't 0. */ + PyErr_Clear(); + } + else if (istep == 0) { + PyErr_SetString(PyExc_ValueError, + "xrange() arg 3 must not be zero"); + Py_CLEAR(step); + } + } + + return step; +} + +/* Ensure all args are either ints or longs */ +static int +normalize_args(PyObject **start, PyObject **len, PyObject **step) +{ + /* XXX(nnorwitz): if step == LONG_MIN, we should probably promote + all args to longs otherwise repr displays the wrong values. */ + if ((*start)->ob_type == (*len)->ob_type && + (*start)->ob_type == (*step)->ob_type) + return 1; + + /* Subclasses are ok too. */ + if (PyInt_Check(*start) && PyInt_Check(*len) && PyInt_Check(*step)) + return 1; + + if (PyLong_Check(*start) && PyLong_Check(*len) && PyLong_Check(*step)) + return 1; + + /* Oh well, try to coerce everything to longs. */ +#define CONVERT(p) \ + do { \ + PyObject *tmp = PyNumber_Long(p); \ + if (!tmp) \ + return 0; \ + Py_DECREF(p); \ + p = tmp; \ + } while (0) + + CONVERT(*start); + CONVERT(*len); + CONVERT(*step); +#undef CONVERT + return 1; +} + /* 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 * large to fit in a signed long. @@ -38,49 +142,95 @@ return n; } +/* XXX(nnorwitz): should we error check if the user passes nonsense? + xrange(-10) + xrange(0, -5) + xrange(0, 5, -1) +*/ static PyObject * range_new(PyTypeObject *type, PyObject *args, PyObject *kw) { - rangeobject *obj; - long ilow = 0, ihigh = 0, istep = 1; - long n; + rangeobject *obj = NULL; + PyObject *start = NULL, *end = NULL, *len = NULL, *step = NULL; if (!_PyArg_NoKeywords("xrange()", kw)) return NULL; if (PyTuple_Size(args) <= 1) { - if (!PyArg_ParseTuple(args, - "l;xrange() requires 1-3 int arguments", - &ihigh)) - return NULL; + if (!PyArg_UnpackTuple(args, "xrange", 1, 1, &end)) + goto error_exit; + len = get_int(end, "stop"); + if (!len) + return NULL; /* all refs are borrowed */ + start = PyInt_FromLong(0); + step = PyInt_FromLong(1); + if (!start || !step) + goto error_exit; + + if (!normalize_args(&start, &len, &step)) + goto error_exit; } else { - if (!PyArg_ParseTuple(args, - "ll|l;xrange() requires 1-3 int arguments", - &ilow, &ihigh, &istep)) + if (!PyArg_UnpackTuple(args, "xrange", 2, 3, + &start, &end, &step)) return NULL; + + /* Convert borrowed refs to owned refs */ + start = get_int(start, "start"); + end = get_int(end, "stop"); + step = validate_step(step); + if (!start || !end || !step) + goto error_exit; + + if (!normalize_args(&start, &end, &step)) + goto error_exit; + + /* calculate len */ + if (PyInt_Check(start)) { + long istep = PyInt_AS_LONG(step); + long ilen; + + /* XXX(nnorwitz): this code doesn't work if istep == LONG_MIN */ + if (istep > 0) + ilen = get_len_of_range(PyInt_AS_LONG(start), + PyInt_AS_LONG(end), + istep); + else + ilen = get_len_of_range(PyInt_AS_LONG(end), + PyInt_AS_LONG(start), + -istep); + len = PyInt_FromLong(ilen); + Py_CLEAR(end); + if (!len) + goto error_exit; + } + else { + PyObject *tmp; + tmp = PyNumber_Subtract(end, start); + Py_CLEAR(end); + if (!tmp) + goto error_exit; + len = PyNumber_FloorDivide(tmp, step); + Py_DECREF(tmp); + if (!len) + goto error_exit; + } } - if (istep == 0) { - PyErr_SetString(PyExc_ValueError, "xrange() arg 3 must not be zero"); - return NULL; - } - if (istep > 0) - n = get_len_of_range(ilow, ihigh, istep); - else - n = get_len_of_range(ihigh, ilow, -istep); - if (n < 0) { - PyErr_SetString(PyExc_OverflowError, - "xrange() result has too many items"); - return NULL; - } obj = PyObject_New(rangeobject, &PyRange_Type); if (obj == NULL) - return NULL; - obj->start = ilow; - obj->len = n; - obj->step = istep; + goto error_exit; + obj->start = start; + obj->len = len; + obj->step = step; return (PyObject *) obj; + +error_exit: + Py_XDECREF(start); + Py_XDECREF(end); + Py_XDECREF(len); + Py_XDECREF(step); + return NULL; } PyDoc_STRVAR(range_doc, @@ -90,45 +240,180 @@ generates the numbers in the range on demand. For looping, this is \n\ slightly faster than range() and more memory efficient."); +static void +range_dealloc(rangeobject *r) +{ + Py_DECREF(r->start); + Py_DECREF(r->step); + Py_DECREF(r->len); +} + +static Py_ssize_t +range_length(rangeobject *r) +{ + Py_ssize_t len; + if (PyInt_Check(r->start)) + len = (Py_ssize_t) PyInt_AS_LONG(r->len); + else + len = _PyLong_AsSsize_t(r->len); + if (len < 0 && !PyErr_Occurred()) + len = 0; + return len; +} + +#if 1 static PyObject * range_item(rangeobject *r, Py_ssize_t i) { - if (i < 0 || i >= r->len) { - PyErr_SetString(PyExc_IndexError, - "xrange object index out of range"); + Py_ssize_t len = range_length(r); + Py_ssize_t rem; + PyObject *rem_obj, *incr, *result; + + /* XXX(nnorwitz): should negative indices be supported? */ + /* XXX(nnorwitz): should support range[x] where x > PY_SSIZE_T_MAX? */ + if (i < 0 || i >= len) { + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_IndexError, + "xrange object index out of range"); return NULL; } - return PyInt_FromSsize_t(r->start + (i % r->len) * r->step); + + /* Handle ints */ + rem = i % len; + if (PyInt_Check(r->start)) { + long start = PyInt_AS_LONG(r->start); + long step = PyInt_AS_LONG(r->step); + return PyInt_FromLong(start + rem * step); + } + + /* Handle longs (similar to int code above) */ + rem_obj = _PyLong_FromSsize_t(rem); + if (!rem_obj) + return NULL; + incr = PyNumber_Multiply(rem_obj, r->step); + Py_DECREF(rem_obj); + if (!incr) + return NULL; + result = PyNumber_Add(r->start, incr); + Py_DECREF(rem_obj); + return result; } +#else +static PyObject * +range_item(rangeobject *r, PyObject *i) +{ + PyObject *rem_obj, *incr, *result; -static Py_ssize_t -range_length(rangeobject *r) + /* Handle ints */ + if (PyInt_Check(r->start)) { + long start = PyInt_AS_LONG(r->start); + long step = PyInt_AS_LONG(r->step); + long rem = PyNumber_AsLong(i); + if (rem == -1 && PyErr_Occurred()) + return NULL; + + rem = rem % range_length(r); + return PyInt_FromLong(start + rem * step); + } + + Py_ssize_t rem = 0; + /* XXX(nnorwitz): Handle longs (similar to int code above) */ + rem_obj = _PyLong_FromSsize_t(rem); + if (!rem_obj) + return NULL; + incr = PyNumber_Multiply(rem_obj, r->step); + Py_DECREF(rem_obj); + if (!incr) + return NULL; + result = PyNumber_Add(r->start, incr); + Py_DECREF(rem_obj); + return result; +} +#endif + +static PyObject * +range_repr_int(rangeobject *r) { - return (Py_ssize_t)(r->len); + long istart = PyInt_AS_LONG(r->start); + long ilen = PyInt_AS_LONG(r->len); + long istep = PyInt_AS_LONG(r->step); + long imax = istart + ilen * istep; + + if (istart == 0 && istep == 1) + return PyString_FromFormat("xrange(%ld)", imax); + if (istep == 1) + return PyString_FromFormat("xrange(%ld, %ld)", istart, imax); + return PyString_FromFormat("xrange(%ld, %ld, %ld)", istart, imax, istep); } static PyObject * -range_repr(rangeobject *r) +range_repr_long(rangeobject *r) { - PyObject *rtn; + PyObject *start_str = NULL, *end_str = NULL, *step_str = NULL; + PyObject *product, *stop, *result = NULL; + Py_ssize_t istart, istep; - if (r->start == 0 && r->step == 1) - rtn = PyString_FromFormat("xrange(%ld)", - r->start + r->len * r->step); + product = PyNumber_Multiply(r->len, r->step); + if (!product) + return NULL; + stop = PyNumber_Add(r->start, product); + Py_DECREF(product); + if (!stop) + return NULL; + + /* We always need the end value. */ + end_str = PyObject_Str(stop); + Py_DECREF(stop); + if (!end_str) + return NULL; - else if (r->step == 1) - rtn = PyString_FromFormat("xrange(%ld, %ld)", - r->start, - r->start + r->len * r->step); + /* XXX(nnorwitz): should we use PyObject_Repr instead of str? */ - else - rtn = PyString_FromFormat("xrange(%ld, %ld, %ld)", - r->start, - r->start + r->len * r->step, - r->step); - return rtn; + /* Check for special case values for printing. We don't always + need the start or step values. We don't care about errors + (it means overflow), so clear the errors. */ + istart = _PyLong_AsSsize_t(r->start); + if (istart != 0 || (istart == -1 && PyErr_Occurred())) { + PyErr_Clear(); + start_str = PyObject_Str(r->start); + } + + istep = _PyLong_AsSsize_t(r->step); + if (istep != 1 || (istep == -1 && PyErr_Occurred())) { + PyErr_Clear(); + step_str = PyObject_Str(r->step); + } + + if (istart == 0 && istep == 1) + result = PyString_FromFormat("xrange(%s)", + PyString_AS_STRING(end_str)); + else if (istep == 1) { + if (start_str) + result = PyString_FromFormat("xrange(%s, %s)", + PyString_AS_STRING(start_str), + PyString_AS_STRING(end_str)); + } + else if (start_str && step_str) + result = PyString_FromFormat("xrange(%s, %s, %s)", + PyString_AS_STRING(start_str), + PyString_AS_STRING(end_str), + PyString_AS_STRING(step_str)); + /* else result is NULL and an error should already be set. */ + + Py_XDECREF(start_str); + Py_XDECREF(end_str); + Py_XDECREF(step_str); + return result; } +static PyObject * +range_repr(rangeobject *r) +{ + if (PyInt_Check(r->start)) + return range_repr_int(r); + return range_repr_long(r); +} + static PySequenceMethods range_as_sequence = { (lenfunc)range_length, /* sq_length */ 0, /* sq_concat */ @@ -154,7 +439,7 @@ "xrange", /* Name of this type */ sizeof(rangeobject), /* Basic object size */ 0, /* Item size for varobject */ - (destructor)PyObject_Del, /* tp_dealloc */ + (destructor)range_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -192,6 +477,11 @@ /*********************** Xrange Iterator **************************/ +/* There are 2 types of iterators, one for C longs, the other for + Python longs (ie, PyObjects). This should make iteration fast + in the normal case, but possible for any numeric value. +*/ + typedef struct { PyObject_HEAD long index; @@ -214,6 +504,21 @@ return PyInt_FromLong(r->len - r->index); } +typedef struct { + PyObject_HEAD + PyObject *index; + PyObject *start; + PyObject *len; + PyObject *step; +} longrangeiterobject; + +static PyObject * +longrangeiter_len(longrangeiterobject *r) +{ + return PyNumber_Subtract(r->len, r->index); +} +static PyObject *rangeiter_new(PyTypeObject *, PyObject *args, PyObject *kw); + PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); static PyMethodDef rangeiter_methods[] = { @@ -221,7 +526,7 @@ {NULL, NULL} /* sentinel */ }; -static PyTypeObject Pyrangeiter_Type = { +PyTypeObject Pyrangeiter_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, /* ob_size */ "rangeiterator", /* tp_name */ @@ -252,50 +557,215 @@ PyObject_SelfIter, /* tp_iter */ (iternextfunc)rangeiter_next, /* tp_iternext */ rangeiter_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + rangeiter_new, /* tp_new */ +}; + +static PyObject * +int_range_iter(long start, long step, long len) +{ + rangeiterobject *it = PyObject_New(rangeiterobject, &Pyrangeiter_Type); + if (it == NULL) + return NULL; + it->start = start; + it->step = step; + it->len = len; + it->index = 0; + return (PyObject *)it; +} + +static PyObject * +rangeiter_new(PyTypeObject *type, PyObject *args, PyObject *kw) +{ + long start, step, len; + + if (!_PyArg_NoKeywords("xrangeiter()", kw)) + return NULL; + + if (!PyArg_ParseTuple(args, "lll;xrangeiter() requires 3 int arguments", + &start, &step, &len)) + return NULL; + + return int_range_iter(start, step, len); +} + +static PyMethodDef longrangeiter_methods[] = { + {"__length_hint__", (PyCFunction)longrangeiter_len, METH_NOARGS, length_hint_doc}, + {NULL, NULL} /* sentinel */ +}; + +static void +longrangeiter_dealloc(longrangeiterobject *r) +{ + Py_XDECREF(r->index); + Py_DECREF(r->start); + Py_DECREF(r->step); + Py_DECREF(r->len); +} + +static PyObject * +longrangeiter_next(longrangeiterobject *r) +{ + PyObject *one, *product, *new_index, *result; + if (PyObject_RichCompareBool(r->index, r->len, Py_LT) != 1) + return NULL; + + one = PyLong_FromLong(1); + if (!one) + return NULL; + + product = PyNumber_Multiply(r->index, r->step); + if (!product) { + Py_DECREF(one); + return NULL; + } + + new_index = PyNumber_Add(r->index, one); + Py_DECREF(one); + if (!new_index) { + Py_DECREF(product); + return NULL; + } + + result = PyNumber_Add(r->start, product); + Py_DECREF(product); + if (result) { + Py_DECREF(r->index); + r->index = new_index; + } + + return result; +} + +static PyTypeObject Pylongrangeiter_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "rangeiterator", /* tp_name */ + sizeof(longrangeiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)longrangeiter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)longrangeiter_next, /* tp_iternext */ + longrangeiter_methods, /* tp_methods */ 0, }; static PyObject * range_iter(PyObject *seq) { - rangeiterobject *it; + rangeobject *r = (rangeobject *)seq; + longrangeiterobject *it; - if (!PyRange_Check(seq)) { - PyErr_BadInternalCall(); + assert(PyRange_Check(seq)); + if (PyInt_Check(r->start)) + return int_range_iter(PyInt_AS_LONG(r->start), + PyInt_AS_LONG(r->step), PyInt_AS_LONG(r->len)); + + it = PyObject_New(longrangeiterobject, &Pylongrangeiter_Type); + if (it == NULL) return NULL; + it->start = r->start; + it->step = r->step; + it->len = r->len; + it->index = PyLong_FromLong(0); + if (!it->index) { + PyObject_Del(it); + return NULL; } - it = PyObject_New(rangeiterobject, &Pyrangeiter_Type); - if (it == NULL) - return NULL; - it->index = 0; - it->start = ((rangeobject *)seq)->start; - it->step = ((rangeobject *)seq)->step; - it->len = ((rangeobject *)seq)->len; + + Py_INCREF(it->start); + Py_INCREF(it->step); + Py_INCREF(it->len); return (PyObject *)it; } static PyObject * range_reverse(PyObject *seq) { - rangeiterobject *it; - long start, step, len; + rangeobject *range = (rangeobject*) seq; + longrangeiterobject *it; + PyObject *one, *sum, *len, *product; - if (!PyRange_Check(seq)) { - PyErr_BadInternalCall(); - return NULL; + assert(PyRange_Check(seq)); + if (PyInt_Check(range->start)) { + long start = PyInt_AS_LONG(range->start); + long step = PyInt_AS_LONG(range->step); + long len = PyInt_AS_LONG(range->len); + /* XXX(nnorwitz): need to check for overflow. */ + return int_range_iter(start + (len - 1) * step, -step, len); } - it = PyObject_New(rangeiterobject, &Pyrangeiter_Type); + + it = PyObject_New(longrangeiterobject, &Pylongrangeiter_Type); if (it == NULL) return NULL; - start = ((rangeobject *)seq)->start; - step = ((rangeobject *)seq)->step; - len = ((rangeobject *)seq)->len; + one = PyLong_FromLong(1); + if (!one) + goto create_failure; - it->index = 0; - it->start = start + (len-1) * step; - it->step = -step; - it->len = len; + len = PyNumber_Subtract(range->len, one); + Py_DECREF(one); + if (!len) + goto create_failure; + product = PyNumber_Add(len, range->step); + Py_DECREF(len); + if (!product) + goto create_failure; + + sum = PyNumber_Add(range->start, product); + Py_DECREF(product); + it->start = sum; + + if (!it->start) { +create_failure: + PyObject_Del(it); + return NULL; + } + it->step = PyNumber_Negative(range->step); + if (!it->step) { + Py_DECREF(it->start); + PyObject_Del(it); + return NULL; + } + + it->len = range->len; + Py_INCREF(it->len); + + it->index = PyLong_FromLong(0); + if (!it->index) { + Py_DECREF(it); + return NULL; + } + return (PyObject *)it; } Index: Lib/xrange.py =================================================================== --- Lib/xrange.py (revision 0) +++ Lib/xrange.py (revision 0) @@ -0,0 +1,120 @@ + +# This module implements the xrange builtin object. +# A generic iterator that works on ints and longs is provided in +# _xrange_iter. However, it is only used for longs. For performance +# during iteration, the iterator for all int values is implemented in +# C. It is the _int_iter class attribute on xrange. In previous +# versions of Python (2.5 and earlier), both the xrange object and iterator +# were implemented in C. Only C longs were supported. This version +# supports Python ints, longs, or anything with an __index__ method. +# Floats are supported as they were in 2.5 (ie, they are truncated) +# with a deprecation warning issued. +# +# There isn't really anything public in this file. During startup, +# this module is imported and we install xrange as the builtin. + +# This is the iterator/generator returned from iter(xrange(...)). +def _xrange_iter(value, step, len): + end = value + step * len + # The comparison is opposite depending on step. + if step > 0: + while value < end: + yield value + value += step + else: + while value > end: + yield value + value += step + +# Utility function calculating the number of elements. +def _get_len_of_range(lo, hi, step): + if lo < hi: + return ((hi - lo - 1) // step) + 1 + return 0 + + +# Utility function for verifying (and possibly converting) parameters. +def _get_int_value(value, name): + if isinstance(value, float): + import warnings + msg = '%s value should be an int or long, not %s' % (name, type(value)) + warnings.warn(msg, DeprecationWarning, 3) + return int(value) + + try: + return value.__index__() + except AttributeError: + pass + try: + return value.__int__() + except AttributeError: + raise TypeError('%s value should be an int or support __index__' % name) + + +class xrange(object): + + """xrange([start,] stop[, step]) -> xrange object + +Like range(), but instead of returning a list, returns an object that +generates the numbers in the range on demand. For looping, this is +slightly faster than range() and more memory efficient.""" + + __slots__ = ('start', 'step', '_len', 'stop') + _int_iter = None # This is set on startup from C. + + def __init__(self, start, stop=None, step=None): + start = _get_int_value(start, 'start') + if stop is None: + (start, stop) = (0, start) + else: + stop = _get_int_value(stop, 'stop') + + if step is None: + step = 1 + elif step == 0: + raise ValueError('xrange() arg 3 must not be zero') + else: + step = _get_int_value(step, 'step') + + self.step = step + self.start = start + # Only used for repr() to make empty sequences and + # -sys.maxint - 1 display properly. + self.stop = stop + + if step > 0: + self._len = _get_len_of_range(start, stop, step) + else: + self._len = _get_len_of_range(stop, start, -step) + + def _make_iter(self, start, step, len): + if type(start) is type(step) is type(len) is int: + return self._int_iter(start, step, len) + return _xrange_iter(start, step, len) + + def __reversed__(self): + """Returns a reverse iterator.""" + new_start = self.start + (self._len - 1) * self.step + return self._make_iter(new_start, -self.step, self._len) + + def __iter__(self): + return self._make_iter(self.start, self.step, self._len) + + def __getitem__(self, item): + if isinstance(item, slice): + # XXX(nnorwitz): handle slices (new feature) + raise ValueError("Can't handle slices yet") + item = _get_int_value(item, 'item') + if -self._len <= item < self._len: + return self.start + (item % self._len) * self.step + raise IndexError("xrange object index out of range") + + def __repr__(self): + if self.step == 1 and self.start == 0: + return 'xrange(%d)' % self.stop + if self.step == 1: + return 'xrange(%d, %d)' % (self.start, self.stop) + return 'xrange(%d, %d, %d)' % (self.start, self.stop, self.step) + + def __len__(self): + return self._len Property changes on: Lib/xrange.py ___________________________________________________________________ Name: svn:keywords + Id Name: svn:eol-style + native Index: Lib/test/test_xrange.py =================================================================== --- Lib/test/test_xrange.py (revision 51564) +++ Lib/test/test_xrange.py (working copy) @@ -43,19 +43,19 @@ self.assertRaises(TypeError, xrange, 1, 2, 3, 4) self.assertRaises(ValueError, xrange, 1, 2, 0) - self.assertRaises(OverflowError, xrange, 1e100, 1e101, 1e101) + #self.assertRaises(OverflowError, xrange, 1e100, 1e101, 1e101) self.assertRaises(TypeError, xrange, 0, "spam") self.assertRaises(TypeError, xrange, 0, 42, "spam") self.assertEqual(len(xrange(0, sys.maxint, sys.maxint-1)), 2) - self.assertRaises(OverflowError, xrange, -sys.maxint, sys.maxint) - self.assertRaises(OverflowError, xrange, 0, 2*sys.maxint) + #self.assertRaises(OverflowError, xrange, -sys.maxint, sys.maxint) + #self.assertRaises(OverflowError, xrange, 0, 2*sys.maxint) r = xrange(-sys.maxint, sys.maxint, 2) self.assertEqual(len(r), sys.maxint) - self.assertRaises(OverflowError, xrange, -sys.maxint-1, sys.maxint, 2) + #self.assertRaises(OverflowError, xrange, -sys.maxint-1, sys.maxint, 2) def test_main(): test.test_support.run_unittest(XrangeTest)