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.
This commit is contained in:
Armin Rigo 2004-12-23 22:13:13 +00:00
parent 0a6864ecc5
commit a174813113
2 changed files with 47 additions and 16 deletions

View File

@ -125,9 +125,45 @@ def do_prefix_binops():
else: else:
print '=', format_result(x) 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", warnings.filterwarnings("ignore",
r'complex divmod\(\), // and % are deprecated', r'complex divmod\(\), // and % are deprecated',
DeprecationWarning, DeprecationWarning,
r'test.test_coercion$') r'test.test_coercion$')
do_infix_binops() do_infix_binops()
do_prefix_binops() do_prefix_binops()
do_cmptypes()

View File

@ -606,33 +606,28 @@ try_3way_compare(PyObject *v, PyObject *w)
w->ob_type->tp_compare == _PyObject_SlotCompare) w->ob_type->tp_compare == _PyObject_SlotCompare)
return _PyObject_SlotCompare(v, w); 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); c = PyNumber_CoerceEx(&v, &w);
if (c < 0) if (c < 0)
return -2; return -2;
if (c > 0) if (c > 0)
return 2; return 2;
f = v->ob_type->tp_compare;
/* Try v's comparison, if defined */ if (f != NULL && f == w->ob_type->tp_compare) {
if ((f = v->ob_type->tp_compare) != NULL) {
c = (*f)(v, w); c = (*f)(v, w);
Py_DECREF(v); Py_DECREF(v);
Py_DECREF(w); Py_DECREF(w);
return adjust_tp_compare(c); 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 */ /* No comparison defined */
Py_DECREF(v); Py_DECREF(v);
Py_DECREF(w); Py_DECREF(w);