]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Dima Dorfman's patch for coercion/comparison of C types (patch #995939), with
authorArmin Rigo <arigo@tunes.org>
Thu, 23 Dec 2004 22:13:13 +0000 (22:13 +0000)
committerArmin Rigo <arigo@tunes.org>
Thu, 23 Dec 2004 22:13:13 +0000 (22:13 +0000)
a minor change after the coercion, to accept two objects not necessarily of
the same type but with the same tp_compare.

Lib/test/test_coercion.py
Objects/object.c

index 331771f67295e0e03663ebe5a6607e4151e166e4..37d735b3ba5bfb64206e129f683b4db885dfca5c 100644 (file)
@@ -125,9 +125,45 @@ def do_prefix_binops():
                 else:
                     print '=', format_result(x)
 
+# New-style class version of CoerceNumber
+class CoerceTo(object):
+    def __init__(self, arg):
+        self.arg = arg
+    def __coerce__(self, other):
+        if isinstance(other, CoerceTo):
+            return self.arg, other.arg
+        else:
+            return self.arg, other
+
+def assert_(expr, msg=None):
+    if not expr:
+        raise AssertionError, msg
+
+def do_cmptypes():
+    # Built-in tp_compare slots expect their arguments to have the
+    # same type, but a user-defined __coerce__ doesn't have to obey.
+    # SF #980352
+    evil_coercer = CoerceTo(42)
+    # Make sure these don't crash any more
+    assert_(cmp(u'fish', evil_coercer) != 0)
+    assert_(cmp(slice(1), evil_coercer) != 0)
+    # ...but that this still works
+    class WackyComparer(object):
+        def __cmp__(self, other):
+            assert_(other == 42, 'expected evil_coercer, got %r' % other)
+            return 0
+    assert_(cmp(WackyComparer(), evil_coercer) == 0)
+    # ...and classic classes too, since that code path is a little different
+    class ClassicWackyComparer:
+        def __cmp__(self, other):
+            assert_(other == 42, 'expected evil_coercer, got %r' % other)
+            return 0
+    assert_(cmp(ClassicWackyComparer(), evil_coercer) == 0)
+
 warnings.filterwarnings("ignore",
                         r'complex divmod\(\), // and % are deprecated',
                         DeprecationWarning,
                         r'test.test_coercion$')
 do_infix_binops()
 do_prefix_binops()
+do_cmptypes()
index 3f70009bd3ff8101a0ee5859bfa776a77146e7d5..d86d74f6d7659c14fcdd7eea0506156fc6fc6e17 100644 (file)
@@ -606,33 +606,28 @@ try_3way_compare(PyObject *v, PyObject *w)
            w->ob_type->tp_compare == _PyObject_SlotCompare)
                return _PyObject_SlotCompare(v, w);
 
-       /* Try coercion; if it fails, give up */
+       /* If we're here, v and w,
+           a) are not instances;
+           b) have different types or a type without tp_compare; and
+           c) don't have a user-defined tp_compare.
+          tp_compare implementations in C assume that both arguments
+          have their type, so we give up if the coercion fails or if
+          it yields types which are still incompatible (which can
+          happen with a user-defined nb_coerce).
+       */
        c = PyNumber_CoerceEx(&v, &w);
        if (c < 0)
                return -2;
        if (c > 0)
                return 2;
-
-       /* Try v's comparison, if defined */
-       if ((f = v->ob_type->tp_compare) != NULL) {
+       f = v->ob_type->tp_compare;
+       if (f != NULL && f == w->ob_type->tp_compare) {
                c = (*f)(v, w);
                Py_DECREF(v);
                Py_DECREF(w);
                return adjust_tp_compare(c);
        }
 
-       /* Try w's comparison, if defined */
-       if ((f = w->ob_type->tp_compare) != NULL) {
-               c = (*f)(w, v); /* swapped! */
-               Py_DECREF(v);
-               Py_DECREF(w);
-               c = adjust_tp_compare(c);
-               if (c >= -1)
-                       return -c; /* Swapped! */
-               else
-                       return c;
-       }
-
        /* No comparison defined */
        Py_DECREF(v);
        Py_DECREF(w);