diff --git a/numpy/core/_internal.py b/numpy/core/_internal.py index 3b0c464674b6..56b49afa8856 100644 --- a/numpy/core/_internal.py +++ b/numpy/core/_internal.py @@ -10,7 +10,7 @@ import platform import warnings -from .multiarray import dtype, array, ndarray +from .multiarray import dtype, array, ndarray, promote_types try: import ctypes except ImportError: @@ -433,6 +433,37 @@ def _copy_fields(ary): 'formats': [dt.fields[name][0] for name in dt.names]} return array(ary, dtype=copy_dtype, copy=True) +def _promote_fields(dt1, dt2): + """ Perform type promotion for two structured dtypes. + + Parameters + ---------- + dt1 : structured dtype + First dtype. + dt2 : structured dtype + Second dtype. + + Returns + ------- + out : dtype + The promoted dtype + + Notes + ----- + This function ignores the "titles" + """ + if (dt1.names is None or dt2.names is None) or dt1.names != dt2.names: + raise TypeError("invalid type promotion") + + # If the lengths all match, assume the titles (if existing) match the + # names. + if not len(dt1.names) == len(dt1.fields) == len(dt2.fields): + raise TypeError( + "NumPy currently does not support promotion with field titles.") + + return dtype([(name, promote_types(dt1[name], dt2[name])) + for name in dt1.names]) + def _getfield_is_safe(oldtype, newtype, offset): """ Checks safety of getfield for object arrays. diff --git a/numpy/core/src/multiarray/arrayobject.c b/numpy/core/src/multiarray/arrayobject.c index 78f547de2002..918aef418db5 100644 --- a/numpy/core/src/multiarray/arrayobject.c +++ b/numpy/core/src/multiarray/arrayobject.c @@ -1046,31 +1046,71 @@ _void_compare(PyArrayObject *self, PyArrayObject *other, int cmp_op) "Void-arrays can only be compared for equality."); return NULL; } - if (PyArray_HASFIELDS(self)) { - PyObject *res = NULL, *temp, *a, *b; - PyObject *key, *value, *temp2; - PyObject *op; - Py_ssize_t pos = 0; + if (PyArray_TYPE(other) != NPY_VOID) { + PyErr_SetString(PyExc_ValueError, + "Cannot compare structured or void to non-void arrays. " + "(This may return array of False in the future.)"); + return NULL; + } + + if (PyArray_HASFIELDS(self) && PyArray_HASFIELDS(other)) { + PyArray_Descr *self_descr = PyArray_DESCR(self); + PyArray_Descr *other_descr = PyArray_DESCR(other); + npy_intp result_ndim = PyArray_NDIM(self) > PyArray_NDIM(other) ? PyArray_NDIM(self) : PyArray_NDIM(other); - op = (cmp_op == Py_EQ ? n_ops.logical_and : n_ops.logical_or); - while (PyDict_Next(PyArray_DESCR(self)->fields, &pos, &key, &value)) { - if (NPY_TITLE_KEY(key, value)) { - continue; - } - a = array_subscript_asarray(self, key); + int field_count = PyTuple_GET_SIZE(self_descr->names); + if (field_count != PyTuple_GET_SIZE(other_descr->names)) { + PyErr_SetString(PyExc_TypeError, + "Cannot compare structured dtypes with different number of " + "fields. (This may return array of False in the future.)"); + return NULL; + } + + PyObject *op = (cmp_op == Py_EQ ? n_ops.logical_and : n_ops.logical_or); + PyObject *res = NULL; + for (int i = 0; i < field_count; ++i) { + PyObject *fieldname, *temp, *temp2; + + fieldname = PyTuple_GET_ITEM(self_descr->names, i); + PyArrayObject *a = (PyArrayObject *)array_subscript_asarray( + self, fieldname); if (a == NULL) { Py_XDECREF(res); return NULL; } - b = array_subscript_asarray(other, key); + fieldname = PyTuple_GET_ITEM(other_descr->names, i); + PyArrayObject *b = (PyArrayObject *)array_subscript_asarray( + other, fieldname); if (b == NULL) { Py_XDECREF(res); Py_DECREF(a); return NULL; } - temp = array_richcompare((PyArrayObject *)a,b,cmp_op); + /* + * If the fields were subarrays, the dimensions may have changed. + * In that case, the new shape (subarray part) must match exactly. + * (If this is 0, there is no subarray.) + */ + int field_dims_a = PyArray_NDIM(a) - PyArray_NDIM(self); + int field_dims_b = PyArray_NDIM(b) - PyArray_NDIM(other); + if (field_dims_a != field_dims_b || ( + field_dims_a != 0 && /* neither is subarray */ + /* Compare only the added (subarray) dimensions: */ + !PyArray_CompareLists( + PyArray_DIMS(a) + PyArray_NDIM(self), + PyArray_DIMS(b) + PyArray_NDIM(other), + field_dims_a))) { + PyErr_SetString(PyExc_TypeError, + "Cannot compare subarrays with different shapes."); + Py_DECREF(a); + Py_DECREF(b); + Py_XDECREF(res); + return NULL; + } + + temp = array_richcompare(a, (PyObject *)b,cmp_op); Py_DECREF(a); Py_DECREF(b); if (temp == NULL) { @@ -1155,7 +1195,23 @@ _void_compare(PyArrayObject *self, PyArrayObject *other, int cmp_op) } return res; } + else if (PyArray_HASFIELDS(self) || PyArray_HASFIELDS(other)) { + PyErr_SetString(PyExc_TypeError, + "Cannot compare structured with unstructured void. " + "(This may return array of False in the future.)"); + return NULL; + } else { + /* Cannot have subarray, since subarray is absorbed into array. */ + assert(PyArray_DESCR(self)->subarray == NULL); + assert(PyArray_DESCR(other)->subarray == NULL); + if (PyArray_ITEMSIZE(self) != PyArray_ITEMSIZE(other)) { + PyErr_SetString(PyExc_TypeError, + "cannot compare unstructured voids of different length. " + "Use bytes to compare. " + "(This may return array of False in the future.)"); + return NULL; + } /* compare as a string. Assumes self and other have same descr->type */ return _strings_richcompare(self, other, cmp_op, 0); } @@ -1358,24 +1414,7 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op) return Py_NotImplemented; } - _res = PyArray_CanCastTypeTo(PyArray_DESCR(self), - PyArray_DESCR(array_other), - NPY_EQUIV_CASTING); - if (_res == 0) { - /* 2015-05-07, 1.10 */ - Py_DECREF(array_other); - if (DEPRECATE_FUTUREWARNING( - "elementwise == comparison failed and returning scalar " - "instead; this will raise an error or perform " - "elementwise comparison in the future.") < 0) { - return NULL; - } - Py_INCREF(Py_False); - return Py_False; - } - else { - result = _void_compare(self, array_other, cmp_op); - } + result = _void_compare(self, array_other, cmp_op); Py_DECREF(array_other); return result; } diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c index 1a962ef788d7..59460c713e2f 100644 --- a/numpy/core/src/multiarray/convert_datatype.c +++ b/numpy/core/src/multiarray/convert_datatype.c @@ -3162,10 +3162,25 @@ can_cast_fields_safety(PyArray_Descr *from, PyArray_Descr *to) { NPY_CASTING casting = NPY_NO_CASTING | _NPY_CAST_IS_VIEW; + /* + * If the itemsize (includes padding at the end) does not match, consider + * this a SAFE cast only. A view may be OK if we shrink. + */ + if (from->elsize > to->elsize) { + /* + * The itemsize may mismatch even if all fields and formats match + * (due to additional padding). + */ + casting = PyArray_MinCastSafety( + casting, NPY_SAFE_CASTING | _NPY_CAST_IS_VIEW); + } + else if (from->elsize < to->elsize) { + casting = PyArray_MinCastSafety(casting, NPY_SAFE_CASTING); + } + Py_ssize_t field_count = PyTuple_Size(from->names); if (field_count != PyTuple_Size(to->names)) { - /* TODO: This should be rejected! */ - return NPY_UNSAFE_CASTING; + return -1; } for (Py_ssize_t i = 0; i < field_count; i++) { PyObject *from_key = PyTuple_GET_ITEM(from->names, i); @@ -3175,56 +3190,69 @@ can_cast_fields_safety(PyArray_Descr *from, PyArray_Descr *to) } PyArray_Descr *from_base = (PyArray_Descr*)PyTuple_GET_ITEM(from_tup, 0); - /* - * TODO: This should use to_key (order), compare gh-15509 by - * by Allan Haldane. And raise an error on failure. - * (Fixing that may also requires fixing/changing promotion.) - */ - PyObject *to_tup = PyDict_GetItem(to->fields, from_key); + PyObject *to_key = PyTuple_GET_ITEM(to->names, i); + PyObject *to_tup = PyDict_GetItem(to->fields, to_key); if (to_tup == NULL) { - return NPY_UNSAFE_CASTING; + return give_bad_field_error(from_key); } PyArray_Descr *to_base = (PyArray_Descr*)PyTuple_GET_ITEM(to_tup, 0); + int cmp = PyUnicode_Compare(from_key, to_key); + if (error_converting(cmp)) { + return -1; + } + if (cmp != 0) { + /* + * Field name mismatch, consider this at most SAFE. + */ + casting = PyArray_MinCastSafety( + casting, NPY_SAFE_CASTING | _NPY_CAST_IS_VIEW); + } NPY_CASTING field_casting = PyArray_GetCastSafety(from_base, to_base, NULL); if (field_casting < 0) { return -1; } casting = PyArray_MinCastSafety(casting, field_casting); + if (casting & _NPY_CAST_IS_VIEW) { + /* + * Unset cast-is-view (and use at most equivlanet casting) if the + * field offsets do not match. (not no-casting) + */ + PyObject *from_offset = PyTuple_GET_ITEM(from_tup, 1); + PyObject *to_offset = PyTuple_GET_ITEM(to_tup, 1); + cmp = PyObject_RichCompareBool(from_offset, to_offset, Py_EQ); + if (error_converting(cmp)) { + assert(0); /* Both are longs, this should never fail */ + return -1; + } + if (!cmp) { + casting = PyArray_MinCastSafety(casting, NPY_EQUIV_CASTING); + } + } + + /* Also check the title (denote mismatch as SAFE only) */ + PyObject *from_title = from_key; + PyObject *to_title = to_key; + if (PyTuple_GET_SIZE(from_tup) > 2) { + from_title = PyTuple_GET_ITEM(from_tup, 2); + } + if (PyTuple_GET_SIZE(to_tup) > 2) { + to_title = PyTuple_GET_ITEM(to_tup, 2); + } + cmp = PyObject_RichCompareBool(from_title, to_title, Py_EQ); + if (error_converting(cmp)) { + return -1; + } + if (!cmp) { + casting = PyArray_MinCastSafety( + casting, NPY_SAFE_CASTING | _NPY_CAST_IS_VIEW); + } + } if (!(casting & _NPY_CAST_IS_VIEW)) { assert((casting & ~_NPY_CAST_IS_VIEW) != NPY_NO_CASTING); - return casting; - } - - /* - * If the itemsize (includes padding at the end), fields, or names - * do not match, this cannot be a view and also not a "no" cast - * (identical dtypes). - * It may be possible that this can be relaxed in some cases. - */ - if (from->elsize != to->elsize) { - /* - * The itemsize may mismatch even if all fields and formats match - * (due to additional padding). - */ - return PyArray_MinCastSafety(casting, NPY_EQUIV_CASTING); } - int cmp = PyObject_RichCompareBool(from->fields, to->fields, Py_EQ); - if (cmp != 1) { - if (cmp == -1) { - PyErr_Clear(); - } - return PyArray_MinCastSafety(casting, NPY_EQUIV_CASTING); - } - cmp = PyObject_RichCompareBool(from->names, to->names, Py_EQ); - if (cmp != 1) { - if (cmp == -1) { - PyErr_Clear(); - } - return PyArray_MinCastSafety(casting, NPY_EQUIV_CASTING); - } return casting; } diff --git a/numpy/core/src/multiarray/dtypemeta.c b/numpy/core/src/multiarray/dtypemeta.c index 4ee721964a91..f70e31a6f0d3 100644 --- a/numpy/core/src/multiarray/dtypemeta.c +++ b/numpy/core/src/multiarray/dtypemeta.c @@ -267,26 +267,70 @@ string_unicode_common_instance(PyArray_Descr *descr1, PyArray_Descr *descr2) static PyArray_Descr * void_common_instance(PyArray_Descr *descr1, PyArray_Descr *descr2) { - /* - * We currently do not support promotion of void types unless they - * are equivalent. - */ - if (!PyArray_CanCastTypeTo(descr1, descr2, NPY_EQUIV_CASTING)) { - if (descr1->subarray == NULL && descr1->names == NULL && - descr2->subarray == NULL && descr2->names == NULL) { + if (descr1->subarray == NULL && descr1->names == NULL && + descr2->subarray == NULL && descr2->names == NULL) { + if (descr1->elsize != descr2->elsize) { PyErr_SetString(PyExc_TypeError, "Invalid type promotion with void datatypes of different " "lengths. Use the `np.bytes_` datatype instead to pad the " "shorter value with trailing zero bytes."); + return NULL; } - else { + Py_INCREF(descr1); + return descr1; + } + + if (descr1->names != NULL && descr2->names != NULL) { + /* If both have fields promoting individual fields may be possible */ + static PyObject *promote_fields_func = NULL; + npy_cache_import("numpy.core._internal", "_promote_fields", + &promote_fields_func); + if (promote_fields_func == NULL) { + return NULL; + } + PyObject *result = PyObject_CallFunctionObjArgs(promote_fields_func, + descr1, descr2, NULL); + if (result == NULL) { + return NULL; + } + if (!PyObject_TypeCheck(result, Py_TYPE(descr1))) { + PyErr_SetString(PyExc_RuntimeError, + "Internal NumPy error: `_promote_fields` did not return " + "a valid descriptor object."); + Py_DECREF(result); + return NULL; + } + return (PyArray_Descr *)result; + } + else if (descr1->subarray != NULL && descr2->subarray != NULL) { + int cmp = PyObject_RichCompareBool( + descr1->subarray->shape, descr2->subarray->shape, Py_EQ); + if (error_converting(cmp)) { + return NULL; + } + if (!cmp) { PyErr_SetString(PyExc_TypeError, - "invalid type promotion with structured datatype(s)."); + "invalid type promotion with subarray datatypes " + "(shape mismatch)."); } - return NULL; + PyArray_Descr *new_base = PyArray_PromoteTypes( + descr1->subarray->base, descr2->subarray->base); + if (new_base == NULL) { + return NULL; + } + + PyArray_Descr *new_descr = PyArray_DescrNew(descr1); + if (new_descr == NULL) { + Py_DECREF(new_base); + return NULL; + } + Py_SETREF(new_descr->subarray->base, new_base); + return new_descr; } - Py_INCREF(descr1); - return descr1; + + PyErr_SetString(PyExc_TypeError, + "invalid type promotion with structured datatype(s)."); + return NULL; } static int diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index 42e632e4aa2a..396ff02a293b 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -200,14 +200,6 @@ def __ne__(self, other): self.assert_deprecated(lambda: np.arange(2) == NotArray()) self.assert_deprecated(lambda: np.arange(2) != NotArray()) - struct1 = np.zeros(2, dtype="i4,i4") - struct2 = np.zeros(2, dtype="i4,i4,i4") - - assert_warns(FutureWarning, lambda: struct1 == 1) - assert_warns(FutureWarning, lambda: struct1 == struct2) - assert_warns(FutureWarning, lambda: struct1 != 1) - assert_warns(FutureWarning, lambda: struct1 != struct2) - def test_array_richcompare_legacy_weirdness(self): # It doesn't really work to use assert_deprecated here, b/c part of # the point of assert_deprecated is to check that when warnings are diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py index 8a6b7dcd5f95..a809a2d495cd 100644 --- a/numpy/core/tests/test_dtype.py +++ b/numpy/core/tests/test_dtype.py @@ -151,11 +151,11 @@ def test_field_order_equality(self): 'formats': ['i4', 'f4'], 'offsets': [0, 4]}) y = np.dtype({'names': ['B', 'A'], - 'formats': ['f4', 'i4'], + 'formats': ['i4', 'f4'], 'offsets': [4, 0]}) assert_equal(x == y, False) - # But it is currently an equivalent cast: - assert np.can_cast(x, y, casting="equiv") + # This is an safe cast (not equiv) due to the different names: + assert np.can_cast(x, y, casting="safe") class TestRecord: diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index 25dd76256663..540503c6e6a1 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -30,6 +30,7 @@ ) from numpy.testing._private.utils import _no_tracing from numpy.core.tests._locales import CommaDecimalPointLocale +from numpy.lib.recfunctions import repack_fields # Need to test an object that does not fully implement math interface from datetime import timedelta, datetime @@ -1233,21 +1234,15 @@ def test_subarray_comparison(self): # Check that incompatible sub-array shapes don't result to broadcasting x = np.zeros((1,), dtype=[('a', ('f4', (1, 2))), ('b', 'i1')]) y = np.zeros((1,), dtype=[('a', ('f4', (2,))), ('b', 'i1')]) - # This comparison invokes deprecated behaviour, and will probably - # start raising an error eventually. What we really care about in this - # test is just that it doesn't return True. - with suppress_warnings() as sup: - sup.filter(FutureWarning, "elementwise == comparison failed") - assert_equal(x == y, False) + # The main importance is that it does not return True: + with pytest.raises(TypeError): + x == y x = np.zeros((1,), dtype=[('a', ('f4', (2, 1))), ('b', 'i1')]) y = np.zeros((1,), dtype=[('a', ('f4', (2,))), ('b', 'i1')]) - # This comparison invokes deprecated behaviour, and will probably - # start raising an error eventually. What we really care about in this - # test is just that it doesn't return True. - with suppress_warnings() as sup: - sup.filter(FutureWarning, "elementwise == comparison failed") - assert_equal(x == y, False) + # The main importance is that it does not return True: + with pytest.raises(TypeError): + x == y # Check that structured arrays that are different only in # byte-order work @@ -1428,7 +1423,7 @@ def testassign(arr, v): assert_equal(testassign(arr, v1), ans) assert_equal(testassign(arr, v2), ans) assert_equal(testassign(arr, v3), ans) - assert_raises(ValueError, lambda: testassign(arr, v4)) + assert_raises(TypeError, lambda: testassign(arr, v4)) assert_equal(testassign(arr, v5), ans) w[:] = 4 assert_equal(arr, np.array([(1,4),(1,4)], dtype=dt)) @@ -1463,6 +1458,53 @@ def test_multiindex_titles(self): assert_raises(ValueError, lambda : a[['b','b']]) # field exists, but repeated a[['b','c']] # no exception + def test_structured_cast_promotion_fieldorder(self): + # gh-15494 + # dtypes with different field names are not promotable + A = ("a", "i8") + ab = np.array([(1, 2)], dtype=[A, B]) + ba = np.array([(1, 2)], dtype=[B, A]) + assert_raises(TypeError, np.concatenate, ab, ba) + assert_raises(TypeError, np.result_type, ab.dtype, ba.dtype) + assert_raises(TypeError, np.promote_types, ab.dtype, ba.dtype) + + # dtypes with same field names/order but different memory offsets + # and byte-order are promotable to packed nbo. + assert_equal(np.promote_types(ab.dtype, ba[['a', 'b']].dtype), + repack_fields(ab.dtype.newbyteorder('N'))) + + # gh-13667 + # dtypes with different fieldnames but castable field types are castable + assert_equal(np.can_cast(ab.dtype, ba.dtype), True) + assert_equal(ab.astype(ba.dtype).dtype, ba.dtype) + assert_equal(np.can_cast('f8,i8', [('f0', 'f8'), ('f1', 'i8')]), True) + assert_equal(np.can_cast('f8,i8', [('f1', 'f8'), ('f0', 'i8')]), True) + assert_equal(np.can_cast('f8,i8', [('f1', 'i8'), ('f0', 'f8')]), False) + assert_equal(np.can_cast('f8,i8', [('f1', 'i8'), ('f0', 'f8')], + casting='unsafe'), True) + + ab[:] = ba # make sure assignment still works + + # tests of type-promotion of corresponding fields + dt1 = np.dtype([("", "i4")]) + dt2 = np.dtype([("", "i8")]) + assert_equal(np.promote_types(dt1, dt2), np.dtype([('f0', 'i8')])) + assert_equal(np.promote_types(dt2, dt1), np.dtype([('f0', 'i8')])) + assert_raises(TypeError, np.promote_types, dt1, np.dtype([("", "V3")])) + assert_equal(np.promote_types('i4,f8', 'i8,f4'), + np.dtype([('f0', 'i8'), ('f1', 'f8')])) + # test nested case + dt1nest = np.dtype([("", dt1)]) + dt2nest = np.dtype([("", dt2)]) + assert_equal(np.promote_types(dt1nest, dt2nest), + np.dtype([('f0', np.dtype([('f0', 'i8')]))])) + + # note that offsets are lost when promoting: + dt = np.dtype({'names': ['x'], 'formats': ['i4'], 'offsets': [8]}) + a = np.ones(3, dtype=dt) + assert_equal(np.concatenate([a, a]).dtype, np.dtype([('x', 'i4')])) + def test_structured_asarray_is_view(self): # A scalar viewing an array preserves its view even when creating a # new array. This test documents behaviour, it may not be the best diff --git a/numpy/core/tests/test_nditer.py b/numpy/core/tests/test_nditer.py index b44343c5755c..d8f6ab3d16c8 100644 --- a/numpy/core/tests/test_nditer.py +++ b/numpy/core/tests/test_nditer.py @@ -1967,13 +1967,13 @@ def test_iter_buffered_cast_structured_type_failure_with_cleanup(): a = np.array([(1, 2, 3), (4, 5, 6)], dtype=sdt1) for intent in ["readwrite", "readonly", "writeonly"]: - # If the following assert fails, the place where the error is raised - # within nditer may change. That is fine, but it may make sense for - # a new (hard to design) test to replace it. The `simple_arr` is - # designed to require a multi-step cast (due to having fields). - assert np.can_cast(a.dtype, sdt2, casting="unsafe") + # This test was initially designed to test an error at a different + # place, but will now raise earlier to to the cast not being possible: + # `assert np.can_cast(a.dtype, sdt2, casting="unsafe")` fails. + # Without a faulty DType, there is probably probably no reliable + # way to get the initial tested behaviour. simple_arr = np.array([1, 2], dtype="i,i") # requires clean up - with pytest.raises(ValueError): + with pytest.raises(TypeError): nditer((simple_arr, a), ['buffered', 'refs_ok'], [intent, intent], casting='unsafe', op_dtypes=["f,f", sdt2]) diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index f5113150e8f7..7b9995754714 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -929,7 +929,6 @@ def test_promote_types_strings(self, swap, string_dtype): @pytest.mark.parametrize(["dtype1", "dtype2"], [[np.dtype("V6"), np.dtype("V10")], [np.dtype([("name1", "i8")]), np.dtype([("name2", "i8")])], - [np.dtype("i8,i8"), np.dtype("i4,i4")], ]) def test_invalid_void_promotion(self, dtype1, dtype2): # Mainly test structured void promotion, which currently allows @@ -941,9 +940,10 @@ def test_invalid_void_promotion(self, dtype1, dtype2): [[np.dtype("V10"), np.dtype("V10")], [np.dtype([("name1", "i8")])], [np.dtype("i8,i8"), np.dtype("i8,>i8")], + [np.dtype("i8,i8"), np.dtype("i4,i4")], ]) def test_valid_void_promotion(self, dtype1, dtype2): - assert np.promote_types(dtype1, dtype2) is dtype1 + assert np.promote_types(dtype1, dtype2) == dtype1 @pytest.mark.parametrize("dtype", list(np.typecodes["All"]) + @@ -1028,25 +1028,6 @@ def test_promote_types_metadata(self, dtype1, dtype2): assert res_bs == res assert res_bs.metadata == res.metadata - @pytest.mark.parametrize(["dtype1", "dtype2"], - [[np.dtype("V6"), np.dtype("V10")], - [np.dtype([("name1", "i8")]), np.dtype([("name2", "i8")])], - [np.dtype("i8,i8"), np.dtype("i4,i4")], - ]) - def test_invalid_void_promotion(self, dtype1, dtype2): - # Mainly test structured void promotion, which currently allows - # byte-swapping, but nothing else: - with pytest.raises(TypeError): - np.promote_types(dtype1, dtype2) - - @pytest.mark.parametrize(["dtype1", "dtype2"], - [[np.dtype("V10"), np.dtype("V10")], - [np.dtype([("name1", "i8")])], - [np.dtype("i8,i8"), np.dtype("i8,>i8")], - ]) - def test_valid_void_promotion(self, dtype1, dtype2): - assert np.promote_types(dtype1, dtype2) is dtype1 - def test_can_cast(self): assert_(np.can_cast(np.int32, np.int64)) assert_(np.can_cast(np.float64, complex)) diff --git a/numpy/testing/tests/test_utils.py b/numpy/testing/tests/test_utils.py index 31d2cdc76b3e..60fe82e88222 100644 --- a/numpy/testing/tests/test_utils.py +++ b/numpy/testing/tests/test_utils.py @@ -151,14 +151,13 @@ def test_recarrays(self): self._test_equal(a, b) - c = np.empty(2, [('floupipi', float), ('floupa', float)]) + c = np.empty(2, [('floupipi', float), + ('floupi', float), ('floupa', float)]) c['floupipi'] = a['floupi'].copy() c['floupa'] = a['floupa'].copy() - with suppress_warnings() as sup: - l = sup.record(FutureWarning, message="elementwise == ") + with pytest.raises(TypeError): self._test_not_equal(c, b) - assert_equal(len(l), 1) def test_masked_nan_inf(self): # Regression test for gh-11121