]> granicus.if.org Git - python/commitdiff
bpo-37648: Fixed minor inconsistency in some __contains__. (GH-14904)
authorSerhiy Storchaka <storchaka@gmail.com>
Sun, 4 Aug 2019 11:12:48 +0000 (14:12 +0300)
committerGitHub <noreply@github.com>
Sun, 4 Aug 2019 11:12:48 +0000 (14:12 +0300)
The collection's item is now always at the left and
the needle is on the right of ==.

12 files changed:
Doc/library/test.rst
Lib/test/list_tests.py
Lib/test/seq_tests.py
Lib/test/support/__init__.py
Lib/test/test_iter.py
Misc/NEWS.d/next/Core and Builtins/2019-07-22-11-05-05.bpo-37648.6TY2L-.rst [new file with mode: 0644]
Modules/_asynciomodule.c
Modules/_ssl.c
Objects/abstract.c
Objects/dictobject.c
Objects/listobject.c
Objects/tupleobject.c

index da6a85d340be34feeb5e8ae2a614754a9a30966f..6eef5c65499a8440b88bc38ed631352d4b8e98aa 100644 (file)
@@ -367,6 +367,12 @@ The :mod:`test.support` module defines the following constants:
    Object that is equal to anything.  Used to test mixed type comparison.
 
 
+.. data:: NEVER_EQ
+
+   Object that is not equal to anything (even to :data:`ALWAYS_EQ`).
+   Used to test mixed type comparison.
+
+
 .. data:: LARGEST
 
    Object that is greater than anything (except itself).
index 40316de220fac2ca2e976f2f60fa686a440c8d6b..44bc2ae6573c1a15f340e1087a13acc3fb83069b 100644 (file)
@@ -7,6 +7,7 @@ import os
 from functools import cmp_to_key
 
 from test import support, seq_tests
+from test.support import ALWAYS_EQ, NEVER_EQ
 
 
 class CommonTest(seq_tests.CommonTest):
@@ -329,6 +330,20 @@ class CommonTest(seq_tests.CommonTest):
 
         self.assertRaises(TypeError, a.remove)
 
+        a = self.type2test([1, 2])
+        self.assertRaises(ValueError, a.remove, NEVER_EQ)
+        self.assertEqual(a, [1, 2])
+        a.remove(ALWAYS_EQ)
+        self.assertEqual(a, [2])
+        a = self.type2test([ALWAYS_EQ])
+        a.remove(1)
+        self.assertEqual(a, [])
+        a = self.type2test([ALWAYS_EQ])
+        a.remove(NEVER_EQ)
+        self.assertEqual(a, [])
+        a = self.type2test([NEVER_EQ])
+        self.assertRaises(ValueError, a.remove, ALWAYS_EQ)
+
         class BadExc(Exception):
             pass
 
index 65b110ef7818dac652ad6414628b94fd941269ea..1d9ad588987f9769bd98048558164fbdbe834027 100644 (file)
@@ -6,6 +6,7 @@ import unittest
 import sys
 import pickle
 from test import support
+from test.support import ALWAYS_EQ, NEVER_EQ
 
 # Various iterables
 # This is used for checking the constructor (here and in test_deque.py)
@@ -221,15 +222,15 @@ class CommonTest(unittest.TestCase):
         self.assertRaises(TypeError, u.__contains__)
 
     def test_contains_fake(self):
-        class AllEq:
-            # Sequences must use rich comparison against each item
-            # (unless "is" is true, or an earlier item answered)
-            # So instances of AllEq must be found in all non-empty sequences.
-            def __eq__(self, other):
-                return True
-            __hash__ = None # Can't meet hash invariant requirements
-        self.assertNotIn(AllEq(), self.type2test([]))
-        self.assertIn(AllEq(), self.type2test([1]))
+        # Sequences must use rich comparison against each item
+        # (unless "is" is true, or an earlier item answered)
+        # So ALWAYS_EQ must be found in all non-empty sequences.
+        self.assertNotIn(ALWAYS_EQ, self.type2test([]))
+        self.assertIn(ALWAYS_EQ, self.type2test([1]))
+        self.assertIn(1, self.type2test([ALWAYS_EQ]))
+        self.assertNotIn(NEVER_EQ, self.type2test([]))
+        self.assertNotIn(ALWAYS_EQ, self.type2test([NEVER_EQ]))
+        self.assertIn(NEVER_EQ, self.type2test([ALWAYS_EQ]))
 
     def test_contains_order(self):
         # Sequences must test in-order.  If a rich comparison has side
@@ -350,6 +351,11 @@ class CommonTest(unittest.TestCase):
         self.assertEqual(a.count(1), 3)
         self.assertEqual(a.count(3), 0)
 
