@@ -1565,49 +1565,66 @@ PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth,
15651565
15661566 if (NPY_UNLIKELY (fixed_descriptor != NULL && PyDataType_HASSUBARRAY (dtype ))) {
15671567 /*
1568- * When a subarray dtype was passed in, its dimensions are absorbed
1569- * into the array dimension (causing a dimension mismatch).
1570- * We can't reasonably handle this because of inconsistencies in
1571- * how it was handled (depending on nested list vs. embed array-likes).
1572- * So we give a deprecation warning and fall back to legacy code.
1568+ * When a subarray dtype was passed in, its dimensions are appended
1569+ * to the array dimension (causing a dimension mismatch).
1570+ * There is a problem with that, because if we coerce from non-arrays
1571+ * we do this correctly by element (as defined by tuples), but for
1572+ * arrays we first append the dimensions and then assign to the base
1573+ * dtype and then assign which causes the problem.
1574+ *
1575+ * Thus, we check if there is an array included, in that case we
1576+ * give a FutureWarning.
1577+ * When the warning is removed, PyArray_Pack will have to ensure
1578+ * that that it does not append the dimensions when creating the
1579+ * subarrays to assign `arr[0] = obj[0]`.
15731580 */
1574- ret = (PyArrayObject * )PyArray_NewFromDescr (
1575- & PyArray_Type , dtype , ndim , dims , NULL , NULL ,
1576- flags & NPY_ARRAY_F_CONTIGUOUS , NULL );
1577- if (ret == NULL ) {
1578- npy_free_coercion_cache (cache );
1579- return NULL ;
1581+ int includes_array = 0 ;
1582+ if (cache != NULL ) {
1583+ /* This is not ideal, but it is a pretty special case */
1584+ coercion_cache_obj * next = cache ;
1585+ while (next != NULL ) {
1586+ if (!next -> sequence ) {
1587+ includes_array = 1 ;
1588+ break ;
1589+ }
1590+ next = next -> next ;
1591+ }
15801592 }
1581- assert (PyArray_NDIM (ret ) != ndim );
1593+ if (includes_array ) {
1594+ npy_free_coercion_cache (cache );
15821595
1583- if (cache == NULL ) {
1584- /* This is a single item. Sets only first subarray element. */
1585- assert (ndim == 0 );
1586- if (PyArray_Pack (PyArray_DESCR (ret ), PyArray_DATA (ret ), op ) < 0 ) {
1596+ ret = (PyArrayObject * ) PyArray_NewFromDescr (
1597+ & PyArray_Type , dtype , ndim , dims , NULL , NULL ,
1598+ flags & NPY_ARRAY_F_CONTIGUOUS , NULL );
1599+ if (ret == NULL ) {
1600+ return NULL ;
1601+ }
1602+ assert (PyArray_NDIM (ret ) != ndim );
1603+
1604+ /* NumPy 1.20, 2020-10-01 */
1605+ if (DEPRECATE_FUTUREWARNING (
1606+ "creating an array with a subarray dtype will behave "
1607+ "differently when the `np.array()` (or `asarray`, etc.) "
1608+ "call includes an array or array object.\n"
1609+ "If you are converting a single array or a list of arrays,"
1610+ "you can opt-in to the future behaviour using:\n"
1611+ " np.array(arr, dtype=np.dtype(['f', dtype]))['f']\n"
1612+ " np.array([arr1, arr2], dtype=np.dtype(['f', dtype]))['f']\n"
1613+ "\n"
1614+ "By including a new field and indexing it after the "
1615+ "conversion.\n"
1616+ "This may lead to a different result or to current failures "
1617+ "succeeding. (FutureWarning since NumPy 1.20)" ) < 0 ) {
15871618 Py_DECREF (ret );
15881619 return NULL ;
15891620 }
1590- }
1591- else {
1592- npy_free_coercion_cache (cache );
1621+
15931622 if (setArrayFromSequence (ret , op , 0 , NULL ) < 0 ) {
15941623 Py_DECREF (ret );
15951624 return NULL ;
15961625 }
1626+ return (PyObject * )ret ;
15971627 }
1598- /* NumPy 1.20, 2020-10-01 */
1599- if (DEPRECATE (
1600- "using a dtype with a subarray field is deprecated. "
1601- "This can lead to inconsistent behaviour due to the resulting "
1602- "dtype being different from the input dtype. "
1603- "You may try to use `dtype=dtype.base`, which should give the "
1604- "same result for most inputs, but does not guarantee the "
1605- "output dimensions to match the subarray ones. "
1606- "(Deprecated NumPy 1.20)" )) {
1607- Py_DECREF (ret );
1608- return NULL ;
1609- }
1610- return (PyObject * )ret ;
16111628 }
16121629
16131630 if (dtype == NULL ) {
@@ -1700,26 +1717,52 @@ PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth,
17001717 }
17011718
17021719 /* Create a new array and copy the data */
1720+ Py_INCREF (dtype ); /* hold on in case of a subarray that is replaced */
17031721 ret = (PyArrayObject * )PyArray_NewFromDescr (
17041722 & PyArray_Type , dtype , ndim , dims , NULL , NULL ,
17051723 flags & NPY_ARRAY_F_CONTIGUOUS , NULL );
17061724 if (ret == NULL ) {
17071725 npy_free_coercion_cache (cache );
1726+ Py_DECREF (dtype );
17081727 return NULL ;
17091728 }
1729+ if (ndim == PyArray_NDIM (ret )) {
1730+ /*
1731+ * Appending of dimensions did not occur, so use the actual dtype
1732+ * below. This is relevant for S0 or U0 which can be replaced with
1733+ * S1 or U1, although that should likely change.
1734+ */
1735+ Py_SETREF (dtype , PyArray_DESCR (ret ));
1736+ Py_INCREF (dtype );
1737+ }
1738+
17101739 if (cache == NULL ) {
17111740 /* This is a single item. Set it directly. */
17121741 assert (ndim == 0 );
17131742
1714- if (PyArray_Pack (PyArray_DESCR (ret ), PyArray_BYTES (ret ), op ) < 0 ) {
1743+ if (PyArray_Pack (dtype , PyArray_BYTES (ret ), op ) < 0 ) {
1744+ Py_DECREF (dtype );
17151745 Py_DECREF (ret );
17161746 return NULL ;
17171747 }
1748+ Py_DECREF (dtype );
17181749 return (PyObject * )ret ;
17191750 }
17201751 assert (ndim != 0 );
17211752 assert (op == cache -> converted_obj );
1722- if (PyArray_AssignFromCache (ret , cache ) < 0 ) {
1753+
1754+ /* Decrease the number of dimensions to the detected ones */
1755+ int out_ndim = PyArray_NDIM (ret );
1756+ PyArray_Descr * out_descr = PyArray_DESCR (ret );
1757+ ((PyArrayObject_fields * )ret )-> nd = ndim ;
1758+ ((PyArrayObject_fields * )ret )-> descr = dtype ;
1759+
1760+ int success = PyArray_AssignFromCache (ret , cache );
1761+
1762+ ((PyArrayObject_fields * )ret )-> nd = out_ndim ;
1763+ ((PyArrayObject_fields * )ret )-> descr = out_descr ;
1764+ Py_DECREF (dtype );
1765+ if (success < 0 ) {
17231766 Py_DECREF (ret );
17241767 return NULL ;
17251768 }
0 commit comments