Repository: numpy/numpy
File: numpy/_core/src/multiarray/unique.cpp (C++)

Code before:
    if (!PyArg_ParseTuple(args, "O&", PyArray_Converter, &self))
        return NULL;
    
    // Making sure the DECREF is called when the function returns, with
    // or w/o an exception
    auto self_decref = finally([&]() { Py_XDECREF(self); });


    try {
        /* Handle zero-sized arrays specially */
        if (PyArray_SIZE(self) == 0) {
            return PyArray_NewLikeArray(
                self,
                NPY_ANYORDER,
                NULL, // descr (use prototype's descr)
                1 // subok
            );
        }

        auto type = PyArray_TYPE(self);
        // we only support data types present in our unique_funcs map
        if (unique_funcs.find(type) == unique_funcs.end()) {
            // Grab GIL before raising an exception
            NPY_ALLOW_C_API;
            PyErr_SetString(PyExc_NotImplementedError, "Unsupported dtype");
            NPY_DISABLE_C_API;
            return NULL;
        }

        res = unique_funcs[type](self);
        return res;
    }
    catch (const std::bad_alloc &e) {
        // Grab GIL before raising an exception
        NPY_ALLOW_C_API;
        PyErr_NoMemory();
        NPY_DISABLE_C_API;
        return NULL;
    }
    catch (const std::exception &e) {
        // Grab GIL before raising an exception
        NPY_ALLOW_C_API;
        PyErr_SetString(PyExc_RuntimeError, e.what());
        NPY_DISABLE_C_API;
        return NULL;
    }
}


Review comment (suggestion):
Here, we always have the GIL.  You'll need to add a `finally` function/destructor to the caller where you release the GIL (similar to the NpyIter cleanup, or including in that).

I realize that this comes from the ufunc code which is in a different situation here.  I am not sure that you are already releasing the GIL, but if you are not, you could do so before the `do {} while ()` loop and grab it again after).

Just a brief comment because I happened to look at it.  Will look at it more later (ping me in a while if I forgot).

Code after:


/**
 * Python exposed implementation of `_unique_hash`.
 *
 * This is a C only function wrapping code that may cause C++ exceptions into
 * try/catch.
 *
 * @param arr NumPy array to find the unique values of.
 * @return Base-class NumPy array with unique values, `NotImplemented` if the
 * type is unsupported or `NULL` with an error set.
 */
extern "C" NPY_NO_EXPORT PyObject *
array__unique_hash(PyObject *NPY_UNUSED(module), PyObject *arr_obj)
{
    if (!PyArray_Check(arr_obj)) {
        PyErr_SetString(PyExc_TypeError,
                "_unique_hash() requires a NumPy array input.");
        return NULL;
    }
    PyArrayObject *arr = (PyArrayObject *)arr_obj;

    try {
        auto type = PyArray_TYPE(arr);
        // we only support data types present in our unique_funcs map
        if (unique_funcs.find(type) == unique_funcs.end()) {
            Py_RETURN_NOTIMPLEMENTED;
        }

        return unique_funcs[type](arr);
    }
    catch (const std::bad_alloc &e) {
        PyErr_NoMemory();
        return NULL;
    }
    catch (const std::exception &e) {
        PyErr_SetString(PyExc_RuntimeError, e.what());
        return NULL;
    }
}