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.
This commit is contained in:
parent
9bea3abf0d
commit
4bb1e36eec
|
@ -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 test_support import verify, verbose, TestFailed, TESTFN
|
||||||
from copy import deepcopy
|
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__"):
|
def testunop(a, res, expr="len(a)", meth="__len__"):
|
||||||
if verbose: print "checking", expr
|
if verbose: print "checking", expr
|
||||||
dict = {'a': a}
|
dict = {'a': a}
|
||||||
|
@ -2133,6 +2137,36 @@ def copies():
|
||||||
a.bar.append(4)
|
a.bar.append(4)
|
||||||
verify(d.bar == [1,2,3])
|
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():
|
def test_main():
|
||||||
lists()
|
lists()
|
||||||
|
@ -2178,6 +2212,7 @@ def test_main():
|
||||||
setclass()
|
setclass()
|
||||||
pickles()
|
pickles()
|
||||||
copies()
|
copies()
|
||||||
|
binopoverride()
|
||||||
if verbose: print "All OK"
|
if verbose: print "All OK"
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -300,11 +300,14 @@ PyNumber_Check(PyObject *o)
|
||||||
|
|
||||||
v w Action
|
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)
|
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 new w.op(v,w), coerce(v,w), v.op(v,w)
|
||||||
old old 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:
|
Legend:
|
||||||
-------
|
-------
|
||||||
* new == new style number
|
* new == new style number
|
||||||
|
@ -318,29 +321,35 @@ static PyObject *
|
||||||
binary_op1(PyObject *v, PyObject *w, const int op_slot)
|
binary_op1(PyObject *v, PyObject *w, const int op_slot)
|
||||||
{
|
{
|
||||||
PyObject *x;
|
PyObject *x;
|
||||||
binaryfunc *slot;
|
binaryfunc slotv = NULL;
|
||||||
if (v->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(v)) {
|
binaryfunc slotw = NULL;
|
||||||
slot = NB_BINOP(v->ob_type->tp_as_number, op_slot);
|
|
||||||
if (*slot) {
|
if (v->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(v))
|
||||||
x = (*slot)(v, w);
|
slotv = *NB_BINOP(v->ob_type->tp_as_number, op_slot);
|
||||||
if (x != Py_NotImplemented) {
|
if (w->ob_type != v->ob_type &&
|
||||||
return x;
|
w->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(w)) {
|
||||||
}
|
slotw = *NB_BINOP(w->ob_type->tp_as_number, op_slot);
|
||||||
Py_DECREF(x); /* can't do it */
|
if (slotw == slotv)
|
||||||
}
|
slotw = NULL;
|
||||||
if (v->ob_type == w->ob_type) {
|
|
||||||
goto binop_error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (w->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(w)) {
|
if (slotw && PyType_IsSubtype(w->ob_type, v->ob_type)) {
|
||||||
slot = NB_BINOP(w->ob_type->tp_as_number, op_slot);
|
x = slotw(v, w);
|
||||||
if (*slot) {
|
if (x != Py_NotImplemented)
|
||||||
x = (*slot)(v, w);
|
return x;
|
||||||
if (x != Py_NotImplemented) {
|
Py_DECREF(x); /* can't do it */
|
||||||
return x;
|
slotw = NULL;
|
||||||
}
|
}
|
||||||
Py_DECREF(x); /* can't do it */
|
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)) {
|
if (!NEW_STYLE_NUMBER(v) || !NEW_STYLE_NUMBER(w)) {
|
||||||
int err = PyNumber_CoerceEx(&v, &w);
|
int err = PyNumber_CoerceEx(&v, &w);
|
||||||
|
@ -350,9 +359,10 @@ binary_op1(PyObject *v, PyObject *w, const int op_slot)
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
PyNumberMethods *mv = v->ob_type->tp_as_number;
|
PyNumberMethods *mv = v->ob_type->tp_as_number;
|
||||||
if (mv) {
|
if (mv) {
|
||||||
slot = NB_BINOP(mv, op_slot);
|
binaryfunc slot;
|
||||||
if (*slot) {
|
slot = *NB_BINOP(mv, op_slot);
|
||||||
PyObject *x = (*slot)(v, w);
|
if (slot) {
|
||||||
|
PyObject *x = slot(v, w);
|
||||||
Py_DECREF(v);
|
Py_DECREF(v);
|
||||||
Py_DECREF(w);
|
Py_DECREF(w);
|
||||||
return x;
|
return x;
|
||||||
|
@ -363,7 +373,6 @@ binary_op1(PyObject *v, PyObject *w, const int op_slot)
|
||||||
Py_DECREF(w);
|
Py_DECREF(w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binop_error:
|
|
||||||
Py_INCREF(Py_NotImplemented);
|
Py_INCREF(Py_NotImplemented);
|
||||||
return Py_NotImplemented;
|
return Py_NotImplemented;
|
||||||
}
|
}
|
||||||
|
@ -420,6 +429,18 @@ ternary_op(PyObject *v,
|
||||||
register ternaryfunc *slot;
|
register ternaryfunc *slot;
|
||||||
|
|
||||||
mv = v->ob_type->tp_as_number;
|
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)) {
|
if (mv != NULL && NEW_STYLE_NUMBER(v)) {
|
||||||
/* try v.op(v,w,z) */
|
/* try v.op(v,w,z) */
|
||||||
slot = NB_TERNOP(mv, op_slot);
|
slot = NB_TERNOP(mv, op_slot);
|
||||||
|
@ -435,7 +456,6 @@ ternary_op(PyObject *v,
|
||||||
goto ternary_error;
|
goto ternary_error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mw = w->ob_type->tp_as_number;
|
|
||||||
if (mw != NULL && NEW_STYLE_NUMBER(w)) {
|
if (mw != NULL && NEW_STYLE_NUMBER(w)) {
|
||||||
/* try w.op(v,w,z) */
|
/* try w.op(v,w,z) */
|
||||||
slot = NB_TERNOP(mw,op_slot);
|
slot = NB_TERNOP(mw,op_slot);
|
||||||
|
|
Loading…
Reference in New Issue