It's a fact: for binary operators, *under certain circumstances*,
__rop__ now takes precendence over __op__.  Those circumstances are:

  - Both arguments are new-style classes
  - Both arguments are new-style numbers
  - Their implementation slots for tp_op differ
  - Their types differ
  - The right argument's type is a subtype of the left argument's type

Also did this for the ternary operator (pow) -- only the binary case
is dealt with properly though, since __rpow__ is not supported anyway.
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py
index 2c5e7a4..22ae09a 100644
--- a/Lib/test/test_descr.py
+++ b/Lib/test/test_descr.py
@@ -1,8 +1,12 @@
-# Test descriptor-related enhancements
+# Test enhancements related to descriptors and new-style classes
 
 from test_support import verify, verbose, TestFailed, TESTFN
 from copy import deepcopy
 
+def vereq(a, b):
+    if a != b:
+        raise TestFailed, "%r != %r" % (a, b)
+
 def testunop(a, res, expr="len(a)", meth="__len__"):
     if verbose: print "checking", expr
     dict = {'a': a}
@@ -2133,6 +2137,36 @@
     a.bar.append(4)
     verify(d.bar == [1,2,3])
 
+def binopoverride():
+    if verbose: print "Testing overrides of binary operations..."
+    class I(int):
+        def __repr__(self):
+            return "I(%r)" % int(self)
+        def __add__(self, other):
+            return I(int(self) + int(other))
+        __radd__ = __add__
+        def __pow__(self, other, mod=None):
+            if mod is None:
+                return I(pow(int(self), int(other)))
+            else:
+                return I(pow(int(self), int(other), int(mod)))
+        def __rpow__(self, other, mod=None):
+            if mod is None:
+                return I(pow(int(other), int(self), mod))
+            else:
+                return I(pow(int(other), int(self), int(mod)))
+            
+    vereq(`I(1) + I(2)`, "I(3)")
+    vereq(`I(1) + 2`, "I(3)")
+    vereq(`1 + I(2)`, "I(3)")
+    vereq(`I(2) ** I(3)`, "I(8)")
+    vereq(`2 ** I(3)`, "I(8)")
+    vereq(`I(2) ** 3`, "I(8)")
+    vereq(`pow(I(2), I(3), I(5))`, "I(3)")
+    class S(str):
+        def __eq__(self, other):
+            return self.lower() == other.lower()
+
 
 def test_main():
     lists()
@@ -2178,6 +2212,7 @@
     setclass()
     pickles()
     copies()
+    binopoverride()
     if verbose: print "All OK"
 
 if __name__ == "__main__":
diff --git a/Objects/abstract.c b/Objects/abstract.c
index fdf1a44..c353159 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -300,11 +300,14 @@
 
   v	w	Action
   -------------------------------------------------------------------
-  new	new	v.op(v,w), w.op(v,w)
+  new	new	w.op(v,w)[*], v.op(v,w), w.op(v,w)
   new	old	v.op(v,w), coerce(v,w), v.op(v,w)
   old	new	w.op(v,w), coerce(v,w), v.op(v,w)
   old	old	coerce(v,w), v.op(v,w)
 
+  [*] only when v->ob_type != w->ob_type && w->ob_type is a subclass of
+      v->ob_type
+
   Legend:
   -------
   * new == new style number
@@ -318,29 +321,35 @@
 binary_op1(PyObject *v, PyObject *w, const int op_slot)
 {
 	PyObject *x;
-	binaryfunc *slot;
-	if (v->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(v)) {
-		slot = NB_BINOP(v->ob_type->tp_as_number, op_slot);
-		if (*slot) {
-			x = (*slot)(v, w);
-			if (x != Py_NotImplemented) {
-				return x;
-			}
-			Py_DECREF(x); /* can't do it */
-		}
-		if (v->ob_type == w->ob_type) {
-			goto binop_error;
-		}
+	binaryfunc slotv = NULL;
+	binaryfunc slotw = NULL;
+
+	if (v->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(v))
+		slotv = *NB_BINOP(v->ob_type->tp_as_number, op_slot);
+	if (w->ob_type != v->ob_type &&
+	    w->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(w)) {
+		slotw = *NB_BINOP(w->ob_type->tp_as_number, op_slot);
+		if (slotw == slotv)
+			slotw = NULL;
 	}
-	if (w->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(w)) {
-		slot = NB_BINOP(w->ob_type->tp_as_number, op_slot);
-		if (*slot) {
-			x = (*slot)(v, w);
-			if (x != Py_NotImplemented) {
-				return x;
-			}
-			Py_DECREF(x); /* can't do it */
-		}
+	if (slotw && PyType_IsSubtype(w->ob_type, v->ob_type)) {
+		x = slotw(v, w);
+		if (x != Py_NotImplemented)
+			return x;
+		Py_DECREF(x); /* can't do it */
+		slotw = NULL;
+	}
+	if (slotv) {
+		x = slotv(v, w);
+		if (x != Py_NotImplemented)
+			return x;
+		Py_DECREF(x); /* can't do it */
+	}
+	if (slotw) {
+		x = slotw(v, w);
+		if (x != Py_NotImplemented)
+			return x;
+		Py_DECREF(x); /* can't do it */
 	}
 	if (!NEW_STYLE_NUMBER(v) || !NEW_STYLE_NUMBER(w)) {
 		int err = PyNumber_CoerceEx(&v, &w);
@@ -350,9 +359,10 @@
 		if (err == 0) {
 			PyNumberMethods *mv = v->ob_type->tp_as_number;
 			if (mv) {
-				slot = NB_BINOP(mv, op_slot);
-				if (*slot) {
-					PyObject *x = (*slot)(v, w);
+				binaryfunc slot;
+				slot = *NB_BINOP(mv, op_slot);
+				if (slot) {
+					PyObject *x = slot(v, w);
 					Py_DECREF(v);
 					Py_DECREF(w);
 					return x;
@@ -363,7 +373,6 @@
 			Py_DECREF(w);
 		}
 	}
-binop_error:
 	Py_INCREF(Py_NotImplemented);
 	return Py_NotImplemented;
 }
@@ -420,6 +429,18 @@
 	register ternaryfunc *slot;
 	
 	mv = v->ob_type->tp_as_number;
+	mw = w->ob_type->tp_as_number;
+	if (v->ob_type != w->ob_type && mw && NEW_STYLE_NUMBER(w)) {
+		slot = NB_TERNOP(mw, op_slot);
+		if (*slot && *slot != *NB_TERNOP(mv, op_slot) &&
+		    PyType_IsSubtype(w->ob_type, v->ob_type)) {
+			x = (*slot)(v, w, z);
+			if (x != Py_NotImplemented)
+				return x;
+			/* Can't do it... fall through */
+			Py_DECREF(x);
+		}
+	}
 	if (mv != NULL && NEW_STYLE_NUMBER(v)) {
 		/* try v.op(v,w,z) */
 		slot = NB_TERNOP(mv, op_slot);
@@ -435,7 +456,6 @@
 			goto ternary_error;
 		}
 	}
-	mw = w->ob_type->tp_as_number;
 	if (mw != NULL && NEW_STYLE_NUMBER(w)) {
 		/* try w.op(v,w,z) */
 		slot = NB_TERNOP(mw,op_slot);