+        self.assertEqual(a.count(ALWAYS_EQ), 9)
+        self.assertEqual(self.type2test([ALWAYS_EQ, ALWAYS_EQ]).count(1), 2)
+        self.assertEqual(self.type2test([ALWAYS_EQ, ALWAYS_EQ]).count(NEVER_EQ), 2)
+        self.assertEqual(self.type2test([NEVER_EQ, NEVER_EQ]).count(ALWAYS_EQ), 0)
+
         self.assertRaises(TypeError, a.count)
 
         class BadExc(Exception):
@@ -378,6 +384,11 @@ class CommonTest(unittest.TestCase):
         self.assertEqual(u.index(0, 3, 4), 3)
         self.assertRaises(ValueError, u.index, 2, 0, -10)
 
+        self.assertEqual(u.index(ALWAYS_EQ), 0)
+        self.assertEqual(self.type2test([ALWAYS_EQ, ALWAYS_EQ]).index(1), 0)
+        self.assertEqual(self.type2test([ALWAYS_EQ, ALWAYS_EQ]).index(NEVER_EQ), 0)
+        self.assertRaises(ValueError, self.type2test([NEVER_EQ, NEVER_EQ]).index, ALWAYS_EQ)
+
         self.assertRaises(TypeError, u.index)
 
         class BadExc(Exception):
index c82037eea5232277b8923847821e4992d3cb5af3..d34f2efaed537fd18bcb79ca83911c98f0979460 100644 (file)
@@ -113,7 +113,7 @@ __all__ = [
     "run_with_locale", "swap_item",
     "swap_attr", "Matcher", "set_memlimit", "SuppressCrashReport", "sortdict",
     "run_with_tz", "PGO", "missing_compiler_executable", "fd_count",
-    "ALWAYS_EQ", "LARGEST", "SMALLEST"
+    "ALWAYS_EQ", "NEVER_EQ", "LARGEST", "SMALLEST"
     ]
 
 class Error(Exception):
@@ -3115,6 +3115,17 @@ class _ALWAYS_EQ:
 
 ALWAYS_EQ = _ALWAYS_EQ()
 
