Repository: numpy/numpy
Pull request: API: Fix cython exception handling for exported extern C functions
This hopefully fixes them for all the functions currently in the `.pyd` files.  A surprising amount of them look like scary thing I wouldn't mind to just delete :).

Anyway, had looked at some Cython code today and remembered this.

Closes gh-19291

Problem statement:
BUG,ENH: cython stubs should use `except (0|-1?)` etc. for integer returning functions
According to the docs `PyArray_IntpConverter` is supposed to just return `NPY_FAIL` (0) on failure and `NPY_SUCCESS` (1) otherwise. So to help manually raise an exception I write something like:
```cython
cdef np.PyArray_Dims shape
if not np.PyArray_IntpConverter(size, &shape):
	raise ValueError("`size` is not a valid argument.")
```

But this ends up raising a `SystemError` with the message: `<class 'ValueError'> returned a result with an error set`, Which ends up causing unittests to fail since the intended ValueError is not raised.
### Reproducing code example:
Passing a set for `size` instead of the expected tuple.

Is there a way to silence the `SystemError` and only raise the intended one?

### NumPy/Python version information:

```
1.19.0 3.6.12 (default, May 11 2021, 17:48:34) 
[GCC 10.2.0]
```

Hints from issue discussion:
An exception must be/is set if a converter fails.  It may be that the cython `.pyd` file is wrong though, and might be nicer to read: `int PyArray_IntpConverter(...) except 0`.  And then be used with a try/except branch if you want to replace the error.

Although, I guess there may be ways to explicitly probe an in-fly error here. (If just by calling `PyErr_Occurred()` explicitly)
The declaration in `__init__.pxd` currently is
```cython
int PyArray_IntpConverter (object, PyArray_Dims *)
```

Looking at the function implementation, it seems that the exception originates from this line:
https://github.com/numpy/numpy/blob/20e14f3a6a2368d240820a4b6293f0837406c087/numpy/core/src/multiarray/conversion_utils.c#L142
which maybe the one responsible for the "silent" exception. I supposed the `PyErr_Occurred()` would be added in the following block, right?

Also just noticed this is related to: https://github.com/numpy/numpy/issues/19010, I guess I no longer have a reason to procrastinate now :D
No, it is unrelated to gh-19010, because the behaviour is absolutely correct.  The `__init__.pxd` may be wrong, but from a C-perspective, it is exactly as advertised.
> No, it is unrelated to [gh-19010](https://github.com/numpy/numpy/issues/19010), because the behaviour is absolutely correct. The `__init__.pxd` may be wrong, but from a C-perspective, it is exactly as advertised.

Not the best wording there from my last reply. I meant that the function is directly related to the ones that need to be addressed in https://github.com/numpy/numpy/issues/19010, not the issue itself. What do you suggest is the correct course of action to address this one?
@seberg By the way, [PyDimMem_FREE](https://numpy.org/devdocs/reference/c-api/array.html#c.PyDimMem_FREE) is not available as part of the cython declarations and is required by [PyArray_IntpConverter](https://numpy.org/devdocs/reference/c-api/array.html#c.PyArray_IntpConverter) according to the docs, to free the pointer in `shape`. I use `PyMem_Free` as a workaround currently but maybe this also should be addressed? Or at least the docs be updated?
I am pretty sure that `PyMem_Free` is incorrect (a debug python will shout at you, a non-debug python might just crash randomly at some point). `PyMem_RawFree` or just `free` is probably right.  Ideally we would force you to use `PyDimMem_FREE` (and make it a function!), so that we could use `PyMem_Free` or similar at some point.  The fact that this function allocates the dimensions at all is a bit annoying IMO.

Honestly, I am not sure what to do best here.  For all I know the cython definitions should be adapted to way I said.  At least unless the current version is actually useful...  `PyDimMem_FREE` should maybe be exposed (I don't particularly like the API, there is no reason the caller shouldn't just stack allocate that array to begin with, it would suit most usages better).
Thanks for the suggestion, I wasn't quite sure which free function to pick. That helps a lot. Maybe a new function that allows the caller to allocate the memory and pass a pointer into it is a possibility? Something like:
```
int PyArray_IntpConverter2(PyObject *obj, npy_intp* dim_ptr, PyArray_Dims *seq)
```
which will do the length calculation and then assign the `ptr` member of `seq` struct to the `dim_ptr` buffer.
What is the bigger picture: why are you parsing shapes? Perhaps the problem is that we expose `PyArray_IntpConverter` and `PyArray_Dims` at all.
> What is the bigger picture: why are you parsing shapes? Perhaps the problem is that we expose `PyArray_IntpConverter` and `PyArray_Dims` at all.

To return a shape broadcasted array, as in functions like `normal([1, 2, 3], size=(2, 4, 3)) -> (2, 4, 3)`. To do this using the C-API does not seem very easy. One way I did this is to:
1) create an empty (2, 4, 3) array
2) broadcast that with the input using PyArray_MultiIterNew3() to get a new broadcast object and
3) then use that to populate the return array. 

