# HG changeset patch # Parent f6b3ad30185172823f81592cf48d928f9c286085 diff -r f6b3ad301851 Doc/library/functions.rst --- a/Doc/library/functions.rst Mon Oct 17 11:36:49 2011 +1100 +++ b/Doc/library/functions.rst Mon Oct 17 15:51:21 2011 +0100 @@ -1077,6 +1077,14 @@ >>> r[-1] 18 + Testing range objects for equality with ``==`` and ``!=`` also + compares them as sequences, i. e. two range objects are considered + equal if they represent the same sequence of values. (Note that + two range objects that compare equal might have different + :attr:`start`, :attr:`stop` and :attr:`step` attributes, for + example ``range(0) == range(2, 1, 3)`` or ``range(0, 3, 2) == + range(0, 4, 2)``.) + Ranges containing absolute values larger than :data:`sys.maxsize` are permitted but some features (such as :func:`len`) will raise :exc:`OverflowError`. diff -r f6b3ad301851 Lib/test/test_range.py --- a/Lib/test/test_range.py Mon Oct 17 11:36:49 2011 +1100 +++ b/Lib/test/test_range.py Mon Oct 17 15:51:21 2011 +0100 @@ -507,6 +507,31 @@ for k in values - {0}: r[i:j:k] + def test_comparison(self): + test_ranges = [range(0), range(0, -1), range(1, 1, 3), + range(1), range(5, 6), range(5, 6, 2), + range(5, 7, 2), range(2), range(0, 4, 2), + range(0, 5, 2), range(0, 6, 2)] + test_tuples = list(map(tuple, test_ranges)) + ranges_eq = [a == b for a in test_ranges for b in test_ranges] + tuples_eq = [a == b for a in test_tuples for b in test_tuples] + self.assertEqual(ranges_eq, tuples_eq) + ranges_ne = [a != b for a in test_ranges for b in test_ranges] + self.assertEqual(ranges_ne, [not x for x in ranges_eq]) + for a in test_ranges: + for b in test_ranges: + if a == b: + self.assertEqual(hash(a), hash(b)) + self.assertEqual(range(0) == (), False) + self.assertEqual(() == range(0), False) + with self.assertRaises(TypeError): + range(0) < range(0) + with self.assertRaises(TypeError): + range(0) > range(0) + with self.assertRaises(TypeError): + range(0) <= range(0) + with self.assertRaises(TypeError): + range(0) >= range(0) def test_main(): test.support.run_unittest(RangeTest) diff -r f6b3ad301851 Misc/ACKS --- a/Misc/ACKS Mon Oct 17 11:36:49 2011 +1100 +++ b/Misc/ACKS Mon Oct 17 15:51:21 2011 +0100 @@ -612,6 +612,7 @@ Vladimir Marangozov David Marek Doug Marien +Sven Marnach Alex Martelli Anthony Martin Owen Martin diff -r f6b3ad301851 Objects/rangeobject.c --- a/Objects/rangeobject.c Mon Oct 17 11:36:49 2011 +1100 +++ b/Objects/rangeobject.c Mon Oct 17 15:51:21 2011 +0100 @@ -609,6 +609,127 @@ PY_ITERSEARCH_CONTAINS); } +/* Compare two range objects. Return 1 for equal, 0 for not equal + and -1 on error. The alogrithm is roughly the C equivalent of + + if len(r0) != len(r1): + return False + if not r0: + return True + if r0.start != r1.start: + return False + if len(r0) == 1: + return True + return r0.step == r1.step +*/ +static int +range_compare(rangeobject *r0, rangeobject *r1) +{ + int cmp_result; + PyObject *one; + + cmp_result = PyObject_RichCompareBool(r0->length, r1->length, Py_EQ); + if (cmp_result != 1) + return cmp_result; /* return False or error to the caller */ + cmp_result = PyObject_Not(r0->length); + if (cmp_result != 0) + return cmp_result; /* return True or error to the caller */ + cmp_result = PyObject_RichCompareBool(r0->start, r1->start, Py_EQ); + if (cmp_result != 1) + return cmp_result; /* return False or error to the caller */ + one = PyLong_FromLong(1); + if (!one) + return -1; + cmp_result = PyObject_RichCompareBool(r0->length, one, Py_EQ); + Py_DECREF(one); + if (cmp_result != 0) + return cmp_result; /* return True or error to the caller */ + return PyObject_RichCompareBool(r0->step, r1->step, Py_EQ); +} + +static PyObject * +range_richcompare(PyObject *self, PyObject *other, int op) +{ + int result; + + if (!PyRange_Check(other)) + Py_RETURN_NOTIMPLEMENTED; + switch (op) { + case Py_NE: + case Py_EQ: + if (self == other) { + result = 1; + } else { + result = range_compare((rangeobject*)self, (rangeobject*)other); + if (result == -1) + return NULL; + } + if (op == Py_NE) + result = !result; + if (result) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + case Py_LE: + case Py_GE: + case Py_LT: + case Py_GT: + Py_RETURN_NOTIMPLEMENTED; + default: + PyErr_BadArgument(); + return NULL; + } +} + +/* Hash function for range objects. Rough C equivalent of + + if not r: + return hash((len(r), None, None)) + if len(r) == 1: + return hash((len(r), r.start, None)) + return hash((len(r), r.start, r.step)) +*/ +static Py_hash_t +range_hash(rangeobject *r) +{ + PyObject *t; + Py_hash_t result = -1; + int cmp_result; + + t = PyTuple_New(3); + if (!t) + return -1; + PyTuple_SET_ITEM(t, 0, r->length); + cmp_result = PyObject_Not(r->length); + if (cmp_result == -1) + goto end; + if (cmp_result == 1) { + PyTuple_SET_ITEM(t, 1, Py_None); + PyTuple_SET_ITEM(t, 2, Py_None); + } else { + PyTuple_SET_ITEM(t, 1, r->start); + PyObject *one; + one = PyLong_FromLong(1); + if (!one) + goto end; + cmp_result = PyObject_RichCompareBool(r->length, one, Py_EQ); + Py_DECREF(one); + if (cmp_result == -1) + goto end; + if (cmp_result == 1) + PyTuple_SET_ITEM(t, 2, Py_None); + else + PyTuple_SET_ITEM(t, 2, r->step); + } + result = PyObject_Hash(t); + end: + PyTuple_SET_ITEM(t, 0, NULL); + PyTuple_SET_ITEM(t, 1, NULL); + PyTuple_SET_ITEM(t, 2, NULL); + Py_DECREF(t); + return result; +} + static PyObject * range_count(rangeobject *r, PyObject *ob) { @@ -763,7 +884,7 @@ 0, /* tp_as_number */ &range_as_sequence, /* tp_as_sequence */ &range_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ + (hashfunc)range_hash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ @@ -773,7 +894,7 @@ range_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ - 0, /* tp_richcompare */ + range_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ range_iter, /* tp_iter */ 0, /* tp_iternext */