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

Code before:

    NPY_PREPARE_ARGPARSER;
    if (npy_parse_arguments("count_nonzero", args, len_args, NULL,
            "", PyArray_Converter, &array,
            NULL, NULL, NULL) < 0) {
        return NULL;
    }

    count = PyArray_CountNonzero(array);
    Py_DECREF(array);

    if (count == -1) {
        return NULL;
    }

    PyArray_Descr *descr = PyArray_DescrFromType(NPY_INTP);
    if (descr == NULL) {
        return NULL;
    }
    return PyArray_Scalar(&count, descr, NULL);
}

static PyObject *
array_fromstring(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *keywds)
{
    char *data;
    Py_ssize_t nin = -1;
    char *sep = NULL;
    Py_ssize_t s;
    static char *kwlist[] = {"string", "dtype", "count", "sep", "like", NULL};
    PyObject *like = Py_None;
    PyArray_Descr *descr = NULL;

    if (!PyArg_ParseTupleAndKeywords(args, keywds,
                "s#|O&" NPY_SSIZE_T_PYFMT "s$O:fromstring", kwlist,
                &data, &s, PyArray_DescrConverter, &descr, &nin, &sep, &like)) {
        Py_XDECREF(descr);
        return NULL;
    }
    if (like != Py_None) {
        PyObject *deferred = array_implement_c_array_function_creation(
                "fromstring", like, args, keywds, NULL, 0, NULL);
        if (deferred != Py_NotImplemented) {
            Py_XDECREF(descr);
            return deferred;
        }
    }

    /* binary mode, condition copied from PyArray_FromString */
    if (sep == NULL || strlen(sep) == 0) {
        PyErr_SetString(PyExc_ValueError,

Review comment (suggestion):
This is a more significant change than it may look, since it has serious impact on promotion for `count_nonzero` without an axis.
(I.e. code like `arr.sum() / count_nonzero(arr)` can behave differently.)

Maybe we can do it, but we should discuss it briefly/add a release note for visibility.
But I am tempted to fix it in the array api tests to say that it is completely fine to return an integer for `count_nonzero(arr, axis=None)`.

(I think an integer return is just better for NumPy users, the argument against it is only that we can also return arrays of course, for which there is no equivalent behavior obviously.)

Code after:

static PyObject *
array_count_nonzero(PyObject *NPY_UNUSED(self), PyObject *const *args, Py_ssize_t len_args)
{
    PyArrayObject *array;
    npy_intp count;

    NPY_PREPARE_ARGPARSER;
    if (npy_parse_arguments("count_nonzero", args, len_args, NULL,
            "", PyArray_Converter, &array,
            NULL, NULL, NULL) < 0) {
        return NULL;
    }

    count = PyArray_CountNonzero(array);
    Py_DECREF(array);

    if (count == -1) {
        return NULL;
    }

    PyArray_Descr *descr = PyArray_DescrFromType(NPY_INTP);
    if (descr == NULL) {
        return NULL;
    }
    return PyArray_Scalar(&count, descr, NULL);
}

static PyObject *
array_fromstring(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *keywds)
{
    char *data;
    Py_ssize_t nin = -1;
    char *sep = NULL;
    Py_ssize_t s;
    static char *kwlist[] = {"string", "dtype", "count", "sep", "like", NULL};
    PyObject *like = Py_None;
    PyArray_Descr *descr = NULL;

    if (!PyArg_ParseTupleAndKeywords(args, keywds,
                "s#|O&" NPY_SSIZE_T_PYFMT "s$O:fromstring", kwlist,
                &data, &s, PyArray_DescrConverter, &descr, &nin, &sep, &like)) {
        Py_XDECREF(descr);
        return NULL;
    }
    if (like != Py_None) {
        PyObject *deferred = array_implement_c_array_function_creation(
                "fromstring", like, args, keywds, NULL, 0, NULL);
        if (deferred != Py_NotImplemented) {
            Py_XDECREF(descr);
            return deferred;