I dont like this way because it wastes memory. The other is to use `PyArray_IntpConverter` on the input shape to create a `shape` variable of type `PyArray_Dims` that can be used with `PyArray_BroadcastToShape` on the individual inputs to create iterators. This way I dont have to create an intermediate empty array just so I can broadcast using the multi-iterator. Maybe there are functions I am not aware of but broadcasting using the C-API seems a bit involved hence why I require this approach. If you know a better way to do this using the C-API i'd be happy to hear it.
> functions like normal([1, 2, 3], size=(2, 4, 3)) -> (2, 4, 3)

Can you link to this function? I still don't get what the goal is. If you want to return a shape-broadcast array via a call to a numpy function, just call the function and it will return the array? I am missing something basic.

Commit message: API: Fix cython exception handling for exported extern C functions

This hopefully fixes them for all the functions currently in the `.pyd`
files.  A surprising amount of them look like scary thing I wouldn't
mind to just delete :).

Anyway, had looked at some Cython code today and remembered this.

Closes gh-19291

Patch to review:
diff --git a/numpy/__init__.cython-30.pxd b/numpy/__init__.cython-30.pxd
index 7771d32bd123..e7cfcc5237c3 100644
--- a/numpy/__init__.cython-30.pxd
+++ b/numpy/__init__.cython-30.pxd
@@ -401,7 +401,7 @@ cdef extern from "numpy/arrayobject.h":
     int PyArray_TYPE(ndarray arr) nogil
 
     object PyArray_GETITEM(ndarray arr, void *itemptr)
-    int PyArray_SETITEM(ndarray arr, void *itemptr, object obj)
+    int PyArray_SETITEM(ndarray arr, void *itemptr, object obj) except -1
 
     bint PyTypeNum_ISBOOL(int) nogil
     bint PyTypeNum_ISUNSIGNED(int) nogil
@@ -452,7 +452,7 @@ cdef extern from "numpy/arrayobject.h":
 
     bint PyArray_SAFEALIGNEDCOPY(ndarray) nogil
     bint PyArray_ISNBO(char) nogil              # works on ndarray.byteorder
-    bint PyArray_IsNativeByteOrder(char) nogil  # works on ndarray.byteorder
+    bint PyArray_IsNativeByteOrder(char) nogil # works on ndarray.byteorder
     bint PyArray_ISNOTSWAPPED(ndarray) nogil
     bint PyArray_ISBYTESWAPPED(ndarray) nogil
 
@@ -549,7 +549,7 @@ cdef extern from "numpy/arrayobject.h":
     # Functions taking dtype and returning object/ndarray are disabled
     # for now as they steal dtype references. I'm conservative and disable
     # more than is probably needed until it can be checked further.
-    int PyArray_SetNumericOps        (object)
+    int PyArray_SetNumericOps (object) except -1
     object PyArray_GetNumericOps ()
     int PyArray_INCREF (ndarray)
     int PyArray_XDECREF (ndarray)
@@ -559,11 +559,11 @@ cdef extern from "numpy/arrayobject.h":
     char * PyArray_Zero (ndarray)
     char * PyArray_One (ndarray)
     #object PyArray_CastToType (ndarray, dtype, int)
-    int PyArray_CastTo (ndarray, ndarray)
-    int PyArray_CastAnyTo (ndarray, ndarray)
-    int PyArray_CanCastSafely (int, int)
-    npy_bool PyArray_CanCastTo (dtype, dtype)
-    int PyArray_ObjectType (object, int)
+    int PyArray_CastTo (ndarray, ndarray) except -1
+    int PyArray_CastAnyTo (ndarray, ndarray) except -1
+    int PyArray_CanCastSafely (int, int)  # writes errors
+    npy_bool PyArray_CanCastTo (dtype, dtype)  # writes errors
+    int PyArray_ObjectType (object, int) except 0
     dtype PyArray_DescrFromObject (object, dtype)
     #ndarray* PyArray_ConvertToCommonType (object, int *)
     dtype PyArray_DescrFromScalar (object)
