From a1748131132ebc83f443b2a8ddc5073bfefa504f Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Thu, 23 Dec 2004 22:13:13 +0000 Subject: [PATCH] Dima Dorfman's patch for coercion/comparison of C types (patch #995939), with 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 | 36 ++++++++++++++++++++++++++++++++++++ Objects/object.c | 27 +++++++++++---------------- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/Lib/test/test_coercion.py b/Lib/test/test_coercion.py index 331771f6729..37d735b3ba5 100644 --- a/Lib/test/test_coercion.py +++ b/Lib/test/test_coercion.py @@ -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() diff --git a/Objects/object.c b/Objects/object.c index 3f70009bd3f..d86d74f6d76 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -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);