Index: Objects/rangeobject.c =================================================================== --- Objects/rangeobject.c (revision 62506) +++ Objects/rangeobject.c (working copy) @@ -43,6 +43,36 @@ return step; } +static PyObject * +normalize_stop(PyObject *start, PyObject *stop, PyObject *step) +{ + int remainder_zero; + PyObject *raw_len = NULL, *remainder = NULL, *result = NULL; + + raw_len = PyNumber_Subtract(stop, start); + if (raw_len == NULL) + goto fail; + remainder = PyNumber_Remainder(raw_len, step); + if (remainder == NULL) + goto fail; + remainder_zero = PyObject_Not(remainder); + if (remainder_zero == -1) + goto fail; + if (remainder_zero) + return stop; + result = PyNumber_Subtract(stop, remainder); + + Py_DECREF(stop); + Py_DECREF(raw_len); + Py_DECREF(remainder); + return result; + +fail: + Py_XDECREF(raw_len); + Py_XDECREF(remainder); + return NULL; +} + /* XXX(nnorwitz): should we error check if the user passes any empty ranges? range(-10) range(0, -5) @@ -80,6 +110,10 @@ if (!start || !stop || !step) goto Fail; } + + stop = normalize_stop(start, stop, step); + if (stop == NULL) + return NULL; obj = PyObject_New(rangeobject, &PyRange_Type); if (obj == NULL) @@ -252,6 +286,60 @@ r->start, r->stop, r->step); } +static long +range_hash(rangeobject *v) +{ + Py_ssize_t len, start, step; + + len = range_length(v); + if (len == -1) + return -1; + start = PyObject_Hash(v->start); + if (start == -1) + return -1; + step = PyObject_Hash(v->step); + if (step == -1) + return -1; + + return (long)(start*1000003L+len)*1630457L+step; +} + +static PyObject * +range_richcompare(rangeobject *v, rangeobject *w, int op) +{ + int is_equal, lengths_equal = 0; + + if (!PyRange_Check(v) || !PyRange_Check(w) || + (op != Py_EQ && op != Py_NE)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + is_equal = PyObject_RichCompareBool(v->start, w->start, Py_EQ); + if (is_equal == -1) + return NULL; + if (!is_equal) + goto find_result; + + is_equal = PyObject_RichCompareBool(v->stop, w->stop, Py_EQ); + if (is_equal == -1) + return NULL; + if (!is_equal) + goto find_result; + + is_equal = PyObject_RichCompareBool(v->step, w->step, Py_EQ); + if (is_equal == -1) + return NULL; + +find_result: + if ((is_equal && op == Py_EQ) || (!is_equal && op == Py_NE)) { + Py_RETURN_TRUE; + } + else { + Py_RETURN_FALSE; + } +} + static PySequenceMethods range_as_sequence = { (lenfunc)range_length, /* sq_length */ 0, /* sq_concat */ @@ -274,43 +362,43 @@ PyTypeObject PyRange_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - "range", /* Name of this type */ - sizeof(rangeobject), /* Basic object size */ - 0, /* Item size for varobject */ - (destructor)range_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - (reprfunc)range_repr, /* tp_repr */ - 0, /* tp_as_number */ - &range_as_sequence, /* 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 */ - range_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - range_iter, /* tp_iter */ - 0, /* tp_iternext */ - range_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 */ - range_new, /* tp_new */ + "range", /* Name of this type */ + sizeof(rangeobject), /* Basic object size */ + 0, /* Item size for varobject */ + (destructor)range_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)range_repr, /* tp_repr */ + 0, /* tp_as_number */ + &range_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)range_hash, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + range_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)range_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + range_iter, /* tp_iter */ + 0, /* tp_iternext */ + range_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 */ + range_new, /* tp_new */ }; /*********************** range Iterator **************************/ Index: Lib/test/test_builtin.py =================================================================== --- Lib/test/test_builtin.py (revision 62506) +++ Lib/test/test_builtin.py (working copy) @@ -1595,6 +1595,21 @@ self.assertRaises(OverflowError, len, range(0, sys.maxsize**10)) + self.assertEquals(range(1234), range(1234)) + self.assertEquals(range(4, 3, 2), range(4, 3, 2)) + self.assertEquals(range(2222, 3333), range(2222, 3333)) + self.assertNotEquals(range(1, 15, 3), range(1, 20, 3)) + # The sequence of numbers can be the same + self.assertEquals(range(0, 9, 2), range(0, 8, 2)) + + self.assertEquals(hash(range(1234)), hash(range(1234))) + self.assertEquals(hash(range(1, 20, 3)), hash(range(1, 20, 3))) + self.assertNotEquals(hash(range(5, 10)), hash(range(6, 10))) + self.assertEquals(hash(range(0, 10, 2)), hash(range(0, 11, 2))) + self.assertNotEquals(hash(range(1)), hash(range(1, 2, 3))) + # Can't hash huge ranges + self.assertRaises(OverflowError, hash, range(2**100)) + def test_input(self): self.write_testfile() fp = open(TESTFN, 'r')