@@ -362,7 +362,8 @@ PyArray_GetSubType(int narrays, PyArrayObject **arrays) {
362362 */
363363NPY_NO_EXPORT PyArrayObject *
364364PyArray_ConcatenateArrays (int narrays , PyArrayObject * * arrays , int axis ,
365- PyArrayObject * ret )
365+ PyArrayObject * ret , PyArray_Descr * dtype ,
366+ NPY_CASTING casting )
366367{
367368 int iarrays , idim , ndim ;
368369 npy_intp shape [NPY_MAXDIMS ];
@@ -426,6 +427,7 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis,
426427 }
427428
428429 if (ret != NULL ) {
430+ assert (dtype == NULL );
429431 if (PyArray_NDIM (ret ) != ndim ) {
430432 PyErr_SetString (PyExc_ValueError ,
431433 "Output array has wrong dimensionality" );
@@ -445,10 +447,16 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis,
445447 /* Get the priority subtype for the array */
446448 PyTypeObject * subtype = PyArray_GetSubType (narrays , arrays );
447449
448- /* Get the resulting dtype from combining all the arrays */
449- PyArray_Descr * dtype = PyArray_ResultType (narrays , arrays , 0 , NULL );
450450 if (dtype == NULL ) {
451- return NULL ;
451+ /* Get the resulting dtype from combining all the arrays */
452+ dtype = (PyArray_Descr * )PyArray_ResultType (
453+ narrays , arrays , 0 , NULL );
454+ if (dtype == NULL ) {
455+ return NULL ;
456+ }
457+ }
458+ else {
459+ Py_INCREF (dtype );
452460 }
453461
454462 /*
@@ -494,7 +502,7 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis,
494502
495503 /* Copy the data for this array */
6284
496504 if (PyArray_AssignArray ((PyArrayObject * )sliding_view , arrays [iarrays ],
497- NULL , NPY_SAME_KIND_CASTING ) < 0 ) {
505+ NULL , casting ) < 0 ) {
498506 Py_DECREF (sliding_view );
499507 Py_DECREF (ret );
500508 return NULL ;
@@ -514,7 +522,9 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis,
514522 */
515523NPY_NO_EXPORT PyArrayObject *
516524PyArray_ConcatenateFlattenedArrays (int narrays , PyArrayObject * * arrays ,
517- NPY_ORDER order , PyArrayObject * ret )
525+ NPY_ORDER order , PyArrayObject * ret ,
526+ PyArray_Descr * dtype , NPY_CASTING casting ,
527+ npy_bool casting_not_passed )
518528{
519529 int iarrays ;
520530 npy_intp shape = 0 ;
@@ -541,7 +551,10 @@ PyArray_ConcatenateFlattenedArrays(int narrays, PyArrayObject **arrays,
541551 }
542552 }
543553
554+ int out_passed = 0 ;
544555 if (ret != NULL ) {
556+ assert (dtype == NULL );
557+ out_passed = 1 ;
545558 if (PyArray_NDIM (ret ) != 1 ) {
546559 PyErr_SetString (PyExc_ValueError ,
547560 "Output array must be 1D" );
@@ -560,10 +573,16 @@ PyArray_ConcatenateFlattenedArrays(int narrays, PyArrayObject **arrays,
560573 /* Get the priority subtype for the array */
561574 PyTypeObject * subtype = PyArray_GetSubType (narrays , arrays );
562575
563- /* Get the resulting dtype from combining all the arrays */
564- PyArray_Descr * dtype = PyArray_ResultType (narrays , arrays , 0 , NULL );
565576 if (dtype == NULL ) {
566- return NULL ;
577+ /* Get the resulting dtype from combining all the arrays */
578+ dtype = (PyArray_Descr * )PyArray_ResultType (
579+ narrays , arrays , 0 , NULL );
580+ if (dtype == NULL ) {
581+ return NULL ;
582+ }
583+ }
584+ else {
585+ Py_INCREF (dtype );
567586 }
568587
569588 stride = dtype -> elsize ;
@@ -593,10 +612,37 @@ PyArray_ConcatenateFlattenedArrays(int narrays, PyArrayObject **arrays,
593612 return NULL ;
594613 }
595614
615+ int give_deprecation_warning = 1 ; /* To give warning for just one input array. */
596616 for (iarrays = 0 ; iarrays < narrays ; ++ iarrays ) {
597617 /* Adjust the window dimensions for this array */
598618 sliding_view -> dimensions [0 ] = PyArray_SIZE (arrays [iarrays ]);
599619
620+ if (!PyArray_CanCastArrayTo (
621+ arrays [iarrays ], PyArray_DESCR (ret ), casting )) {
622+ /* This should be an error, but was previously allowed here. */
623+ if (casting_not_passed && out_passed ) {
624+ /* NumPy 1.20, 2020-09-03 */
625+ if (give_deprecation_warning && DEPRECATE (
626+ "concatenate() with `axis=None` will use same-kind "
627+ "casting by default in the future. Please use "
628+ "`casting='unsafe'` to retain the old behaviour. "
629+ "In the future this will be a TypeError." ) < 0 ) {
630+ Py_DECREF (sliding_view );
631+ Py_DECREF (ret );
632+ return NULL ;
633+ }
634+ give_deprecation_warning = 0 ;
635+ }
636+ else {
637+ npy_set_invalid_cast_error (
638+ PyArray_DESCR (arrays [iarrays ]), PyArray_DESCR (ret ),
639+ casting , PyArray_NDIM (arrays [iarrays ]) == 0 );
640+ Py_DECREF (sliding_view );
641+ Py_DECREF (ret );
642+ return NULL ;
643+ }
644+ }
645+
600646 /* Copy the data for this array */
601647 if (PyArray_CopyAsFlat ((PyArrayObject * )sliding_view , arrays [iarrays ],
602648 order ) < 0 ) {
@@ -614,8 +660,21 @@ PyArray_ConcatenateFlattenedArrays(int narrays, PyArrayObject **arrays,
614660 return ret ;
615661}
616662
663+
664+ /**
665+ * Implementation for np.concatenate
666+ *
667+ * @param op Sequence of arrays to concatenate
668+ * @param axis Axis to concatenate along
669+ * @param ret output array to fill
670+ * @param dtype Forced output array dtype (cannot be combined with ret)
671+ * @param casting Casting mode used
672+ * @param casting_not_passed Deprecation helper
673+ */
617674NPY_NO_EXPORT PyObject *
618- PyArray_ConcatenateInto (PyObject * op , int axis , PyArrayObject * ret )
675+ PyArray_ConcatenateInto (PyObject * op ,
676+ int axis , PyArrayObject * ret , PyArray_Descr * dtype ,
677+ NPY_CASTING casting , npy_bool casting_not_passed )
619678{
620679 int iarrays , narrays ;
621680 PyArrayObject * * arrays ;
@@ -625,6 +684,12 @@ PyArray_ConcatenateInto(PyObject *op, int axis, PyArrayObject *ret)
625684 "The first input argument needs to be a sequence" );
626685 return NULL ;
627686 }
687+ if (ret != NULL && dtype != NULL ) {
688+ PyErr_SetString (PyExc_TypeError ,
689+ "concatenate() only takes `out` or `dtype` as an "
690+ "argument, but both were provided." );
691+ return NULL ;
692+ }
628693
629694 /* Convert the input list into arrays */
630695 narrays = PySequence_Size (op );
@@ -651,10 +716,13 @@ PyArray_ConcatenateInto(PyObject *op, int axis, PyArrayObject *ret)
651716 }
652717
653718 if (axis >= NPY_MAXDIMS ) {
654- ret = PyArray_ConcatenateFlattenedArrays (narrays , arrays , NPY_CORDER , ret );
719+ ret = PyArray_ConcatenateFlattenedArrays (
720+ narrays , arrays , NPY_CORDER , ret , dtype ,
721+ casting , casting_not_passed );
655722 }
656723 else {
657- ret = PyArray_ConcatenateArrays (narrays , arrays , axis , ret );
724+ ret = PyArray_ConcatenateArrays (
725+ narrays , arrays , axis , ret , dtype , casting );
658726 }
659727
660728 for (iarrays = 0 ; iarrays < narrays ; ++ iarrays ) {
@@ -686,7 +754,16 @@ PyArray_ConcatenateInto(PyObject *op, int axis, PyArrayObject *ret)
686754NPY_NO_EXPORT PyObject *
687755PyArray_Concatenate (PyObject * op , int axis )
688756{
689- return PyArray_ConcatenateInto (op , axis , NULL );
757+ /* retain legacy behaviour for casting */
758+ NPY_CASTING casting ;
759+ if (axis >= NPY_MAXDIMS ) {
760+ casting = NPY_UNSAFE_CASTING ;
761+ }
762+ else {
763+ casting = NPY_SAME_KIND_CASTING ;
764+ }
765+ return PyArray_ConcatenateInto (
766+ op , axis , NULL , NULL , casting , 0 );
690767}
691768
692769static int
@@ -2259,11 +2336,27 @@ array_concatenate(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds)
22592336{
22602337 PyObject * a0 ;
22612338 PyObject * out = NULL ;
2339+ PyArray_Descr * dtype = NULL ;
2340+ NPY_CASTING casting = NPY_SAME_KIND_CASTING ;
2341+ PyObject * casting_obj = NULL ;
2342+ PyObject * res ;
22622343 int axis = 0 ;
2263- static char * kwlist [] = {"seq" , "axis" , "out" , NULL };
2264-
2265- if (!PyArg_ParseTupleAndKeywords (args , kwds , "O|O&O:concatenate" , kwlist ,
2266- & a0 , PyArray_AxisConverter , & axis , & out )) {
2344+ static char * kwlist [] = {"seq" , "axis" , "out" , "dtype" , "casting" , NULL };
2345+ if (!PyArg_ParseTupleAndKeywords (args , kwds , "O|O&O$O&O:concatenate" , kwlist ,
2346+ & a0 , PyArray_AxisConverter , & axis , & out ,
2347+ PyArray_DescrConverter2 , & dtype , & casting_obj )) {
2348+ return NULL ;
2349+ }
2350+ int casting_not_passed = 0 ;
2351+ if (casting_obj == NULL ) {
2352+ /*
2353+ * Casting was not passed in, needed for deprecation only.
2354+ * This should be simplified once the deprecation is finished.
2355+ */
2356+ casting_not_passed = 1 ;
2357+ }
2358+ else if (!PyArray_CastingConverter (casting_obj , & casting )) {
2359+ Py_XDECREF (dtype );
22672360 return NULL ;
22682361 }
22692362 if (out != NULL ) {
@@ -2272,10 +2365,14 @@ array_concatenate(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds)
22722365 }
22732366 else if (!PyArray_Check (out )) {
22742367 PyErr_SetString (PyExc_TypeError , "'out' must be an array" );
2368+ Py_XDECREF (dtype );
22752369 return NULL ;
22762370 }
22772371 }
2278- return PyArray_ConcatenateInto (a0 , axis , (PyArrayObject * )out );
2372+ res = PyArray_ConcatenateInto (a0 , axis , (PyArrayObject * )out , dtype ,
2373+ casting , casting_not_passed );
2374+ Py_XDECREF (dtype );
2375+ return res ;
22792376}
22802377
22812378static PyObject *
0 commit comments