summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Berg <sebastian@sipsolutions.net>2021-01-06 11:33:05 -0600
committerGitHub <noreply@github.com>2021-01-06 19:33:05 +0200
commit5363cfa7d07641e84fbb5f21a217018c9b55b2fa (patch)
tree80ed7dfce646e4855220fb6930f0d68ebb24bc54
parent10eeabee267e17c042bd1ff991823b16c85ed563 (diff)
downloadnumpy-5363cfa7d07641e84fbb5f21a217018c9b55b2fa.tar.gz
DEP: Deprecate `data_type.dtype` if attribute is not already a dtype (#13578)
* DEP: Deprecate `data_type.dtype` if attribute is not already a dtype After the deprecation, a recursive lookup for `.dtype` will not be possible, since `.dtype` has to be a dtype instance.
-rw-r--r--changelog/13578.deprecation.rst7
-rw-r--r--numpy/core/src/multiarray/descriptor.c14
-rw-r--r--numpy/core/tests/test_deprecations.py17
-rw-r--r--numpy/core/tests/test_dtype.py12
-rw-r--r--numpy/f2py/tests/test_array_from_pyobj.py84
-rw-r--r--numpy/typing/tests/data/pass/dtype.py2
6 files changed, 87 insertions, 49 deletions
diff --git a/changelog/13578.deprecation.rst b/changelog/13578.deprecation.rst
new file mode 100644
index 000000000..58ec7e589
--- /dev/null
+++ b/changelog/13578.deprecation.rst
@@ -0,0 +1,7 @@
+The ``.dtype`` attribute must return a ``dtype``
+------------------------------------------------
+
+A ``DeprecationWarning`` is now given if the ``.dtype`` attribute
+of an object passed into ``np.dtype`` or as a ``dtype=obj`` argument
+is not a dtype. NumPy will stop attempting to recursively coerce the
+result of ``.dtype``.
diff --git a/numpy/core/src/multiarray/descriptor.c b/numpy/core/src/multiarray/descriptor.c
index a8d575248..f0dfac55d 100644
--- a/numpy/core/src/multiarray/descriptor.c
+++ b/numpy/core/src/multiarray/descriptor.c
@@ -108,6 +108,11 @@ _try_convert_from_dtype_attr(PyObject *obj)
goto fail;
}
+ if (PyArray_DescrCheck(dtypedescr)) {
+ /* The dtype attribute is already a valid descriptor */
+ return (PyArray_Descr *)dtypedescr;
+ }
+
if (Py_EnterRecursiveCall(
" while trying to convert the given data type from its "
"`.dtype` attribute.") != 0) {
@@ -122,6 +127,15 @@ _try_convert_from_dtype_attr(PyObject *obj)
goto fail;
}
+ /* Deprecated 2021-01-05, NumPy 1.21 */
+ if (DEPRECATE("in the future the `.dtype` attribute of a given data"
+ "type object must be a valid dtype instance. "
+ "`data_type.dtype` may need to be coerced using "
+ "`np.dtype(data_type.dtype)`. (Deprecated NumPy 1.20)") < 0) {
+ Py_DECREF(newdescr);
+ return NULL;
+ }
+
return newdescr;
fail:
diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py
index 7760281ff..5498e1cf9 100644
--- a/numpy/core/tests/test_deprecations.py
+++ b/numpy/core/tests/test_deprecations.py
@@ -329,6 +329,23 @@ class TestNumericStyleTypecodes(_DeprecationTestCase):
args=(dt,))
+class TestDTypeAttributeIsDTypeDeprecation(_DeprecationTestCase):
+ # Deprecated 2021-01-05, NumPy 1.21
+ message = r".*`.dtype` attribute"
+
+ def test_deprecation_dtype_attribute_is_dtype(self):
+ class dt:
+ dtype = "f8"
+
+ class vdt(np.void):
+ dtype = "f,f"
+
+ self.assert_deprecated(lambda: np.dtype(dt))
+ self.assert_deprecated(lambda: np.dtype(dt()))
+ self.assert_deprecated(lambda: np.dtype(vdt))
+ self.assert_deprecated(lambda: np.dtype(vdt(1)))
+
+
class TestTestDeprecated:
def test_assert_deprecated(self):
test_case_instance = _DeprecationTestCase()
diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py
index 0ebcc72da..03e0e172a 100644
--- a/numpy/core/tests/test_dtype.py
+++ b/numpy/core/tests/test_dtype.py
@@ -3,6 +3,7 @@ import operator
import pytest
import ctypes
import gc
+import warnings
import numpy as np
from numpy.core._rational_tests import rational
@@ -1106,7 +1107,7 @@ def test_keyword_argument():
class TestFromDTypeAttribute:
def test_simple(self):
class dt:
- dtype = "f8"
+ dtype = np.dtype("f8")
assert np.dtype(dt) == np.float64
assert np.dtype(dt()) == np.float64
@@ -1130,22 +1131,21 @@ class TestFromDTypeAttribute:
# what this should be useful for. Note that if np.void is used
# numpy will think we are deallocating a base type [1.17, 2019-02].
dtype = np.dtype("f,f")
- pass
np.dtype(dt)
np.dtype(dt(1))
def test_void_subtype_recursion(self):
- class dt(np.void):
+ class vdt(np.void):
pass
- dt.dtype = dt
+ vdt.dtype = vdt
with pytest.raises(RecursionError):
- np.dtype(dt)
+ np.dtype(vdt)
with pytest.raises(RecursionError):
- np.dtype(dt(1))
+ np.dtype(vdt(1))
class TestDTypeClasses:
diff --git a/numpy/f2py/tests/test_array_from_pyobj.py b/numpy/f2py/tests/test_array_from_pyobj.py
index b719f2495..0524da342 100644
--- a/numpy/f2py/tests/test_array_from_pyobj.py
+++ b/numpy/f2py/tests/test_array_from_pyobj.py
@@ -3,9 +3,8 @@ import sys
import copy
import pytest
-from numpy import (
- array, alltrue, ndarray, zeros, dtype, intp, clongdouble
- )
+import numpy as np
+
from numpy.testing import assert_, assert_equal
from numpy.core.multiarray import typeinfo
from . import util
@@ -119,7 +118,7 @@ _cast_dict['CFLOAT'] = _cast_dict['FLOAT'] + ['CFLOAT']
# 16 byte long double types this means the inout intent cannot be satisfied
# and several tests fail as the alignment flag can be randomly true or fals
# when numpy gains an aligned allocator the tests could be enabled again
-if ((intp().dtype.itemsize != 4 or clongdouble().dtype.alignment <= 8) and
+if ((np.intp().dtype.itemsize != 4 or np.clongdouble().dtype.alignment <= 8) and
sys.platform != 'win32'):
_type_names.extend(['LONGDOUBLE', 'CDOUBLE', 'CLONGDOUBLE'])
_cast_dict['LONGDOUBLE'] = _cast_dict['LONG'] + \
@@ -133,7 +132,7 @@ class Type:
_type_cache = {}
def __new__(cls, name):
- if isinstance(name, dtype):
+ if isinstance(name, np.dtype):
dtype0 = name
name = None
for n, i in typeinfo.items():
@@ -153,7 +152,8 @@ class Type:
info = typeinfo[self.NAME]
self.type_num = getattr(wrap, 'NPY_' + self.NAME)
assert_equal(self.type_num, info.num)
- self.dtype = info.type
+ self.dtype = np.dtype(info.type)
+ self.type = info.type
self.elsize = info.bits / 8
self.dtypechar = info.char
@@ -202,7 +202,7 @@ class Array:
# arr.dtypechar may be different from typ.dtypechar
self.arr = wrap.call(typ.type_num, dims, intent.flags, obj)
- assert_(isinstance(self.arr, ndarray), repr(type(self.arr)))
+ assert_(isinstance(self.arr, np.ndarray), repr(type(self.arr)))
self.arr_attr = wrap.array_attrs(self.arr)
@@ -225,11 +225,12 @@ class Array:
return
if intent.is_intent('cache'):
- assert_(isinstance(obj, ndarray), repr(type(obj)))
- self.pyarr = array(obj).reshape(*dims).copy()
+ assert_(isinstance(obj, np.ndarray), repr(type(obj)))
+ self.pyarr = np.array(obj).reshape(*dims).copy()
else:
- self.pyarr = array(array(obj, dtype=typ.dtypechar).reshape(*dims),
- order=self.intent.is_intent('c') and 'C' or 'F')
+ self.pyarr = np.array(
+ np.array(obj, dtype=typ.dtypechar).reshape(*dims),
+ order=self.intent.is_intent('c') and 'C' or 'F')
assert_(self.pyarr.dtype == typ,
repr((self.pyarr.dtype, typ)))
assert_(self.pyarr.flags['OWNDATA'], (obj, intent))
@@ -266,7 +267,7 @@ class Array:
repr((self.arr_attr[5][3], self.type.elsize)))
assert_(self.arr_equal(self.pyarr, self.arr))
- if isinstance(self.obj, ndarray):
+ if isinstance(self.obj, np.ndarray):
if typ.elsize == Type(obj.dtype).elsize:
if not intent.is_intent('copy') and self.arr_attr[1] <= 1:
assert_(self.has_shared_memory())
@@ -274,8 +275,7 @@ class Array:
def arr_equal(self, arr1, arr2):
if arr1.shape != arr2.shape:
return False
- s = arr1 == arr2
- return alltrue(s.flatten())
+ return (arr1 == arr2).all()
def __str__(self):
return str(self.arr)
@@ -285,7 +285,7 @@ class Array:
"""
if self.obj is self.arr:
return True
- if not isinstance(self.obj, ndarray):
+ if not isinstance(self.obj, np.ndarray):
return False
obj_attr = wrap.array_attrs(self.obj)
return obj_attr[0] == self.arr_attr[0]
@@ -318,7 +318,7 @@ class TestSharedMemory:
def test_in_from_2casttype(self):
for t in self.type.cast_types():
- obj = array(self.num2seq, dtype=t.dtype)
+ obj = np.array(self.num2seq, dtype=t.dtype)
a = self.array([len(self.num2seq)], intent.in_, obj)
if t.elsize == self.type.elsize:
assert_(
@@ -327,7 +327,7 @@ class TestSharedMemory:
assert_(not a.has_shared_memory(), repr(t.dtype))
def test_inout_2seq(self):
- obj = array(self.num2seq, dtype=self.type.dtype)
+ obj = np.array(self.num2seq, dtype=self.type.dtype)
a = self.array([len(self.num2seq)], intent.inout, obj)
assert_(a.has_shared_memory())
@@ -341,12 +341,12 @@ class TestSharedMemory:
raise SystemError('intent(inout) should have failed on sequence')
def test_f_inout_23seq(self):
- obj = array(self.num23seq, dtype=self.type.dtype, order='F')
+ obj = np.array(self.num23seq, dtype=self.type.dtype, order='F')
shape = (len(self.num23seq), len(self.num23seq[0]))
a = self.array(shape, intent.in_.inout, obj)
assert_(a.has_shared_memory())
- obj = array(self.num23seq, dtype=self.type.dtype, order='C')
+ obj = np.array(self.num23seq, dtype=self.type.dtype, order='C')
shape = (len(self.num23seq), len(self.num23seq[0]))
try:
a = self.array(shape, intent.in_.inout, obj)
@@ -359,14 +359,14 @@ class TestSharedMemory:
'intent(inout) should have failed on improper array')
def test_c_inout_23seq(self):
- obj = array(self.num23seq, dtype=self.type.dtype)
+ obj = np.array(self.num23seq, dtype=self.type.dtype)
shape = (len(self.num23seq), len(self.num23seq[0]))
a = self.array(shape, intent.in_.c.inout, obj)
assert_(a.has_shared_memory())
def test_in_copy_from_2casttype(self):
for t in self.type.cast_types():
- obj = array(self.num2seq, dtype=t.dtype)
+ obj = np.array(self.num2seq, dtype=t.dtype)
a = self.array([len(self.num2seq)], intent.in_.copy, obj)
assert_(not a.has_shared_memory(), repr(t.dtype))
@@ -377,14 +377,14 @@ class TestSharedMemory:
def test_in_from_23casttype(self):
for t in self.type.cast_types():
- obj = array(self.num23seq, dtype=t.dtype)
+ obj = np.array(self.num23seq, dtype=t.dtype)
a = self.array([len(self.num23seq), len(self.num23seq[0])],
intent.in_, obj)
assert_(not a.has_shared_memory(), repr(t.dtype))
def test_f_in_from_23casttype(self):
for t in self.type.cast_types():
- obj = array(self.num23seq, dtype=t.dtype, order='F')
+ obj = np.array(self.num23seq, dtype=t.dtype, order='F')
a = self.array([len(self.num23seq), len(self.num23seq[0])],
intent.in_, obj)
if t.elsize == self.type.elsize:
@@ -394,7 +394,7 @@ class TestSharedMemory:
def test_c_in_from_23casttype(self):
for t in self.type.cast_types():
- obj = array(self.num23seq, dtype=t.dtype)
+ obj = np.array(self.num23seq, dtype=t.dtype)
a = self.array([len(self.num23seq), len(self.num23seq[0])],
intent.in_.c, obj)
if t.elsize == self.type.elsize:
@@ -404,14 +404,14 @@ class TestSharedMemory:
def test_f_copy_in_from_23casttype(self):
for t in self.type.cast_types():
- obj = array(self.num23seq, dtype=t.dtype, order='F')
+ obj = np.array(self.num23seq, dtype=t.dtype, order='F')
a = self.array([len(self.num23seq), len(self.num23seq[0])],
intent.in_.copy, obj)
assert_(not a.has_shared_memory(), repr(t.dtype))
def test_c_copy_in_from_23casttype(self):
for t in self.type.cast_types():
- obj = array(self.num23seq, dtype=t.dtype)
+ obj = np.array(self.num23seq, dtype=t.dtype)
a = self.array([len(self.num23seq), len(self.num23seq[0])],
intent.in_.c.copy, obj)
assert_(not a.has_shared_memory(), repr(t.dtype))
@@ -420,7 +420,7 @@ class TestSharedMemory:
for t in self.type.all_types():
if t.elsize != self.type.elsize:
continue
- obj = array(self.num2seq, dtype=t.dtype)
+ obj = np.array(self.num2seq, dtype=t.dtype)
shape = (len(self.num2seq),)
a = self.array(shape, intent.in_.c.cache, obj)
assert_(a.has_shared_memory(), repr(t.dtype))
@@ -428,7 +428,7 @@ class TestSharedMemory:
a = self.array(shape, intent.in_.cache, obj)
assert_(a.has_shared_memory(), repr(t.dtype))
- obj = array(self.num2seq, dtype=t.dtype, order='F')
+ obj = np.array(self.num2seq, dtype=t.dtype, order='F')
a = self.array(shape, intent.in_.c.cache, obj)
assert_(a.has_shared_memory(), repr(t.dtype))
@@ -449,7 +449,7 @@ class TestSharedMemory:
for t in self.type.all_types():
if t.elsize >= self.type.elsize:
continue
- obj = array(self.num2seq, dtype=t.dtype)
+ obj = np.array(self.num2seq, dtype=t.dtype)
shape = (len(self.num2seq),)
try:
self.array(shape, intent.in_.cache, obj) # Should succeed
@@ -485,18 +485,18 @@ class TestSharedMemory:
shape = (2,)
a = self.array(shape, intent.hide, None)
assert_(a.arr.shape == shape)
- assert_(a.arr_equal(a.arr, zeros(shape, dtype=self.type.dtype)))
+ assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype)))
shape = (2, 3)
a = self.array(shape, intent.hide, None)
assert_(a.arr.shape == shape)
- assert_(a.arr_equal(a.arr, zeros(shape, dtype=self.type.dtype)))
+ assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype)))
assert_(a.arr.flags['FORTRAN'] and not a.arr.flags['CONTIGUOUS'])
shape = (2, 3)
a = self.array(shape, intent.c.hide, None)
assert_(a.arr.shape == shape)
- assert_(a.arr_equal(a.arr, zeros(shape, dtype=self.type.dtype)))
+ assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype)))
assert_(not a.arr.flags['FORTRAN'] and a.arr.flags['CONTIGUOUS'])
shape = (-1, 3)
@@ -514,18 +514,18 @@ class TestSharedMemory:
shape = (2,)
a = self.array(shape, intent.optional, None)
assert_(a.arr.shape == shape)
- assert_(a.arr_equal(a.arr, zeros(shape, dtype=self.type.dtype)))
+ assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype)))
shape = (2, 3)
a = self.array(shape, intent.optional, None)
assert_(a.arr.shape == shape)
- assert_(a.arr_equal(a.arr, zeros(shape, dtype=self.type.dtype)))
+ assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype)))
assert_(a.arr.flags['FORTRAN'] and not a.arr.flags['CONTIGUOUS'])
shape = (2, 3)
a = self.array(shape, intent.c.optional, None)
assert_(a.arr.shape == shape)
- assert_(a.arr_equal(a.arr, zeros(shape, dtype=self.type.dtype)))
+ assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype)))
assert_(not a.arr.flags['FORTRAN'] and a.arr.flags['CONTIGUOUS'])
def test_optional_from_2seq(self):
@@ -547,14 +547,14 @@ class TestSharedMemory:
assert_(not a.has_shared_memory())
def test_inplace(self):
- obj = array(self.num23seq, dtype=self.type.dtype)
+ obj = np.array(self.num23seq, dtype=self.type.dtype)
assert_(not obj.flags['FORTRAN'] and obj.flags['CONTIGUOUS'])
shape = obj.shape
a = self.array(shape, intent.inplace, obj)
assert_(obj[1][2] == a.arr[1][2], repr((obj, a.arr)))
a.arr[1][2] = 54
assert_(obj[1][2] == a.arr[1][2] ==
- array(54, dtype=self.type.dtype), repr((obj, a.arr)))
+ np.array(54, dtype=self.type.dtype), repr((obj, a.arr)))
assert_(a.arr is obj)
assert_(obj.flags['FORTRAN']) # obj attributes are changed inplace!
assert_(not obj.flags['CONTIGUOUS'])
@@ -563,17 +563,17 @@ class TestSharedMemory:
for t in self.type.cast_types():
if t is self.type:
continue
- obj = array(self.num23seq, dtype=t.dtype)
- assert_(obj.dtype.type == t.dtype)
- assert_(obj.dtype.type is not self.type.dtype)
+ obj = np.array(self.num23seq, dtype=t.dtype)
+ assert_(obj.dtype.type == t.type)
+ assert_(obj.dtype.type is not self.type.type)
assert_(not obj.flags['FORTRAN'] and obj.flags['CONTIGUOUS'])
shape = obj.shape
a = self.array(shape, intent.inplace, obj)
assert_(obj[1][2] == a.arr[1][2], repr((obj, a.arr)))
a.arr[1][2] = 54
assert_(obj[1][2] == a.arr[1][2] ==
- array(54, dtype=self.type.dtype), repr((obj, a.arr)))
+ np.array(54, dtype=self.type.dtype), repr((obj, a.arr)))
assert_(a.arr is obj)
assert_(obj.flags['FORTRAN']) # obj attributes changed inplace!
assert_(not obj.flags['CONTIGUOUS'])
- assert_(obj.dtype.type is self.type.dtype) # obj changed inplace!
+ assert_(obj.dtype.type is self.type.type) # obj changed inplace!
diff --git a/numpy/typing/tests/data/pass/dtype.py b/numpy/typing/tests/data/pass/dtype.py
index a97edc302..4f93426f3 100644
--- a/numpy/typing/tests/data/pass/dtype.py
+++ b/numpy/typing/tests/data/pass/dtype.py
@@ -31,7 +31,7 @@ np.dtype((np.float_, float))
class Test:
- dtype = float
+ dtype = np.dtype(float)
np.dtype(Test())