diff options
Diffstat (limited to 'numpy/core')
-rw-r--r-- | numpy/core/src/umath/loops.c.src | 25 | ||||
-rw-r--r-- | numpy/core/tests/test_deprecations.py | 56 | ||||
-rw-r--r-- | numpy/core/tests/test_umath.py | 11 |
3 files changed, 72 insertions, 20 deletions
diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src index c3abd02f8..d0374fc0a 100644 --- a/numpy/core/src/umath/loops.c.src +++ b/numpy/core/src/umath/loops.c.src @@ -2562,22 +2562,28 @@ NPY_NO_EXPORT void /**begin repeat * #kind = equal, not_equal, greater, greater_equal, less, less_equal# * #OP = EQ, NE, GT, GE, LT, LE# - * #identity = NPY_TRUE, NPY_FALSE, NPY_FALSE, NPY_TRUE, NPY_FALSE, NPY_TRUE# + * #identity = NPY_TRUE, NPY_FALSE, -1*4# */ NPY_NO_EXPORT void OBJECT_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { + int ret; + PyObject *ret_obj; PyObject *in1 = *(PyObject **)ip1; PyObject *in2 = *(PyObject **)ip2; + in1 = in1 ? in1 : Py_None; in2 = in2 ? in2 : Py_None; + /* - * Do not use RichCompareBool because it includes an identity check. - * But this is wrong for elementwise behaviour, since i.e. it says - * that NaN can be equal to NaN, and an array is equal to itself. + * Do not use RichCompareBool because it includes an identity check + * (for == and !=). + * This is wrong for elementwise behaviour, since it means + * that NaN can be equal to NaN and an array is equal to itself. */ - PyObject *ret_obj = PyObject_RichCompare(in1, in2, Py_@OP@); + ret_obj = PyObject_RichCompare(in1, in2, Py_@OP@); if (ret_obj == NULL) { +#if @identity@ != -1 if (in1 == in2) { PyErr_Clear(); if (DEPRECATE("numpy @kind@ will not check object identity " @@ -2588,10 +2594,12 @@ OBJECT_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUS *((npy_bool *)op1) = @identity@; continue; } +#endif return; } - int ret = PyObject_IsTrue(ret_obj); + ret = PyObject_IsTrue(ret_obj); if (ret == -1) { +#if @identity@ != -1 if (in1 == in2) { PyErr_Clear(); if (DEPRECATE("numpy @kind@ will not check object identity " @@ -2603,19 +2611,22 @@ OBJECT_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUS *((npy_bool *)op1) = @identity@; continue; } +#endif return; } +#if @identity@ != -1 if ((in1 == in2) && ((npy_bool)ret != @identity@)) { if (DEPRECATE_FUTUREWARNING( "numpy @kind@ will not check object identity " "in the future. The comparison did not return the " - "same result as suggested by the identity " + "same result as suggested by the identity (`is`)) " "and will change.") < 0) { return; } *((npy_bool *)op1) = @identity@; continue; } +#endif *((npy_bool *)op1) = (npy_bool)ret; } } diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index 58d62aa89..a2b30c3c9 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -397,7 +397,8 @@ class TestComparisonDepreactions(_DeprecationTestCase): # Element comparison error (numpy array can't be compared). a = np.array([1, np.array([1,2,3])], dtype=object) - self.assert_deprecated(op, args=(a, a), num=None) + b = np.array([1, np.array([1,2,3])], dtype=object) + self.assert_deprecated(op, args=(a, b), num=None) def test_string(self): @@ -417,7 +418,6 @@ class TestComparisonDepreactions(_DeprecationTestCase): # comparison in the future. [1, 2] == None should be [False, False]. with warnings.catch_warnings(): warnings.filterwarnings('always', '', FutureWarning) - a = np.array([1, 2]) assert_warns(FutureWarning, operator.eq, np.arange(3), None) assert_warns(FutureWarning, operator.ne, np.arange(3), None) @@ -427,5 +427,57 @@ class TestComparisonDepreactions(_DeprecationTestCase): assert_raises(FutureWarning, operator.ne, np.arange(3), None) +class TestIdentityComparisonDepreactions(_DeprecationTestCase): + """This tests the equal and not_equal object ufuncs identity check + deprecation. This was due to the usage of PyObject_RichCompareBool. + + This tests that for example for `a = np.array([np.nan], dtype=object)` + `a == a` it is warned that False and not `np.nan is np.nan` is returned. + + Should be kept in sync with TestComparisonDepreactions and new tests + added when the deprecation is over. Requires only removing of @identity@ + (and blocks) from the ufunc loops.c.src of the OBJECT comparisons. + """ + + message = "numpy .* will not check object identity in the future." + + def test_identity_equality_mismatch(self): + a = np.array([np.nan], dtype=object) + + with warnings.catch_warnings(): + warnings.filterwarnings('always', '', FutureWarning) + assert_warns(FutureWarning, np.equal, a, a) + assert_warns(FutureWarning, np.not_equal, a, a) + + with warnings.catch_warnings(): + warnings.filterwarnings('error', '', FutureWarning) + assert_raises(FutureWarning, np.equal, a, a) + assert_raises(FutureWarning, np.not_equal, a, a) + # And the other do not warn: + np.less(a, a) + np.greater(a, a) + np.less_equal(a, a) + np.greater_equal(a, a) + + + def test_comparison_error(self): + class FunkyType(object): + def __eq__(self, other): + raise TypeError("I won't compare") + def __ne__(self, other): + raise TypeError("I won't compare") + + a = np.array([FunkyType()]) + self.assert_deprecated(np.equal, args=(a, a)) + self.assert_deprecated(np.not_equal, args=(a, a)) + + + def test_bool_error(self): + # The comparison result cannot be interpreted as a bool + a = np.array([np.array([1, 2, 3]), None], dtype=object) + self.assert_deprecated(np.equal, args=(a, a)) + self.assert_deprecated(np.not_equal, args=(a, a)) + + if __name__ == "__main__": run_module_suite() diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index b7b2f6308..3646fd2a9 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -1534,16 +1534,5 @@ def test_complex_nan_comparisons(): assert_equal(x == y, False, err_msg="%r == %r" % (x, y)) -def test_object_array_comparison(): - obj_array = np.arange(3) - a = np.array([obj_array, 1]) - # Should raise an error because (obj_array == obj_array) is not a bool. - # At this time, a == a would return False because of the error. - assert_raises(ValueError, np.equal, a, a) - - # Check the same for NaN, when it is the *same* NaN. - a = np.array([np.nan]) - assert_equal(a == a, [False]) - if __name__ == "__main__": run_module_suite() |