From a831463ba80c17c12aebc6ed78b86fb71eef9289 Mon Sep 17 00:00:00 2001 From: Ganesh Kathiresan Date: Sat, 12 Jun 2021 19:38:50 +0530 Subject: [PATCH 1/5] BUG: Remove TypeError on invalid dtypes comparison --- numpy/core/src/multiarray/descriptor.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/numpy/core/src/multiarray/descriptor.c b/numpy/core/src/multiarray/descriptor.c index f0dfac55dee8..3f06acd5822e 100644 --- a/numpy/core/src/multiarray/descriptor.c +++ b/numpy/core/src/multiarray/descriptor.c @@ -3228,7 +3228,22 @@ arraydescr_richcompare(PyArray_Descr *self, PyObject *other, int cmp_op) { PyArray_Descr *new = _convert_from_any(other, 0); if (new == NULL) { - return NULL; + /* Cannot convert `other` to dtype */ + + PyErr_Clear(); + + switch (cmp_op) { + case Py_LT: + case Py_LE: + case Py_EQ: + case Py_GT: + case Py_GE: + Py_RETURN_FALSE; + case Py_NE: + Py_RETURN_TRUE; + default: + Py_RETURN_NOTIMPLEMENTED; + } } npy_bool ret; From 73245321ce34b1cd36913a29b950ea2f4c44ded6 Mon Sep 17 00:00:00 2001 From: Ganesh Kathiresan Date: Sat, 12 Jun 2021 19:38:59 +0530 Subject: [PATCH 2/5] TST: Check invalid dtypes comparison --- numpy/core/tests/test_dtype.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py index 8a6b7dcd5f95..3a43bba6885c 100644 --- a/numpy/core/tests/test_dtype.py +++ b/numpy/core/tests/test_dtype.py @@ -88,6 +88,19 @@ def test_invalid_types(self): assert_raises(TypeError, np.dtype, 'q8') assert_raises(TypeError, np.dtype, 'Q8') + @pytest.mark.parametrize( + ['operator', 'expected'], + [('==', False), ('!=', True), + ('>', False), ('<', False), + ('>=', False), ('<=', False)]) + def test_richcompare_invalid_dtype(self, operator, expected): + # Make sure objects that cannot be converted to valid + # dtypes results in False when compared to valid dtypes. + # Here 7 cannot be converted to dtype. No exceptions should be raised + + assert eval(f"np.dtype(np.int32) {operator} 7") == expected,\ + f"dtype richcompare failed for {operator}" + @pytest.mark.parametrize("dtype", ['Bool', 'Complex32', 'Complex64', 'Float16', 'Float32', 'Float64', 'Int8', 'Int16', 'Int32', 'Int64', 'Object0', 'Timedelta64', From 5f8680970907fb507aeaf2edc74b2aacaf0070a9 Mon Sep 17 00:00:00 2001 From: Ganesh Kathiresan Date: Tue, 15 Jun 2021 20:27:41 +0530 Subject: [PATCH 3/5] BUG: Return NotImplemented for unrecognized dtypes --- numpy/core/src/multiarray/descriptor.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/numpy/core/src/multiarray/descriptor.c b/numpy/core/src/multiarray/descriptor.c index 3f06acd5822e..b8b477e5d70f 100644 --- a/numpy/core/src/multiarray/descriptor.c +++ b/numpy/core/src/multiarray/descriptor.c @@ -3229,21 +3229,8 @@ arraydescr_richcompare(PyArray_Descr *self, PyObject *other, int cmp_op) PyArray_Descr *new = _convert_from_any(other, 0); if (new == NULL) { /* Cannot convert `other` to dtype */ - PyErr_Clear(); - - switch (cmp_op) { - case Py_LT: - case Py_LE: - case Py_EQ: - case Py_GT: - case Py_GE: - Py_RETURN_FALSE; - case Py_NE: - Py_RETURN_TRUE; - default: - Py_RETURN_NOTIMPLEMENTED; - } + Py_RETURN_NOTIMPLEMENTED; } npy_bool ret; From 98d88c6f60fd04ab4d8587f0e13e529bc96a41a5 Mon Sep 17 00:00:00 2001 From: Ganesh Kathiresan Date: Tue, 15 Jun 2021 20:28:51 +0530 Subject: [PATCH 4/5] TST: Check invalid dtypes for equality | Check TypeError for comparisions --- numpy/core/tests/test_dtype.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py index 3a43bba6885c..3d15009ea765 100644 --- a/numpy/core/tests/test_dtype.py +++ b/numpy/core/tests/test_dtype.py @@ -88,18 +88,23 @@ def test_invalid_types(self): assert_raises(TypeError, np.dtype, 'q8') assert_raises(TypeError, np.dtype, 'Q8') - @pytest.mark.parametrize( - ['operator', 'expected'], - [('==', False), ('!=', True), - ('>', False), ('<', False), - ('>=', False), ('<=', False)]) - def test_richcompare_invalid_dtype(self, operator, expected): + def test_richcompare_invalid_dtype_equality(self): # Make sure objects that cannot be converted to valid - # dtypes results in False when compared to valid dtypes. + # dtypes results in False/True when compared to valid dtypes. # Here 7 cannot be converted to dtype. No exceptions should be raised - assert eval(f"np.dtype(np.int32) {operator} 7") == expected,\ - f"dtype richcompare failed for {operator}" + assert not np.dtype(np.int32) == 7, "dtype richcompare failed for ==" + assert np.dtype(np.int32) != 7, "dtype richcompare failed for !=" + + @pytest.mark.parametrize( + 'operation', + [operator.le, operator.lt, operator.ge, operator.gt]) + def test_richcompare_invalid_dtype_comparison(self, operation): + # Make sure TypeError is raised for comparison operators + # for invalid dtypes. Here 7 is an invalid dtype. + + with pytest.raises(TypeError): + operation(np.dtype(np.int32), 7) @pytest.mark.parametrize("dtype", ['Bool', 'Complex32', 'Complex64', 'Float16', 'Float32', 'Float64', From d80e4738f781a1d206bbc04a2e863299e5f2e104 Mon Sep 17 00:00:00 2001 From: Ganesh Kathiresan Date: Tue, 15 Jun 2021 20:30:33 +0530 Subject: [PATCH 5/5] BUG: Removed typing for == and != in dtypes --- numpy/__init__.pyi | 2 -- 1 file changed, 2 deletions(-) diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index a3722cd1ccb0..4ec46aea01a4 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -1075,8 +1075,6 @@ class dtype(Generic[_DTypeScalar_co]): # literals as of mypy 0.800. Set the return-type to `Any` for now. def __rmul__(self, value: int) -> Any: ... - def __eq__(self, other: DTypeLike) -> bool: ... - def __ne__(self, other: DTypeLike) -> bool: ... def __gt__(self, other: DTypeLike) -> bool: ... def __ge__(self, other: DTypeLike) -> bool: ... def __lt__(self, other: DTypeLike) -> bool: ...