@@ -587,34 +587,34 @@ cdef extern from "numpy/arrayobject.h":
     #object PyArray_FromIter (object, dtype, npy_intp)
     object PyArray_Return (ndarray)
     #object PyArray_GetField (ndarray, dtype, int)
-    #int PyArray_SetField (ndarray, dtype, int, object)
+    #int PyArray_SetField (ndarray, dtype, int, object) except -1
     object PyArray_Byteswap (ndarray, npy_bool)
     object PyArray_Resize (ndarray, PyArray_Dims *, int, NPY_ORDER)
-    int PyArray_MoveInto (ndarray, ndarray)
-    int PyArray_CopyInto (ndarray, ndarray)
-    int PyArray_CopyAnyInto (ndarray, ndarray)
-    int PyArray_CopyObject (ndarray, object)
+    int PyArray_MoveInto (ndarray, ndarray) except -1
+    int PyArray_CopyInto (ndarray, ndarray) except -1
+    int PyArray_CopyAnyInto (ndarray, ndarray) except -1
+    int PyArray_CopyObject (ndarray, object) except -1
     object PyArray_NewCopy (ndarray, NPY_ORDER)
     object PyArray_ToList (ndarray)
     object PyArray_ToString (ndarray, NPY_ORDER)
-    int PyArray_ToFile (ndarray, stdio.FILE *, char *, char *)
-    int PyArray_Dump (object, object, int)
+    int PyArray_ToFile (ndarray, stdio.FILE *, char *, char *) except -1
+    int PyArray_Dump (object, object, int) except -1
     object PyArray_Dumps (object, int)
-    int PyArray_ValidType (int)
+    int PyArray_ValidType (int)  # Cannot error
     void PyArray_UpdateFlags (ndarray, int)
     object PyArray_New (type, int, npy_intp *, int, npy_intp *, void *, int, int, object)
     #object PyArray_NewFromDescr (type, dtype, int, npy_intp *, npy_intp *, void *, int, object)
     #dtype PyArray_DescrNew (dtype)
     dtype PyArray_DescrNewFromType (int)
-    double PyArray_GetPriority (object, double)
+    double PyArray_GetPriority (object, double)  # clears errors as of 1.25
     object PyArray_IterNew (object)
     object PyArray_MultiIterNew (int, ...)
 
-    int PyArray_PyIntAsInt (object)
+    int PyArray_PyIntAsInt (object) except? -1
     npy_intp PyArray_PyIntAsIntp (object)
-    int PyArray_Broadcast (broadcast)
+    int PyArray_Broadcast (broadcast) except -1
     void PyArray_FillObjectArray (ndarray, object)
-    int PyArray_FillWithScalar (ndarray, object)
+    int PyArray_FillWithScalar (ndarray, object) except -1
     npy_bool PyArray_CheckStrides (int, int, npy_intp, npy_intp, npy_intp *, npy_intp *)
     dtype PyArray_DescrNewByteorder (dtype, char)
     object PyArray_IterAllButAxis (object, int *)
@@ -628,7 +628,7 @@ cdef extern from "numpy/arrayobject.h":
     object PyArray_NewFlagsObject (object)
     npy_bool PyArray_CanCastScalar (type, type)
     #int PyArray_CompareUCS4 (npy_ucs4 *, npy_ucs4 *, register size_t)
-    int PyArray_RemoveSmallest (broadcast)
+    int PyArray_RemoveSmallest (broadcast) except -1
     int PyArray_ElementStrides (object)
     void PyArray_Item_INCREF (char *, dtype)
     void PyArray_Item_XDECREF (char *, dtype)
@@ -639,7 +639,7 @@ cdef extern from "numpy/arrayobject.h":
     object PyArray_PutMask (ndarray, object, object)
     object PyArray_Repeat (ndarray, object, int)
     object PyArray_Choose (ndarray, object, ndarray, NPY_CLIPMODE)
