Repository: numpy/numpy
File: numpy/_core/src/multiarray/arrayobject.c (C)

Code before:
/* if unraisable, called from dealloc, which must not raise an error,
   so make best effort try to write to stderr and clear the error.
*/

static inline int
WARN_IN_DEALLOC(PyObject* warning, const char * msg, npy_bool unraisable) {
    int ret = PyErr_WarnEx(warning, msg, 1);
    if (ret == 0 || !unraisable) {
        return ret;
    }
    PyObject *s = PyUnicode_FromString("array_dealloc");
    PyErr_WriteUnraisable(s);  // OK if s = NULL
    Py_XDECREF(s);
    return 0;
}

/* array object functions */

/*
 * Much of the actual work for dealloc, split off for use in __setstate__
 * via dealloc_all_but_self function defined below.
 * If not unraisable, will return -1 on error, 0 on success.
 * If unraisable, always succeeds.
 */
static int
_dealloc_all_but_self(PyArrayObject *self, npy_bool unraisable)
{
    PyArrayObject_fields *fa = (PyArrayObject_fields *)self;
    int retval;

    if (fa->_buffer_info) {
        if (_buffer_info_free(fa->_buffer_info, (PyObject *)self) < 0) {
            if (!unraisable) {
                return -1;
            }
            PyErr_WriteUnraisable(NULL);
        }
        fa->_buffer_info = NULL;
    }

    if (fa->base) {
        if (PyArray_FLAGS(self) & NPY_ARRAY_WRITEBACKIFCOPY)
        {
            char const * msg = "WRITEBACKIFCOPY detected in array_dealloc. "
                " Required call to PyArray_ResolveWritebackIfCopy or "
                "PyArray_DiscardWritebackIfCopy is missing.";
            if (WARN_IN_DEALLOC(PyExc_RuntimeWarning, msg, unraisable) < 0) {
                return -1;  // Can only happen if !unraisable, so pass on.
            }
            retval = PyArray_ResolveWritebackIfCopy(self);
            if (retval < 0)

Review comment (suggestion):
I wonder if can find a nicer name "clear" might make sense. But we only use it twice, so doesn't matter much.

(`clear` is also a bit clearer on the fact that I think it now has to be setup to be OK to call twice due to error handling in `__setitem__`.)

Code after:
/*
 * During dealloc we cannot propagate errors so if unraisable is set
 * we simply print out the error message and convert the error into
 * success (returning 0).
 */
static inline int
write_and_clear_error_if_unraisable(int status, npy_bool unraisable)
{
    if (status < 0 && unraisable) {
        PyErr_WriteUnraisable(npy_interned_str.array_dealloc);
        return 0;
    }
    return status;
}

/* array object functions */

/*
 * Much of the actual work for dealloc, split off for use in __setstate__
 * via clear_array_attributes function defined below.
 * If not unraisable, will return -1 on error, 0 on success.
 * If unraisable, always succeeds, though may print errors and warnings.
 */
static int
_clear_array_attributes(PyArrayObject *self, npy_bool unraisable)
{
    PyArrayObject_fields *fa = (PyArrayObject_fields *)self;

    if (_buffer_info_free(fa->_buffer_info, (PyObject *)self) < 0) {
        if (write_and_clear_error_if_unraisable(-1, unraisable) < 0) {
            return -1;
        }
    }
    fa->_buffer_info = NULL;

    if (fa->base) {
        if (PyArray_FLAGS(self) & NPY_ARRAY_WRITEBACKIFCOPY)
        {
            char const * msg = "WRITEBACKIFCOPY detected in clearing of array. "
                " Required call to PyArray_ResolveWritebackIfCopy or "
                "PyArray_DiscardWritebackIfCopy is missing.";
            int retval = PyErr_WarnEx(PyExc_RuntimeWarning, msg, 1);
            if (write_and_clear_error_if_unraisable(retval, unraisable) < 0) {
                return -1;
            }
            /*
             * prevent reaching 0 twice and thus recursing into dealloc.
             * Increasing sys.gettotalrefcount, but path should not be taken.
             */
            Py_INCREF(self);
            retval = PyArray_ResolveWritebackIfCopy(self);