From 4a1f593df5d4733831a1c4f03ca40c701433c43d Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Wed, 12 Nov 2008 23:23:36 +0000 Subject: Issue #4296: Fix PyObject_RichCompareBool so that "x in [x]" evaluates to True, even when x doesn't compare equal to itself. This was a regression from 2.6. Reviewed by R. Hettinger and C. Heimes. --- Lib/test/test_contains.py | 24 ++++++++++++++++++++++-- Lib/test/test_float.py | 27 +++++++++++++++++++++++++++ Misc/NEWS | 4 ++++ Objects/object.c | 9 +++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_contains.py b/Lib/test/test_contains.py index e570787..39c61bd 100644 --- a/Lib/test/test_contains.py +++ b/Lib/test/test_contains.py @@ -1,3 +1,4 @@ +from collections import deque from test.support import run_unittest import unittest @@ -6,7 +7,7 @@ class base_set: def __init__(self, el): self.el = el -class set(base_set): +class myset(base_set): def __contains__(self, el): return self.el == el @@ -17,7 +18,7 @@ class seq(base_set): class TestContains(unittest.TestCase): def test_common_tests(self): a = base_set(1) - b = set(1) + b = myset(1) c = seq(1) self.assert_(1 in b) self.assert_(0 not in b) @@ -80,6 +81,25 @@ class TestContains(unittest.TestCase): except TypeError: pass + def test_nonreflexive(self): + # containment and equality tests involving elements that are + # not necessarily equal to themselves + + class MyNonReflexive(object): + def __eq__(self, other): + return False + def __hash__(self): + return 28 + + values = float('nan'), 1, None, 'abc', MyNonReflexive() + constructors = list, tuple, dict.fromkeys, set, frozenset, deque + for constructor in constructors: + container = constructor(values) + for elem in container: + self.assert_(elem in container) + self.assert_(container == constructor(values)) + self.assert_(container == container) + def test_main(): run_unittest(TestContains) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index 4f8446a..2d3b357 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -117,6 +117,33 @@ class GeneralFloatCases(unittest.TestCase): self.assertRaises(OverflowError, float('-inf').as_integer_ratio) self.assertRaises(ValueError, float('nan').as_integer_ratio) + def test_float_containment(self): + floats = (INF, -INF, 0.0, 1.0, NAN) + for f in floats: + self.assert_(f in [f], "'%r' not in []" % f) + self.assert_(f in (f,), "'%r' not in ()" % f) + self.assert_(f in {f}, "'%r' not in set()" % f) + self.assert_(f in {f: None}, "'%r' not in {}" % f) + self.assertEqual([f].count(f), 1, "[].count('%r') != 1" % f) + self.assert_(f in floats, "'%r' not in container" % f) + + for f in floats: + # nonidentical containers, same type, same contents + self.assert_([f] == [f], "[%r] != [%r]" % (f, f)) + self.assert_((f,) == (f,), "(%r,) != (%r,)" % (f, f)) + self.assert_({f} == {f}, "{%r} != {%r}" % (f, f)) + self.assert_({f : None} == {f: None}, "{%r : None} != " + "{%r : None}" % (f, f)) + + # identical containers + l, t, s, d = [f], (f,), {f}, {f: None} + self.assert_(l == l, "[%r] not equal to itself" % f) + self.assert_(t == t, "(%r,) not equal to itself" % f) + self.assert_(s == s, "{%r} not equal to itself" % f) + self.assert_(d == d, "{%r : None} not equal to itself" % f) + + + class FormatFunctionsTestCase(unittest.TestCase): def setUp(self): diff --git a/Misc/NEWS b/Misc/NEWS index ecc808f..035867c 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,10 @@ What's New in Python 3.0 release candiate 3? Core and Builtins ----------------- +- Issue #4296: Fix PyObject_RichCompareBool so that "x in [x]" evaluates to + True, even when x doesn't compare equal to itself. This was a regression + from 2.6. + - Issue #3705: Command-line arguments were not correctly decoded when the terminal does not use UTF8. diff --git a/Objects/object.c b/Objects/object.c index cdbceaf..2c43221 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -687,6 +687,15 @@ PyObject_RichCompareBool(PyObject *v, PyObject *w, int op) PyObject *res; int ok; + /* Quick result when objects are the same. + Guarantees that identity implies equality. */ + if (v == w) { + if (op == Py_EQ) + return 1; + else if (op == Py_NE) + return 0; + } + res = PyObject_RichCompare(v, w, op); if (res == NULL) return -1; -- cgit v0.12