-    int PyArray_Sort (ndarray, int, NPY_SORTKIND)
+    int PyArray_Sort (ndarray, int, NPY_SORTKIND) except -1
     object PyArray_ArgSort (ndarray, int, NPY_SORTKIND)
     object PyArray_SearchSorted (ndarray, object, NPY_SEARCHSIDE, PyObject *)
     object PyArray_ArgMax (ndarray, int, ndarray)
@@ -677,49 +677,49 @@ cdef extern from "numpy/arrayobject.h":
     #int PyArray_As2D (object*, char ***, int *, int *, int)
     int PyArray_Free (object, void *)
     #int PyArray_Converter (object, object*)
-    int PyArray_IntpFromSequence (object, npy_intp *, int)
+    int PyArray_IntpFromSequence (object, npy_intp *, int) except -1
     object PyArray_Concatenate (object, int)
     object PyArray_InnerProduct (object, object)
     object PyArray_MatrixProduct (object, object)
     object PyArray_CopyAndTranspose (object)
     object PyArray_Correlate (object, object, int)
     int PyArray_TypestrConvert (int, int)
-    #int PyArray_DescrConverter (object, dtype*)
-    #int PyArray_DescrConverter2 (object, dtype*)
-    int PyArray_IntpConverter (object, PyArray_Dims *)
-    #int PyArray_BufferConverter (object, chunk)
-    int PyArray_AxisConverter (object, int *)
-    int PyArray_BoolConverter (object, npy_bool *)
-    int PyArray_ByteorderConverter (object, char *)
-    int PyArray_OrderConverter (object, NPY_ORDER *)
-    unsigned char PyArray_EquivTypes (dtype, dtype)
+    #int PyArray_DescrConverter (object, dtype*) except 0
+    #int PyArray_DescrConverter2 (object, dtype*) except 0
+    int PyArray_IntpConverter (object, PyArray_Dims *) except 0
+    #int PyArray_BufferConverter (object, chunk) except 0
+    int PyArray_AxisConverter (object, int *) except 0
+    int PyArray_BoolConverter (object, npy_bool *) except 0
+    int PyArray_ByteorderConverter (object, char *) except 0
+    int PyArray_OrderConverter (object, NPY_ORDER *) except 0
+    unsigned char PyArray_EquivTypes (dtype, dtype)  # clears errors
     #object PyArray_Zeros (int, npy_intp *, dtype, int)
     #object PyArray_Empty (int, npy_intp *, dtype, int)
     object PyArray_Where (object, object, object)
     object PyArray_Arange (double, double, double, int)
     #object PyArray_ArangeObj (object, object, object, dtype)
-    int PyArray_SortkindConverter (object, NPY_SORTKIND *)
+    int PyArray_SortkindConverter (object, NPY_SORTKIND *) except 0
     object PyArray_LexSort (object, int)
     object PyArray_Round (ndarray, int, ndarray)
     unsigned char PyArray_EquivTypenums (int, int)
-    int PyArray_RegisterDataType (dtype)
-    int PyArray_RegisterCastFunc (dtype, int, PyArray_VectorUnaryFunc *)
-    int PyArray_RegisterCanCast (dtype, int, NPY_SCALARKIND)
+    int PyArray_RegisterDataType (dtype) except -1
+    int PyArray_RegisterCastFunc (dtype, int, PyArray_VectorUnaryFunc *) except -1
+    int PyArray_RegisterCanCast (dtype, int, NPY_SCALARKIND) except -1
     #void PyArray_InitArrFuncs (PyArray_ArrFuncs *)
     object PyArray_IntTupleFromIntp (int, npy_intp *)
     int PyArray_TypeNumFromName (char *)
-    int PyArray_ClipmodeConverter (object, NPY_CLIPMODE *)
-    #int PyArray_OutputConverter (object, ndarray*)
+    int PyArray_ClipmodeConverter (object, NPY_CLIPMODE *) except 0
+    #int PyArray_OutputConverter (object, ndarray*) except 0
     object PyArray_BroadcastToShape (object, npy_intp *, int)
     void _PyArray_SigintHandler (int)
     void* _PyArray_GetSigintBuf ()
