summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorMatti Picus <matti.picus@gmail.com>2022-09-07 12:55:38 -0400
committerGitHub <noreply@github.com>2022-09-07 12:55:38 -0400
commit65c10c1cfecb2b3ebd0306ed1c63b103158f80d5 (patch)
treec7189d614ac713d7df0399cd452d98c5562ba912 /numpy
parent0e960b985843ff99db06f89eadaa9f387b5a65f8 (diff)
parent235b75e939b8233f29a073175d93070fcfc5e8d1 (diff)
downloadnumpy-65c10c1cfecb2b3ebd0306ed1c63b103158f80d5.tar.gz
Merge pull request #21995 from eirrgang/mei-1468
BUG: Distinguish exact vs. equivalent dtype for C type aliases.
Diffstat (limited to 'numpy')
-rw-r--r--numpy/array_api/tests/test_asarray.py64
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c23
-rw-r--r--numpy/ma/tests/test_core.py2
3 files changed, 86 insertions, 3 deletions
diff --git a/numpy/array_api/tests/test_asarray.py b/numpy/array_api/tests/test_asarray.py
new file mode 100644
index 000000000..5c269823f
--- /dev/null
+++ b/numpy/array_api/tests/test_asarray.py
@@ -0,0 +1,64 @@
+import itertools
+
+import numpy as np
+
+
+def test_dtype_identity():
+ """Confirm the intended behavior for ``asarray`` results.
+
+ The result of ``asarray()`` should have the dtype provided through the
+ keyword argument, when used. This forces unique array handles to be
+ produced for unique np.dtype objects, but (for equivalent dtypes), the
+ underlying data (the base object) is shared with the original array object.
+
+ Ref https://github.com/numpy/numpy/issues/1468
+ """
+ int_array = np.array([1, 2, 3], dtype='i')
+ assert np.asarray(int_array) is int_array
+
+ # The character code resolves to the singleton dtype object provided
+ # by the numpy package.
+ assert np.asarray(int_array, dtype='i') is int_array
+
+ # Derive a dtype from n.dtype('i'), but add a metadata object to force
+ # the dtype to be distinct.
+ unequal_type = np.dtype('i', metadata={'spam': True})
+ annotated_int_array = np.asarray(int_array, dtype=unequal_type)
+ assert annotated_int_array is not int_array
+ assert annotated_int_array.base is int_array
+ # Create an equivalent descriptor with a new and distinct dtype instance.
+ equivalent_requirement = np.dtype('i', metadata={'spam': True})
+ annotated_int_array_alt = np.asarray(annotated_int_array,
+ dtype=equivalent_requirement)
+ assert unequal_type == equivalent_requirement
+ assert unequal_type is not equivalent_requirement
+ assert annotated_int_array_alt is not annotated_int_array
+ assert annotated_int_array_alt.dtype is equivalent_requirement
+
+ # Check the same logic for a pair of C types whose equivalence may vary
+ # between computing environments.
+ # Find an equivalent pair.
+ integer_type_codes = ('i', 'l', 'q')
+ integer_dtypes = [np.dtype(code) for code in integer_type_codes]
+ typeA = None
+ typeB = None
+ for typeA, typeB in itertools.permutations(integer_dtypes, r=2):
+ if typeA == typeB:
+ assert typeA is not typeB
+ break
+ assert isinstance(typeA, np.dtype) and isinstance(typeB, np.dtype)
+
+ # These ``asarray()`` calls may produce a new view or a copy,
+ # but never the same object.
+ long_int_array = np.asarray(int_array, dtype='l')
+ long_long_int_array = np.asarray(int_array, dtype='q')
+ assert long_int_array is not int_array
+ assert long_long_int_array is not int_array
+ assert np.asarray(long_int_array, dtype='q') is not long_int_array
+ array_a = np.asarray(int_array, dtype=typeA)
+ assert typeA == typeB
+ assert typeA is not typeB
+ assert array_a.dtype is typeA
+ assert array_a is not np.asarray(array_a, dtype=typeB)
+ assert np.asarray(array_a, dtype=typeB).dtype is typeB
+ assert array_a is np.asarray(array_a, dtype=typeB).base
diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c
index 18a572e51..f01752431 100644
--- a/numpy/core/src/multiarray/multiarraymodule.c
+++ b/numpy/core/src/multiarray/multiarraymodule.c
@@ -1627,12 +1627,31 @@ _array_fromobject_generic(
goto finish;
}
}
- /* One more chance */
+ /* One more chance for faster exit if user specified the dtype. */
oldtype = PyArray_DESCR(oparr);
if (PyArray_EquivTypes(oldtype, type)) {
if (copy != NPY_COPY_ALWAYS && STRIDING_OK(oparr, order)) {
Py_INCREF(op);
- ret = oparr;
+ if (oldtype == type) {
+ ret = oparr;
+ }
+ else {
+ /* Create a new PyArrayObject from the caller's
+ * PyArray_Descr. Use the reference `op` as the base
+ * object. */
+ Py_INCREF(type);
+ ret = (PyArrayObject *)PyArray_NewFromDescrAndBase(
+ Py_TYPE(op),
+ type,
+ PyArray_NDIM(oparr),
+ PyArray_DIMS(oparr),
+ PyArray_STRIDES(oparr),
+ PyArray_DATA(oparr),
+ PyArray_FLAGS(oparr),
+ op,
+ op
+ );
+ }
goto finish;
}
else {
diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py
index b056d5169..3997f44e3 100644
--- a/numpy/ma/tests/test_core.py
+++ b/numpy/ma/tests/test_core.py
@@ -5387,7 +5387,7 @@ def test_ufunc_with_out_varied():
def test_astype_mask_ordering():
- descr = [('v', int, 3), ('x', [('y', float)])]
+ descr = np.dtype([('v', int, 3), ('x', [('y', float)])])
x = array([
[([1, 2, 3], (1.0,)), ([1, 2, 3], (2.0,))],
[([1, 2, 3], (3.0,)), ([1, 2, 3], (4.0,))]], dtype=descr)