+class _NEVER_EQ:
+    """
+    Object that is not equal to anything.
+    """
+    def __eq__(self, other):
+        return False
+    def __ne__(self, other):
+        return True
+
+NEVER_EQ = _NEVER_EQ()
+
 @functools.total_ordering
 class _LARGEST:
     """
index 542b28419e2c8e2cd1937471aec8142a3cd74f09..6aceda23e9bd827dfeb10cf8dfde3861d05b77a2 100644 (file)
@@ -3,7 +3,7 @@
 import sys
 import unittest
 from test.support import run_unittest, TESTFN, unlink, cpython_only
-from test.support import check_free_after_iterating
+from test.support import check_free_after_iterating, ALWAYS_EQ, NEVER_EQ
 import pickle
 import collections.abc
 
@@ -41,6 +41,14 @@ class IteratingSequenceClass:
     def __iter__(self):
         return BasicIterClass(self.n)
 
+class IteratorProxyClass:
+    def __init__(self, i):
+        self.i = i
+    def __next__(self):
+        return next(self.i)
+    def __iter__(self):
+        return self
+
 class SequenceClass:
     def __init__(self, n):
         self.n = n
@@ -50,6 +58,12 @@ class SequenceClass:
         else:
             raise IndexError
 
+class SequenceProxyClass:
+    def __init__(self, s):
+        self.s = s
+    def __getitem__(self, i):
+        return self.s[i]
+
 class UnlimitedSequenceClass:
     def __getitem__(self, i):
         return i
@@ -635,6 +649,13 @@ class TestCase(unittest.TestCase):
             for i in "abc", -1, 5, 42.42, (3, 4), [], {1: 1}, 3-12j, sc5:
                 self.assertNotIn(i, sc5)
 
+        self.assertIn(ALWAYS_EQ, IteratorProxyClass(iter([1])))
+        self.assertIn(ALWAYS_EQ, SequenceProxyClass([1]))
+        self.assertNotIn(ALWAYS_EQ, IteratorProxyClass(iter([NEVER_EQ])))
+        self.assertNotIn(ALWAYS_EQ, SequenceProxyClass([NEVER_EQ]))
+        self.assertIn(NEVER_EQ, IteratorProxyClass(iter([ALWAYS_EQ])))
+        self.assertIn(NEVER_EQ, SequenceProxyClass([ALWAYS_EQ]))
+
         self.assertRaises(TypeError, lambda: 3 in 12)
         self.assertRaises(TypeError, lambda: 3 not in map)
 
diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-07-22-11-05-05.bpo-37648.6TY2L-.rst b/Misc/NEWS.d/next/Core and Builtins/2019-07-22-11-05-05.bpo-37648.6TY2L-.rst
new file mode 100644 (file)
index 0000000..3c11d3d
--- /dev/null
@@ -0,0 +1,3 @@
+Fixed minor inconsistency in :meth:`list.__contains__`,
+:meth:`tuple.__contains__` and a few other places. The collection's item is
+now always at the left and the needle is on the right of ``==``.
index e9e6c5682d62e9116ad72580444b243677246cea..4d503a418a2e648594349874fd9055f57a814bac 100644 (file)
@@ -937,7 +937,7 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
     ENSURE_FUTURE_ALIVE(self)
 
     if (self->fut_callback0 != NULL) {
-        int cmp = PyObject_RichCompareBool(fn, self->fut_callback0, Py_EQ);
+        int cmp = PyObject_RichCompareBool(self->fut_callback0, fn, Py_EQ);
         if (cmp == -1) {
             return NULL;
         }
@@ -962,7 +962,7 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
     if (len == 1) {
         PyObject *cb_tup = PyList_GET_ITEM(self->fut_callbacks, 0);
         int cmp = PyObject_RichCompareBool(
-            fn, PyTuple_GET_ITEM(cb_tup, 0), Py_EQ);
+            PyTuple_GET_ITEM(cb_tup, 0), fn, Py_EQ);
         if (cmp == -1) {
             return NULL;
         }
@@ -984,7 +984,7 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
         int ret;
         PyObject *item = PyList_GET_ITEM(self->fut_callbacks, i);
         Py_INCREF(item);
-        ret = PyObject_RichCompareBool(fn, PyTuple_GET_ITEM(item, 0), Py_EQ);
+        ret = PyObject_RichCompareBool(PyTuple_GET_ITEM(item, 0), fn, Py_EQ);
         if (ret == 0) {
             if (j < len) {
                 PyList_SET_ITEM(newlist, j, item);
index da30cbb758e27ec1b8ed92fb5f7359d0d0104641..3d54b844fe0732cfb57c71c2131b198f728f3300 100644 (file)
@@ -5600,8 +5600,7 @@ list_contains(PyListObject *a, PyObject *el)
     int cmp;
 
     for (i = 0, cmp = 0 ; cmp == 0 && i < Py_SIZE(a); ++i)
-        cmp = PyObject_RichCompareBool(el, PyList_GET_ITEM(a, i),
-                                           Py_EQ);
+        cmp = PyObject_RichCompareBool(PyList_GET_ITEM(a, i), el, Py_EQ);
     return cmp;
 }
 
index db1c3064db6f1f6f9cca94c6f55ec763e10fd7e9..f93d73fa7571ab9c3350ade41ac0db09fa82ceed 100644 (file)
@@ -2016,7 +2016,7 @@ _PySequence_IterSearch(PyObject *seq, PyObject *obj, int operation)
             break;
         }
 
-        cmp = PyObject_RichCompareBool(obj, item, Py_EQ);
+        cmp = PyObject_RichCompareBool(item, obj, Py_EQ);
         Py_DECREF(item);
         if (cmp < 0)
             goto Fail;
index b6205d93ca1408f134902aa1943040f1445d8fb9..f168ad5d2f00de95aae41c4a4f0f0b29aa47521b 100644 (file)
@@ -4392,7 +4392,7 @@ dictitems_contains(_PyDictViewObject *dv, PyObject *obj)
         return 0;
     }
     Py_INCREF(found);
-    result = PyObject_RichCompareBool(value, found, Py_EQ);
+    result = PyObject_RichCompareBool(found, value, Py_EQ);
     Py_DECREF(found);
     return result;
 }
index d012ab933a9eb5a0cbd5d22938ebc5879b3c1a1a..cea9b24a3b2fb65367ceeb9e705c6448e84f7b65 100644 (file)
@@ -449,8 +449,7 @@ list_contains(PyListObject *a, PyObject *el)
     int cmp;
 
     for (i = 0, cmp = 0 ; cmp == 0 && i < Py_SIZE(a); ++i)
-        cmp = PyObject_RichCompareBool(el, PyList_GET_ITEM(a, i),
-                                           Py_EQ);
+        cmp = PyObject_RichCompareBool(PyList_GET_ITEM(a, i), el, Py_EQ);
     return cmp;
 }
 
index fc2d2742dd2ca69adf3b96c39565c624d637e6a4..aeaf845d74cfa1b348186e6e1a7159f5360de55a 100644 (file)
@@ -403,8 +403,7 @@ tuplecontains(PyTupleObject *a, PyObject *el)
     int cmp;
 
     for (i = 0, cmp = 0 ; cmp == 0 && i < Py_SIZE(a); ++i)
-        cmp = PyObject_RichCompareBool(el, PyTuple_GET_ITEM(a, i),
-                                           Py_EQ);
+        cmp = PyObject_RichCompareBool(PyTuple_GET_ITEM(a, i), el, Py_EQ);
     return cmp;
 }