-    #int PyArray_DescrAlignConverter (object, dtype*)
-    #int PyArray_DescrAlignConverter2 (object, dtype*)
-    int PyArray_SearchsideConverter (object, void *)
+    #int PyArray_DescrAlignConverter (object, dtype*) except 0
+    #int PyArray_DescrAlignConverter2 (object, dtype*) except 0
+    int PyArray_SearchsideConverter (object, void *) except 0
     object PyArray_CheckAxis (ndarray, int *, int)
     npy_intp PyArray_OverflowMultiplyList (npy_intp *, int)
     int PyArray_CompareString (char *, char *, size_t)
-    int PyArray_SetBaseObject(ndarray, base)  # NOTE: steals a reference to base! Use "set_array_base()" instead.
+    int PyArray_SetBaseObject(ndarray, base) except -1 # NOTE: steals a reference to base! Use "set_array_base()" instead.
 
 
 # Typedefs that matches the runtime dtype objects in
@@ -906,7 +906,7 @@ cdef extern from "numpy/ufuncobject.h":
     object PyUFunc_FromFuncAndData(PyUFuncGenericFunction *,
           void **, char *, int, int, int, int, char *, char *, int)
     int PyUFunc_RegisterLoopForType(ufunc, int,
-                                    PyUFuncGenericFunction, int *, void *)
+                                    PyUFuncGenericFunction, int *, void *) except -1
     void PyUFunc_f_f_As_d_d \
          (char **, npy_intp *, npy_intp *, void *)
     void PyUFunc_d_d \
@@ -956,7 +956,7 @@ cdef extern from "numpy/ufuncobject.h":
     void PyUFunc_clearfperr()
     int PyUFunc_getfperr()
     int PyUFunc_handlefperr \
-        (int, PyObject *, int, int *)
+        (int, PyObject *, int, int *) except -1
     int PyUFunc_ReplaceLoopBySignature \
         (ufunc, PyUFuncGenericFunction, int *, PyUFuncGenericFunction *)
     object PyUFunc_FromFuncAndDataAndSignature \
diff --git a/numpy/__init__.pxd b/numpy/__init__.pxd
index 5ecdf3d0f06f..28d8154c17ef 100644
--- a/numpy/__init__.pxd
+++ b/numpy/__init__.pxd
@@ -359,7 +359,7 @@ cdef extern from "numpy/arrayobject.h":
     int PyArray_TYPE(ndarray arr) nogil
 
     object PyArray_GETITEM(ndarray arr, void *itemptr)
-    int PyArray_SETITEM(ndarray arr, void *itemptr, object obj)
+    int PyArray_SETITEM(ndarray arr, void *itemptr, object obj) except -1
 
     bint PyTypeNum_ISBOOL(int) nogil
     bint PyTypeNum_ISUNSIGNED(int) nogil
@@ -507,7 +507,7 @@ cdef extern from "numpy/arrayobject.h":
     # Functions taking dtype and returning object/ndarray are disabled
     # for now as they steal dtype references. I'm conservative and disable
     # more than is probably needed until it can be checked further.
-    int PyArray_SetNumericOps        (object)
+    int PyArray_SetNumericOps (object) except -1
     object PyArray_GetNumericOps ()
     int PyArray_INCREF (ndarray)
     int PyArray_XDECREF (ndarray)
@@ -517,11 +517,11 @@ cdef extern from "numpy/arrayobject.h":
     char * PyArray_Zero (ndarray)
     char * PyArray_One (ndarray)
     #object PyArray_CastToType (ndarray, dtype, int)
-    int PyArray_CastTo (ndarray, ndarray)
-    int PyArray_CastAnyTo (ndarray, ndarray)
-    int PyArray_CanCastSafely (int, int)
-    npy_bool PyArray_CanCastTo (dtype, dtype)
-    int PyArray_ObjectType (object, int)
+    int PyArray_CastTo (ndarray, ndarray) except -1
+    int PyArray_CastAnyTo (ndarray, ndarray) except -1
+    int PyArray_CanCastSafely (int, int)  # writes errors
+    npy_bool PyArray_CanCastTo (dtype, dtype)  # writes errors
+    int PyArray_ObjectType (object, int) except 0
     dtype PyArray_DescrFromObject (object, dtype)
     #ndarray* PyArray_ConvertToCommonType (object, int *)
     dtype PyArray_DescrFromScalar (object)
@@ -545,34 +545,34 @@ cdef extern from "numpy/arrayobject.h":
     #object PyArray_FromIter (object, dtype, npy_intp)
     object PyArray_Return (ndarray)
     #object PyArray_GetField (ndarray, dtype, int)
