@@ -1565,49 +1565,66 @@ PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth,
1565
1565
1566
1566
if (NPY_UNLIKELY (fixed_descriptor != NULL && PyDataType_HASSUBARRAY (dtype ))) {
1567
1567
/*
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]`.
1573
1580
*/
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
+ }
1580
1592
}
1581
- assert (PyArray_NDIM (ret ) != ndim );
1593
+ if (includes_array ) {
1594
+ npy_free_coercion_cache (cache );
1582
1595
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 ) {
1587
1618
Py_DECREF (ret );
1588
1619
return NULL ;
1589
1620
}
1590
- }
1591
- else {
1592
- npy_free_coercion_cache (cache );
1621
+
1593
1622
if (setArrayFromSequence (ret , op , 0 , NULL ) < 0 ) {
1594
1623
Py_DECREF (ret );
1595
1624
return NULL ;
1596
1625
}
1626
+ return (PyObject * )ret ;
1597
1627
}
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 ;
1611
1628
}
1612
1629
1613
1630
if (dtype == NULL ) {
@@ -1700,26 +1717,52 @@ PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth,
1700
1717
}
1701
1718
1702
1719
/* Create a new array and copy the data */
1720
+ Py_INCREF (dtype ); /* hold on in case of a subarray that is replaced */
1703
1721
ret = (PyArrayObject * )PyArray_NewFromDescr (
1704
1722
& PyArray_Type , dtype , ndim , dims , NULL , NULL ,
1705
1723
flags & NPY_ARRAY_F_CONTIGUOUS , NULL );
1706
1724
if (ret == NULL ) {
1707
1725
npy_free_coercion_cache (cache );
1726
+ Py_DECREF (dtype );
1708
1727
return NULL ;
1709
1728
}
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
+
1710
1739
if (cache == NULL ) {
1711
1740
/* This is a single item. Set it directly. */
1712
1741
assert (ndim == 0 );
1713
1742
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 );
1715
1745
Py_DECREF (ret );
1716
1746
return NULL ;
1717
1747
}
1748
+ Py_DECREF (dtype );
1718
1749
return (PyObject * )ret ;
1719
1750
}
1720
1751
assert (ndim != 0 );
1721
1752
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 ) {
1723
1766
Py_DECREF (ret );
1724
1767
return NULL ;
1725
1768
}
0 commit comments