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

Skip to content

Commit ab3b034

Browse files
committed
Hopefully fix 3-way comparisons. This unfortunately adds yet another
hack, and it's even more disgusting than a PyInstance_Check() call. If the tp_compare slot is the slot used for overrides in Python, it's always called. Add some tests that show what should work too.
1 parent eb94905 commit ab3b034

4 files changed

Lines changed: 53 additions & 6 deletions

File tree

Include/object.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,10 @@ extern DL_IMPORT(int) PyNumber_CoerceEx(PyObject **, PyObject **);
346346

347347
extern DL_IMPORT(void) (*PyObject_ClearWeakRefs)(PyObject *);
348348

349+
/* A slot function whose address we need to compare */
350+
extern int _PyObject_SlotCompare(PyObject *, PyObject *);
351+
352+
349353
/* PyObject_Dir(obj) acts like Python __builtin__.dir(obj), returning a
350354
list of strings. PyObject_Dir(NULL) is like __builtin__.dir(),
351355
returning the names of the current locals. In this case, if there are

Lib/test/test_descr.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1831,6 +1831,33 @@ def __hash__(self):
18311831
verify(cistr('ONe') in d)
18321832
verify(d.get(cistr('thrEE')) == 3)
18331833

1834+
def classic_comparisons():
1835+
if verbose: print "Testing classic comparisons..."
1836+
for base in (int, object):
1837+
if verbose: print " (base = %s)" % base
1838+
class C(base):
1839+
def __init__(self, value):
1840+
self.value = int(value)
1841+
def __cmp__(self, other):
1842+
if isinstance(other, C):
1843+
return cmp(self.value, other.value)
1844+
if isinstance(other, int) or isinstance(other, long):
1845+
return cmp(self.value, other)
1846+
return NotImplemented
1847+
c1 = C(1)
1848+
c2 = C(2)
1849+
c3 = C(3)
1850+
verify(c1 == 1)
1851+
c = {1: c1, 2: c2, 3: c3}
1852+
for x in 1, 2, 3:
1853+
for y in 1, 2, 3:
1854+
verify(cmp(c[x], c[y]) == cmp(x, y), "x=%d, y=%d" % (x, y))
1855+
for op in "<", "<=", "==", "!=", ">", ">=":
1856+
verify(eval("c[x] %s c[y]" % op) == eval("x %s y" % op),
1857+
"x=%d, y=%d" % (x, y))
1858+
verify(cmp(c[x], y) == cmp(x, y), "x=%d, y=%d" % (x, y))
1859+
verify(cmp(x, c[y]) == cmp(x, y), "x=%d, y=%d" % (x, y))
1860+
18341861

18351862
def all():
18361863
lists()
@@ -1869,6 +1896,7 @@ def all():
18691896
keywords()
18701897
restricted()
18711898
str_subclass_as_dict_key()
1899+
classic_comparisons()
18721900

18731901
all()
18741902

Objects/object.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,11 +455,25 @@ try_3way_compare(PyObject *v, PyObject *w)
455455
/* Comparisons involving instances are given to instance_compare,
456456
which has the same return conventions as this function. */
457457

458+
f = v->ob_type->tp_compare;
458459
if (PyInstance_Check(v))
459-
return (*v->ob_type->tp_compare)(v, w);
460+
return (*f)(v, w);
460461
if (PyInstance_Check(w))
461462
return (*w->ob_type->tp_compare)(v, w);
462463

464+
/* If both have the same (non-NULL) tp_compare, use it. */
465+
if (f != NULL && f == w->ob_type->tp_compare) {
466+
c = (*f)(v, w);
467+
if (c < 0 && PyErr_Occurred())
468+
return -1;
469+
return c < 0 ? -1 : c > 0 ? 1 : 0;
470+
}
471+
472+
/* If either tp_compare is _PyObject_SlotCompare, that's safe. */
473+
if (f == _PyObject_SlotCompare ||
474+
w->ob_type->tp_compare == _PyObject_SlotCompare)
475+
return _PyObject_SlotCompare(v, w);
476+
463477
/* Try coercion; if it fails, give up */
464478
c = PyNumber_CoerceEx(&v, &w);
465479
if (c < 0)

Objects/typeobject.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2761,17 +2761,18 @@ half_compare(PyObject *self, PyObject *other)
27612761
return 2;
27622762
}
27632763

2764-
static int
2765-
slot_tp_compare(PyObject *self, PyObject *other)
2764+
/* This slot is published for the benefit of try_3way_compare in object.c */
2765+
int
2766+
_PyObject_SlotCompare(PyObject *self, PyObject *other)
27662767
{
27672768
int c;
27682769

2769-
if (self->ob_type->tp_compare == slot_tp_compare) {
2770+
if (self->ob_type->tp_compare == _PyObject_SlotCompare) {
27702771
c = half_compare(self, other);
27712772
if (c <= 1)
27722773
return c;
27732774
}
2774-
if (other->ob_type->tp_compare == slot_tp_compare) {
2775+
if (other->ob_type->tp_compare == _PyObject_SlotCompare) {
27752776
c = half_compare(other, self);
27762777
if (c < -1)
27772778
return -2;
@@ -3190,7 +3191,7 @@ override_slots(PyTypeObject *type, PyObject *dict)
31903191
PyDict_GetItemString(dict, "__repr__"))
31913192
type->tp_print = NULL;
31923193

3193-
TPSLOT("__cmp__", tp_compare, slot_tp_compare);
3194+
TPSLOT("__cmp__", tp_compare, _PyObject_SlotCompare);
31943195
TPSLOT("__repr__", tp_repr, slot_tp_repr);
31953196
TPSLOT("__hash__", tp_hash, slot_tp_hash);
31963197
TPSLOT("__call__", tp_call, slot_tp_call);

0 commit comments

Comments
 (0)