-    #int PyArray_SetField (ndarray, dtype, int, object)
+    #int PyArray_SetField (ndarray, dtype, int, object) except -1
     object PyArray_Byteswap (ndarray, npy_bool)
     object PyArray_Resize (ndarray, PyArray_Dims *, int, NPY_ORDER)
-    int PyArray_MoveInto (ndarray, ndarray)
-    int PyArray_CopyInto (ndarray, ndarray)
-    int PyArray_CopyAnyInto (ndarray, ndarray)
-    int PyArray_CopyObject (ndarray, object)
+    int PyArray_MoveInto (ndarray, ndarray) except -1
+    int PyArray_CopyInto (ndarray, ndarray) except -1
+    int PyArray_CopyAnyInto (ndarray, ndarray) except -1
+    int PyArray_CopyObject (ndarray, object) except -1
     object PyArray_NewCopy (ndarray, NPY_ORDER)
     object PyArray_ToList (ndarray)
     object PyArray_ToString (ndarray, NPY_ORDER)
-    int PyArray_ToFile (ndarray, stdio.FILE *, char *, char *)
-    int PyArray_Dump (object, object, int)
+    int PyArray_ToFile (ndarray, stdio.FILE *, char *, char *) except -1
+    int PyArray_Dump (object, object, int) except -1
     object PyArray_Dumps (object, int)
-    int PyArray_ValidType (int)
+    int PyArray_ValidType (int)  # Cannot error
     void PyArray_UpdateFlags (ndarray, int)
     object PyArray_New (type, int, npy_intp *, int, npy_intp *, void *, int, int, object)
     #object PyArray_NewFromDescr (type, dtype, int, npy_intp *, npy_intp *, void *, int, object)
     #dtype PyArray_DescrNew (dtype)
     dtype PyArray_DescrNewFromType (int)
-    double PyArray_GetPriority (object, double)
+    double PyArray_GetPriority (object, double)  # clears errors as of 1.25
     object PyArray_IterNew (object)
     object PyArray_MultiIterNew (int, ...)
 
-    int PyArray_PyIntAsInt (object)
+    int PyArray_PyIntAsInt (object) except? -1
     npy_intp PyArray_PyIntAsIntp (object)
-    int PyArray_Broadcast (broadcast)
+    int PyArray_Broadcast (broadcast) except -1
     void PyArray_FillObjectArray (ndarray, object)
-    int PyArray_FillWithScalar (ndarray, object)
+    int PyArray_FillWithScalar (ndarray, object) except -1
     npy_bool PyArray_CheckStrides (int, int, npy_intp, npy_intp, npy_intp *, npy_intp *)
     dtype PyArray_DescrNewByteorder (dtype, char)
     object PyArray_IterAllButAxis (object, int *)
@@ -586,7 +586,7 @@ cdef extern from "numpy/arrayobject.h":
     object PyArray_NewFlagsObject (object)
     npy_bool PyArray_CanCastScalar (type, type)
     #int PyArray_CompareUCS4 (npy_ucs4 *, npy_ucs4 *, register size_t)
-    int PyArray_RemoveSmallest (broadcast)
+    int PyArray_RemoveSmallest (broadcast) except -1
     int PyArray_ElementStrides (object)
     void PyArray_Item_INCREF (char *, dtype)
     void PyArray_Item_XDECREF (char *, dtype)
@@ -597,7 +597,7 @@ cdef extern from "numpy/arrayobject.h":
     object PyArray_PutMask (ndarray, object, object)
     object PyArray_Repeat (ndarray, object, int)
     object PyArray_Choose (ndarray, object, ndarray, NPY_CLIPMODE)
-    int PyArray_Sort (ndarray, int, NPY_SORTKIND)
+    int PyArray_Sort (ndarray, int, NPY_SORTKIND) except -1
     object PyArray_ArgSort (ndarray, int, NPY_SORTKIND)
     object PyArray_SearchSorted (ndarray, object, NPY_SEARCHSIDE, PyObject *)
     object PyArray_ArgMax (ndarray, int, ndarray)
@@ -635,49 +635,49 @@ cdef extern from "numpy/arrayobject.h":
     #int PyArray_As2D (object*, char ***, int *, int *, int)
     int PyArray_Free (object, void *)
     #int PyArray_Converter (object, object*)
