Index: Objects/rangeobject.c =================================================================== --- Objects/rangeobject.c (revision 62354) +++ Objects/rangeobject.c (working copy) @@ -252,6 +252,73 @@ r->start, r->stop, r->step); } +static long +range_hash(rangeobject *v) +{ + /* reproduce tuple hash */ + long x, y; + Py_ssize_t len = Py_SIZE(v); + long mult = 1000003L; + x = 0x345678L; + y = PyObject_Hash(v->start); + if (y == -1) + return -1; + x = (x ^ y) * mult; + /* the cast might truncate len; that doesn't change hash stability */ + mult += (long)(82520L + len + len); + + y = PyObject_Hash(v->stop); + if (y == -1) + return -1; + x = (x ^ y) * mult; + /* the cast might truncate len; that doesn't change hash stability */ + mult += (long)(82520L + len + len); + + y = PyObject_Hash(v->step); + if (y == -1) + return -1; + x = (x ^ y) * mult; + /* the cast might truncate len; that doesn't change hash stability */ + mult += (long)(82520L + len + len); + + x += 97531L; + if (x == -1) + x = -2; + return x; + } + +static PyObject * +range_richcompare(rangeobject *v, rangeobject *w, int op) +{ + int start_same, stop_same, step_same, is_equal; + + if (!PyRange_Check(v) || + !PyRange_Check(w) || + (op != Py_EQ && + op != Py_NE)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + start_same = PyObject_RichCompareBool(v->start, w->start, Py_EQ); + if (start_same == -1) + return NULL; + stop_same = PyObject_RichCompareBool(v->stop, w->stop, Py_EQ); + if (stop_same == -1) + return NULL; + step_same = PyObject_RichCompareBool(v->step, w->step, Py_EQ); + if (step_same == -1) + return NULL; + + is_equal = start_same && stop_same && step_same; + 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 */ @@ -286,7 +353,7 @@ 0, /* tp_as_number */ &range_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ - 0, /* tp_hash */ + (hashfunc)range_hash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ @@ -296,7 +363,7 @@ range_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ - 0, /* tp_richcompare */ + (richcmpfunc)range_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ range_iter, /* tp_iter */ 0, /* tp_iternext */ Index: Lib/test/test_builtin.py =================================================================== --- Lib/test/test_builtin.py (revision 62354) +++ Lib/test/test_builtin.py (working copy) @@ -1595,6 +1595,19 @@ 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, but they can still be unequal + self.assertNotEquals(range(0, 10, 2), range(0, 11, 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.assertNotEquals(hash(range(0, 10, 2)), hash(range(0, 11, 2))) + self.assertNotEquals(hash(range(1)), hash(range(1, 2, 3))) + def test_input(self): self.write_testfile() fp = open(TESTFN, 'r')