Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a174813

Browse files
committed
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.
1 parent 0a6864e commit a174813

2 files changed

Lines changed: 47 additions & 16 deletions

File tree

Lib/test/test_coercion.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,45 @@ def do_prefix_binops():
125125
else:
126126
print '=', format_result(x)
127127

128+
# New-style class version of CoerceNumber
129+
class CoerceTo(object):
130+
def __init__(self, arg):
131+
self.arg = arg
132+
def __coerce__(self, other):
133+
if isinstance(other, CoerceTo):
134+
return self.arg, other.arg
135+
else:
136+
return self.arg, other
137+
138+
def assert_(expr, msg=None):
139+
if not expr:
140+
raise AssertionError, msg
141+
142+
def do_cmptypes():
143+
# Built-in tp_compare slots expect their arguments to have the
144+
# same type, but a user-defined __coerce__ doesn't have to obey.
145+
# SF #980352
146+
evil_coercer = CoerceTo(42)
147+
# Make sure these don't crash any more
148+
assert_(cmp(u'fish', evil_coercer) != 0)
149+
assert_(cmp(slice(1), evil_coercer) != 0)
150+
# ...but that this still works
151+
class WackyComparer(object):
152+
def __cmp__(self, other):
153+
assert_(other == 42, 'expected evil_coercer, got %r' % other)
154+
return 0
155+
assert_(cmp(WackyComparer(), evil_coercer) == 0)
156+
# ...and classic classes too, since that code path is a little different
157+
class ClassicWackyComparer:
158+
def __cmp__(self, other):
159+
assert_(other == 42, 'expected evil_coercer, got %r' % other)
160+
return 0
161+
assert_(cmp(ClassicWackyComparer(), evil_coercer) == 0)
162+
128163
warnings.filterwarnings("ignore",
129164
r'complex divmod\(\), // and % are deprecated',
130165
DeprecationWarning,
131166
r'test.test_coercion$')
132167
do_infix_binops()
133168
do_prefix_binops()
169+
do_cmptypes()

Objects/object.c

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -606,33 +606,28 @@ try_3way_compare(PyObject *v, PyObject *w)
606606
w->ob_type->tp_compare == _PyObject_SlotCompare)
607607
return _PyObject_SlotCompare(v, w);
608608

609-
/* Try coercion; if it fails, give up */
609+
/* If we're here, v and w,
610+
a) are not instances;
611+
b) have different types or a type without tp_compare; and
612+
c) don't have a user-defined tp_compare.
613+
tp_compare implementations in C assume that both arguments
614+
have their type, so we give up if the coercion fails or if
615+
it yields types which are still incompatible (which can
616+
happen with a user-defined nb_coerce).
617+
*/
610618
c = PyNumber_CoerceEx(&v, &w);
611619
if (c < 0)
612620
return -2;
613621
if (c > 0)
614622
return 2;
615-
616-
/* Try v's comparison, if defined */
617-
if ((f = v->ob_type->tp_compare) != NULL) {
623+
f = v->ob_type->tp_compare;
624+
if (f != NULL && f == w->ob_type->tp_compare) {
618625
c = (*f)(v, w);
619626
Py_DECREF(v);
620627
Py_DECREF(w);
621628
return adjust_tp_compare(c);
622629
}
623630

624-
/* Try w's comparison, if defined */
625-
if ((f = w->ob_type->tp_compare) != NULL) {
626-
c = (*f)(w, v); /* swapped! */
627-
Py_DECREF(v);
628-
Py_DECREF(w);
629-
c = adjust_tp_compare(c);
630-
if (c >= -1)
631-
return -c; /* Swapped! */
632-
else
633-
return c;
634-
}
635-
636631
/* No comparison defined */
637632
Py_DECREF(v);
638633
Py_DECREF(w);

0 commit comments

Comments
 (0)