-    int PyArray_IntpFromSequence (object, npy_intp *, int)
+    int PyArray_IntpFromSequence (object, npy_intp *, int) except -1
     object PyArray_Concatenate (object, int)
     object PyArray_InnerProduct (object, object)
     object PyArray_MatrixProduct (object, object)
     object PyArray_CopyAndTranspose (object)
     object PyArray_Correlate (object, object, int)
     int PyArray_TypestrConvert (int, int)
-    #int PyArray_DescrConverter (object, dtype*)
-    #int PyArray_DescrConverter2 (object, dtype*)
-    int PyArray_IntpConverter (object, PyArray_Dims *)
-    #int PyArray_BufferConverter (object, chunk)
-    int PyArray_AxisConverter (object, int *)
-    int PyArray_BoolConverter (object, npy_bool *)
-    int PyArray_ByteorderConverter (object, char *)
-    int PyArray_OrderConverter (object, NPY_ORDER *)
-    unsigned char PyArray_EquivTypes (dtype, dtype)
+    #int PyArray_DescrConverter (object, dtype*) except 0
+    #int PyArray_DescrConverter2 (object, dtype*) except 0
+    int PyArray_IntpConverter (object, PyArray_Dims *) except 0
+    #int PyArray_BufferConverter (object, chunk) except 0
+    int PyArray_AxisConverter (object, int *) except 0
+    int PyArray_BoolConverter (object, npy_bool *) except 0
+    int PyArray_ByteorderConverter (object, char *) except 0
+    int PyArray_OrderConverter (object, NPY_ORDER *) except 0
+    unsigned char PyArray_EquivTypes (dtype, dtype)  # clears errors
     #object PyArray_Zeros (int, npy_intp *, dtype, int)
     #object PyArray_Empty (int, npy_intp *, dtype, int)
     object PyArray_Where (object, object, object)
     object PyArray_Arange (double, double, double, int)
     #object PyArray_ArangeObj (object, object, object, dtype)
-    int PyArray_SortkindConverter (object, NPY_SORTKIND *)
+    int PyArray_SortkindConverter (object, NPY_SORTKIND *) except 0
     object PyArray_LexSort (object, int)
     object PyArray_Round (ndarray, int, ndarray)
     unsigned char PyArray_EquivTypenums (int, int)
-    int PyArray_RegisterDataType (dtype)
-    int PyArray_RegisterCastFunc (dtype, int, PyArray_VectorUnaryFunc *)
-    int PyArray_RegisterCanCast (dtype, int, NPY_SCALARKIND)
+    int PyArray_RegisterDataType (dtype) except -1
+    int PyArray_RegisterCastFunc (dtype, int, PyArray_VectorUnaryFunc *) except -1
+    int PyArray_RegisterCanCast (dtype, int, NPY_SCALARKIND) except -1
     #void PyArray_InitArrFuncs (PyArray_ArrFuncs *)
     object PyArray_IntTupleFromIntp (int, npy_intp *)
     int PyArray_TypeNumFromName (char *)
-    int PyArray_ClipmodeConverter (object, NPY_CLIPMODE *)
-    #int PyArray_OutputConverter (object, ndarray*)
+    int PyArray_ClipmodeConverter (object, NPY_CLIPMODE *) except 0
+    #int PyArray_OutputConverter (object, ndarray*) except 0
     object PyArray_BroadcastToShape (object, npy_intp *, int)
     void _PyArray_SigintHandler (int)
     void* _PyArray_GetSigintBuf ()
-    #int PyArray_DescrAlignConverter (object, dtype*)
-    #int PyArray_DescrAlignConverter2 (object, dtype*)
-    int PyArray_SearchsideConverter (object, void *)
+    #int PyArray_DescrAlignConverter (object, dtype*) except 0
+    #int PyArray_DescrAlignConverter2 (object, dtype*) except 0
+    int PyArray_SearchsideConverter (object, void *) except 0
     object PyArray_CheckAxis (ndarray, int *, int)
     npy_intp PyArray_OverflowMultiplyList (npy_intp *, int)
     int PyArray_CompareString (char *, char *, size_t)
-    int PyArray_SetBaseObject(ndarray, base)  # NOTE: steals a reference to base! Use "set_array_base()" instead.
+    int PyArray_SetBaseObject(ndarray, base) except -1 # NOTE: steals a reference to base! Use "set_array_base()" instead.
 
 
 # Typedefs that matches the runtime dtype objects in
