@@ -857,6 +857,72 @@ static void *const conv1d_full_data[] = {NULL};
857857static const char conv1d_full_typecodes [] = {NPY_DOUBLE , NPY_DOUBLE , NPY_DOUBLE };
858858
859859
860+ /*
861+ * Test helpers for PyUFunc_ReplaceLoopBySignature.
862+ *
863+ * _constant42_loop: unary float64 loop that always writes 42.0.
864+ */
865+ static void
866+ _constant42_loop (char * * args , npy_intp const * dimensions ,
867+ npy_intp const * steps , void * NPY_UNUSED (data ))
868+ {
869+ npy_intp n = dimensions [0 ];
870+ char * out = args [1 ];
871+ npy_intp out_step = steps [1 ];
872+ for (npy_intp i = 0 ; i < n ; i ++ ) {
873+ * (double * )out = 42.0 ;
874+ out += out_step ;
875+ }
876+ }
877+
878+ /*
879+ * replace_loop(ufunc): Replace the dd->d loop with _constant42_loop.
880+ * Only works for unary ufuncs. Returns a capsule holding the old loop.
881+ */
882+ static PyObject *
883+ UMath_Tests_replace_loop (PyObject * NPY_UNUSED (dummy ), PyObject * args )
884+ {
885+ PyUFuncObject * ufunc ;
886+ if (!PyArg_ParseTuple (args , "O!" , & PyUFunc_Type , & ufunc )) {
887+ return NULL ;
888+ }
889+ if (ufunc -> nin != 1 || ufunc -> nout != 1 ) {
890+ PyErr_SetString (PyExc_ValueError ,
891+ "replace_loop only supports unary ufuncs" );
892+ return NULL ;
893+ }
894+ int signature [2 ] = {NPY_DOUBLE , NPY_DOUBLE };
895+ PyUFuncGenericFunction oldfunc = NULL ;
896+ if (PyUFunc_ReplaceLoopBySignature (
897+ ufunc , _constant42_loop , signature , & oldfunc ) < 0 ) {
898+ PyErr_SetString (PyExc_RuntimeError ,
899+ "failed to find a float64 loop" );
900+ return NULL ;
901+ }
902+ return PyCapsule_New ((void * )oldfunc , "oldfunc" , NULL );
903+ }
904+
905+ /*
906+ * restore_loop(ufunc, capsule): Restore the loop saved by replace_loop.
907+ */
908+ static PyObject *
909+ UMath_Tests_restore_loop (PyObject * NPY_UNUSED (dummy ), PyObject * args )
910+ {
911+ PyUFuncObject * ufunc ;
912+ PyObject * capsule ;
913+ if (!PyArg_ParseTuple (args , "O!O" , & PyUFunc_Type , & ufunc , & capsule )) {
914+ return NULL ;
915+ }
916+ PyUFuncGenericFunction oldfunc = (PyUFuncGenericFunction )
917+ PyCapsule_GetPointer (capsule , "oldfunc" );
918+ if (oldfunc == NULL ) {
919+ return NULL ;
920+ }
921+ int signature [2 ] = {NPY_DOUBLE , NPY_DOUBLE };
922+ PyUFunc_ReplaceLoopBySignature (ufunc , oldfunc , signature , NULL );
923+ Py_RETURN_NONE ;
924+ }
925+
860926static PyMethodDef UMath_TestsMethods [] = {
861927 {"test_signature" , UMath_Tests_test_signature , METH_VARARGS ,
862928 "Test signature parsing of ufunc. \n"
@@ -865,6 +931,12 @@ static PyMethodDef UMath_TestsMethods[] = {
865931 "internals. \n" ,
866932 },
867933 {"test_dispatch" , UMath_Tests_test_dispatch , METH_NOARGS , NULL },
934+ {"replace_loop" , UMath_Tests_replace_loop , METH_VARARGS ,
935+ "Replace the float64 loop of a ufunc with one that outputs 42.0.\n"
936+ "Returns a capsule holding the old loop for restore_loop().\n" },
937+ {"restore_loop" , UMath_Tests_restore_loop , METH_VARARGS ,
938+ "Restore a ufunc loop previously replaced by replace_loop().\n"
939+ "Arguments: ufunc, capsule\n" },
868940 {NULL , NULL , 0 , NULL } /* Sentinel */
869941};
870942
0 commit comments