summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPauli Virtanen <pav@iki.fi>2014-07-24 23:58:28 +0300
committerMarten van Kerkwijk <mhvk@astro.utoronto.ca>2015-04-04 21:47:01 -0400
commit367b4094125062fa9018db0f310b28780259f29b (patch)
tree33062684a21778b95ff949295aee291ee20e7322
parent4679ff2380c01f18ea8cfd2d89838ba53f6381de (diff)
downloadnumpy-367b4094125062fa9018db0f310b28780259f29b.tar.gz
BUG: core: ensure binop execution uses ufuncs as fallback
These changes only affect objects defining __numpy_ufunc__. Other objects use the __array_priority__ mechanism to decide whether NotImplemented should be returned or not. The binops previously returned NotImplemented even if other._r<op>__ is ndarray.__r<op>__, rather than trying to evaluate the result via ufuncs. This causes problems in binops of two objects that both subclass from ndarray and define __numpy_ufunc__. The solution added here makes the total logic as follows: def __binop__(self, other): if (hasattr(other, '__numpy_ufunc__') and not isinstance(other, self.__class__) and hasattr(other, '__rop__') and other.__class__.__rop__ is not self.__class__.__rop__): return NotImplemented return np.binop(self, other)
-rw-r--r--numpy/core/src/multiarray/arrayobject.c18
-rw-r--r--numpy/core/src/multiarray/number.c81
2 files changed, 59 insertions, 40 deletions
diff --git a/numpy/core/src/multiarray/arrayobject.c b/numpy/core/src/multiarray/arrayobject.c
index 6e48ef381..1a5c30832 100644
--- a/numpy/core/src/multiarray/arrayobject.c
+++ b/numpy/core/src/multiarray/arrayobject.c
@@ -1297,7 +1297,8 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op)
switch (cmp_op) {
case Py_LT:
- if (needs_right_binop_forward(obj_self, other, "__gt__", 0)) {
+ if (needs_right_binop_forward(obj_self, other, "__gt__", 0) &&
+ Py_TYPE(obj_self)->tp_richcompare != Py_TYPE(other)->tp_richcompare) {
/* See discussion in number.c */
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
@@ -1306,7 +1307,8 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op)
n_ops.less);
break;
case Py_LE:
- if (needs_right_binop_forward(obj_self, other, "__ge__", 0)) {
+ if (needs_right_binop_forward(obj_self, other, "__ge__", 0) &&
+ Py_TYPE(obj_self)->tp_richcompare != Py_TYPE(other)->tp_richcompare) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
@@ -1322,7 +1324,8 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op)
Py_INCREF(Py_False);
return Py_False;
}
- if (needs_right_binop_forward(obj_self, other, "__eq__", 0)) {
+ if (needs_right_binop_forward(obj_self, other, "__eq__", 0) &&
+ Py_TYPE(obj_self)->tp_richcompare != Py_TYPE(other)->tp_richcompare) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
@@ -1397,7 +1400,8 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op)
Py_INCREF(Py_True);
return Py_True;
}
- if (needs_right_binop_forward(obj_self, other, "__ne__", 0)) {
+ if (needs_right_binop_forward(obj_self, other, "__ne__", 0) &&
+ Py_TYPE(obj_self)->tp_richcompare != Py_TYPE(other)->tp_richcompare) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
@@ -1459,7 +1463,8 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op)
}
break;
case Py_GT:
- if (needs_right_binop_forward(obj_self, other, "__lt__", 0)) {
+ if (needs_right_binop_forward(obj_self, other, "__lt__", 0) &&
+ Py_TYPE(obj_self)->tp_richcompare != Py_TYPE(other)->tp_richcompare) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
@@ -1467,7 +1472,8 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op)
n_ops.greater);
break;
case Py_GE:
- if (needs_right_binop_forward(obj_self, other, "__le__", 0)) {
+ if (needs_right_binop_forward(obj_self, other, "__le__", 0) &&
+ Py_TYPE(obj_self)->tp_richcompare != Py_TYPE(other)->tp_richcompare) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
diff --git a/numpy/core/src/multiarray/number.c b/numpy/core/src/multiarray/number.c
index 9c6e43c69..9cbb23ed9 100644
--- a/numpy/core/src/multiarray/number.c
+++ b/numpy/core/src/multiarray/number.c
@@ -111,6 +111,13 @@ has_ufunc_attr(PyObject * obj) {
* [occurs if the other object is a strict subclass provided
* the operation is not in-place]
*
+ * An additional check is made in GIVE_UP_IF_HAS_RIGHT_BINOP macro below:
+ *
+ * (iv) other.__class__.__r*__ is not self.__class__.__r*__
+ *
+ * This is needed, because CPython does not call __rmul__ if
+ * the tp_number slots of the two objects are the same.
+ *
* This always prioritizes the __r*__ routines over __numpy_ufunc__, independent
* of whether the other object is an ndarray subclass or not.
*/
@@ -146,13 +153,19 @@ needs_right_binop_forward(PyObject *self, PyObject *other,
}
}
-#define GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, left_name, right_name, inplace) \
- do { \
- if (needs_right_binop_forward((PyObject *)m1, m2, right_name, \
- inplace)) { \
- Py_INCREF(Py_NotImplemented); \
- return Py_NotImplemented; \
- } \
+/* In pure-Python, SAME_SLOTS can be replaced by
+ getattr(m1, op_name) is getattr(m2, op_name) */
+#define SAME_SLOTS(m1, m2, slot_name) \
+ (Py_TYPE(m1)->tp_as_number != NULL && Py_TYPE(m2)->tp_as_number != NULL && \
+ Py_TYPE(m1)->tp_as_number->slot_name == Py_TYPE(m2)->tp_as_number->slot_name)
+
+#define GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, left_name, right_name, inplace, slot_name) \
+ do { \
+ if (needs_right_binop_forward((PyObject *)m1, m2, right_name, inplace) && \
+ !SAME_SLOTS(m1, m2, slot_name)) { \
+ Py_INCREF(Py_NotImplemented); \
+ return Py_NotImplemented; \
+ } \
} while (0)
@@ -339,21 +352,21 @@ PyArray_GenericInplaceUnaryFunction(PyArrayObject *m1, PyObject *op)
static PyObject *
array_add(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__add__", "__radd__", 0);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__add__", "__radd__", 0, nb_add);
return PyArray_GenericBinaryFunction(m1, m2, n_ops.add);
}
static PyObject *
array_subtract(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__sub__", "__rsub__", 0);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__sub__", "__rsub__", 0, nb_subtract);
return PyArray_GenericBinaryFunction(m1, m2, n_ops.subtract);
}
static PyObject *
array_multiply(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__mul__", "__rmul__", 0);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__mul__", "__rmul__", 0, nb_multiply);
return PyArray_GenericBinaryFunction(m1, m2, n_ops.multiply);
}
@@ -361,7 +374,7 @@ array_multiply(PyArrayObject *m1, PyObject *m2)
static PyObject *
array_divide(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__div__", "__rdiv__", 0);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__div__", "__rdiv__", 0, nb_divide);
return PyArray_GenericBinaryFunction(m1, m2, n_ops.divide);
}
#endif
@@ -369,7 +382,7 @@ array_divide(PyArrayObject *m1, PyObject *m2)
static PyObject *
array_remainder(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__mod__", "__rmod__", 0);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__mod__", "__rmod__", 0, nb_remainder);
return PyArray_GenericBinaryFunction(m1, m2, n_ops.remainder);
}
@@ -533,7 +546,7 @@ array_power(PyArrayObject *a1, PyObject *o2, PyObject *NPY_UNUSED(modulo))
{
/* modulo is ignored! */
PyObject *value;
- GIVE_UP_IF_HAS_RIGHT_BINOP(a1, o2, "__pow__", "__rpow__", 0);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(a1, o2, "__pow__", "__rpow__", 0, nb_power);
value = fast_scalar_power(a1, o2, 0);
if (!value) {
value = PyArray_GenericBinaryFunction(a1, o2, n_ops.power);
@@ -563,56 +576,56 @@ array_invert(PyArrayObject *m1)
static PyObject *
array_left_shift(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__lshift__", "__rlshift__", 0);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__lshift__", "__rlshift__", 0, nb_lshift);
return PyArray_GenericBinaryFunction(m1, m2, n_ops.left_shift);
}
static PyObject *
array_right_shift(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__rshift__", "__rrshift__", 0);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__rshift__", "__rrshift__", 0, nb_rshift);
return PyArray_GenericBinaryFunction(m1, m2, n_ops.right_shift);
}
static PyObject *
array_bitwise_and(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__and__", "__rand__", 0);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__and__", "__rand__", 0, nb_and);
return PyArray_GenericBinaryFunction(m1, m2, n_ops.bitwise_and);
}
static PyObject *
array_bitwise_or(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__or__", "__ror__", 0);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__or__", "__ror__", 0, nb_or);
return PyArray_GenericBinaryFunction(m1, m2, n_ops.bitwise_or);
}
static PyObject *
array_bitwise_xor(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__xor__", "__rxor__", 0);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__xor__", "__rxor__", 0, nb_xor);
return PyArray_GenericBinaryFunction(m1, m2, n_ops.bitwise_xor);
}
static PyObject *
array_inplace_add(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__iadd__", "__radd__", 1);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__iadd__", "__radd__", 1, nb_inplace_add);
return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.add);
}
static PyObject *
array_inplace_subtract(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__isub__", "__rsub__", 1);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__isub__", "__rsub__", 1, nb_inplace_subtract);
return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.subtract);
}
static PyObject *
array_inplace_multiply(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__imul__", "__rmul__", 1);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__imul__", "__rmul__", 1, nb_inplace_multiply);
return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.multiply);
}
@@ -620,7 +633,7 @@ array_inplace_multiply(PyArrayObject *m1, PyObject *m2)
static PyObject *
array_inplace_divide(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__idiv__", "__rdiv__", 1);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__idiv__", "__rdiv__", 1, nb_inplace_divide);
return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.divide);
}
#endif
@@ -628,7 +641,7 @@ array_inplace_divide(PyArrayObject *m1, PyObject *m2)
static PyObject *
array_inplace_remainder(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__imod__", "__rmod__", 1);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__imod__", "__rmod__", 1, nb_inplace_remainder);
return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.remainder);
}
@@ -637,7 +650,7 @@ array_inplace_power(PyArrayObject *a1, PyObject *o2, PyObject *NPY_UNUSED(modulo
{
/* modulo is ignored! */
PyObject *value;
- GIVE_UP_IF_HAS_RIGHT_BINOP(a1, o2, "__ipow__", "__rpow__", 1);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(a1, o2, "__ipow__", "__rpow__", 1, nb_inplace_power);
value = fast_scalar_power(a1, o2, 1);
if (!value) {
value = PyArray_GenericInplaceBinaryFunction(a1, o2, n_ops.power);
@@ -648,56 +661,56 @@ array_inplace_power(PyArrayObject *a1, PyObject *o2, PyObject *NPY_UNUSED(modulo
static PyObject *
array_inplace_left_shift(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__ilshift__", "__rlshift__", 1);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__ilshift__", "__rlshift__", 1, nb_inplace_lshift);
return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.left_shift);
}
static PyObject *
array_inplace_right_shift(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__irshift__", "__rrshift__", 1);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__irshift__", "__rrshift__", 1, nb_inplace_rshift);
return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.right_shift);
}
static PyObject *
array_inplace_bitwise_and(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__iand__", "__rand__", 1);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__iand__", "__rand__", 1, nb_inplace_and);
return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.bitwise_and);
}
static PyObject *
array_inplace_bitwise_or(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__ior__", "__ror__", 1);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__ior__", "__ror__", 1, nb_inplace_or);
return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.bitwise_or);
}
static PyObject *
array_inplace_bitwise_xor(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__ixor__", "__rxor__", 1);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__ixor__", "__rxor__", 1, nb_inplace_xor);
return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.bitwise_xor);
}
static PyObject *
array_floor_divide(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__floordiv__", "__rfloordiv__", 0);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__floordiv__", "__rfloordiv__", 0, nb_floor_divide);
return PyArray_GenericBinaryFunction(m1, m2, n_ops.floor_divide);
}
static PyObject *
array_true_divide(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__truediv__", "__rtruediv__", 0);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__truediv__", "__rtruediv__", 0, nb_true_divide);
return PyArray_GenericBinaryFunction(m1, m2, n_ops.true_divide);
}
static PyObject *
array_inplace_floor_divide(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__ifloordiv__", "__rfloordiv__", 1);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__ifloordiv__", "__rfloordiv__", 1, nb_inplace_floor_divide);
return PyArray_GenericInplaceBinaryFunction(m1, m2,
n_ops.floor_divide);
}
@@ -705,7 +718,7 @@ array_inplace_floor_divide(PyArrayObject *m1, PyObject *m2)
static PyObject *
array_inplace_true_divide(PyArrayObject *m1, PyObject *m2)
{
- GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__itruediv__", "__rtruediv__", 1);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(m1, m2, "__itruediv__", "__rtruediv__", 1, nb_inplace_true_divide);
return PyArray_GenericInplaceBinaryFunction(m1, m2,
n_ops.true_divide);
}
@@ -737,7 +750,7 @@ static PyObject *
array_divmod(PyArrayObject *op1, PyObject *op2)
{
PyObject *divp, *modp, *result;
- GIVE_UP_IF_HAS_RIGHT_BINOP(op1, op2, "__divmod__", "__rdivmod__", 0);
+ GIVE_UP_IF_HAS_RIGHT_BINOP(op1, op2, "__divmod__", "__rdivmod__", 0, nb_divmod);
divp = array_floor_divide(op1, op2);
if (divp == NULL) {