@@ -864,7 +864,7 @@ cdef extern from "numpy/ufuncobject.h":
     object PyUFunc_FromFuncAndData(PyUFuncGenericFunction *,
           void **, char *, int, int, int, int, char *, char *, int)
     int PyUFunc_RegisterLoopForType(ufunc, int,
-                                    PyUFuncGenericFunction, int *, void *)
+                                    PyUFuncGenericFunction, int *, void *) except -1
     void PyUFunc_f_f_As_d_d \
          (char **, npy_intp *, npy_intp *, void *)
     void PyUFunc_d_d \
@@ -914,7 +914,7 @@ cdef extern from "numpy/ufuncobject.h":
     void PyUFunc_clearfperr()
     int PyUFunc_getfperr()
     int PyUFunc_handlefperr \
-        (int, PyObject *, int, int *)
+        (int, PyObject *, int, int *) except -1
     int PyUFunc_ReplaceLoopBySignature \
         (ufunc, PyUFuncGenericFunction, int *, PyUFuncGenericFunction *)
     object PyUFunc_FromFuncAndDataAndSignature \


Code review comments:
diff_hunk: @@ -587,34 +587,34 @@ cdef extern from "numpy/arrayobject.h":
     #object PyArray_FromIter (object, dtype, npy_intp)
     object PyArray_Return (ndarray)
     #object PyArray_GetField (ndarray, dtype, int)
-    #int PyArray_SetField (ndarray, dtype, int, object)
+    #int PyArray_SetField (ndarray, dtype, int, object) except -1
     object PyArray_Byteswap (ndarray, npy_bool)
     object PyArray_Resize (ndarray, PyArray_Dims *, int, NPY_ORDER)
-    int PyArray_MoveInto (ndarray, ndarray)
-    int PyArray_CopyInto (ndarray, ndarray)
-    int PyArray_CopyAnyInto (ndarray, ndarray)
-    int PyArray_CopyObject (ndarray, object)
+    int PyArray_MoveInto (ndarray, ndarray) except -1
+    int PyArray_CopyInto (ndarray, ndarray) except -1
+    int PyArray_CopyAnyInto (ndarray, ndarray) except -1
+    int PyArray_CopyObject (ndarray, object) except -1
     object PyArray_NewCopy (ndarray, NPY_ORDER)
     object PyArray_ToList (ndarray)
     object PyArray_ToString (ndarray, NPY_ORDER)
-    int PyArray_ToFile (ndarray, stdio.FILE *, char *, char *)
-    int PyArray_Dump (object, object, int)
+    int PyArray_ToFile (ndarray, stdio.FILE *, char *, char *) except -1
+    int PyArray_Dump (object, object, int) except -1
     object PyArray_Dumps (object, int)
-    int PyArray_ValidType (int)
+    int PyArray_ValidType (int)  # Cannot error
     void PyArray_UpdateFlags (ndarray, int)
     object PyArray_New (type, int, npy_intp *, int, npy_intp *, void *, int, int, object)
     #object PyArray_NewFromDescr (type, dtype, int, npy_intp *, npy_intp *, void *, int, object)
     #dtype PyArray_DescrNew (dtype)
     dtype PyArray_DescrNewFromType (int)
-    double PyArray_GetPriority (object, double)
+    double PyArray_GetPriority (object, double)  # clears errors as of 1.25
     object PyArray_IterNew (object)
     object PyArray_MultiIterNew (int, ...)
 
-    int PyArray_PyIntAsInt (object)
+    int PyArray_PyIntAsInt (object) except? -1
     npy_intp PyArray_PyIntAsIntp (object)
-    int PyArray_Broadcast (broadcast)
+    int PyArray_Broadcast (broadcast) except -1
     void PyArray_FillObjectArray (ndarray, object)
path: numpy/__init__.cython-30.pxd
text: @user1:
Should this one (and probably a handful of other void functions) be `except *`? I assume this doesn't swallow errors.

@author:
Arg, I didn't scan the `void` ones carefully, I guess.  This isn't designed to error, but clearly can...  (one of those bad functions).  (the xincref/decref family is also weird, I think they tend to clear, but probably not always... and they shouldn't be used anway, especially in cython)