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

Code before:
    npy_bool is_sequence = (*cache)->sequence;
    /*
      If it is a sequence, this object is the argument to PySequence_Fast, e.g.
      the iterable that the user wants to coerce into an array
    */
    PyObject *orig_seq = (*cache)->converted_obj;
    /* Owned reference to an item in the sequence */
    PyObject *item_pyvalue = NULL;
    int depth = (*cache)->depth;
    *cache = npy_unlink_coercion_cache(*cache);

    /* The element is either a sequence or an array */
    if (!is_sequence) {
        /* Straight forward array assignment */
        assert(PyArray_Check(obj));
        if (PyArray_CopyInto(self, (PyArrayObject *)obj) < 0) {
            goto finish;
        }
    }
    else {
        assert(depth != ndim);
        npy_intp orig_length = PyArray_DIMS(self)[0];
        for (npy_intp i = 0; i < orig_length; i++) {
            int err = 0;
            // this macro takes *the argument* of PySequence_Fast, which is orig_seq;
            // not the object returned by PySequence_Fast, which is obj
            NPY_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(orig_seq);
            npy_intp length = PySequence_Fast_GET_SIZE(obj);
            if (length != orig_length) {
                PyErr_SetString(PyExc_RuntimeError,
                                "Inconsistent object during array creation? "
                                "Content of sequences changed (length inconsistent).");
                err = 1;
            }
            else {
                Py_XSETREF(item_pyvalue, Py_NewRef(PySequence_Fast_GET_ITEM(obj, i)));
            }
            NPY_END_CRITICAL_SECTION_SEQUENCE_FAST();

            if (err) {
                goto finish;
            }

            if (ndim == depth + 1) {
                /*
                 * Straight forward assignment of elements.  Note that it is
                 * possible for such an element to be a 0-D array or array-like.
                 * `PyArray_Pack` supports arrays as well as we want: We
                 * support exact NumPy arrays, but at this point ignore others.
                 * (Please see the `PyArray_Pack` function comment if this
                 * rightly confuses you.)

Review comment (suggestion):
You likely did, but can you explain why not `obj`?  If `PySequence_Fast` did convert to tuple, we'll have our own object anyway?
(Ideally in the comment, I guess.)

Also, can't we assume that a fast-sequence will not change its size if we hold a critical section on it?  (But then, I guess critical sections are not fool-proof, so maybe that is a silly thought.)

Code after:
    npy_bool is_sequence = (*cache)->sequence;
    /*
      If it is a sequence, this object is the argument to PySequence_Fast, e.g.
      the iterable that the user wants to coerce into an array
    */
    PyObject *orig_seq = (*cache)->converted_obj;
    /* Owned reference to an item in the sequence */
    PyObject *item_pyvalue = NULL;
    int depth = (*cache)->depth;
    *cache = npy_unlink_coercion_cache(*cache);

    /* The element is either a sequence or an array */
    if (!is_sequence) {
        /* Straight forward array assignment */
        assert(PyArray_Check(obj));
        if (PyArray_CopyInto(self, (PyArrayObject *)obj) < 0) {
            goto finish;
        }
    }
    else {
        assert(depth != ndim);
        npy_intp orig_length = PyArray_DIMS(self)[0];
        int err = 1;
        NPY_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(orig_seq);
        for (npy_intp i = 0; i < orig_length; i++) {
            // this macro takes *the argument* of PySequence_Fast, which is orig_seq;
            // not the object returned by PySequence_Fast, which is a proxy object
            // with its own per-object PyMutex lock.
            // We want to lock the list object exposed to users, not the proxy.
            npy_intp length = PySequence_Fast_GET_SIZE(obj);
            if (length != orig_length) {
                PyErr_SetString(PyExc_RuntimeError,
                                "Inconsistent object during array creation? "
                                "Content of sequences changed (length inconsistent).");
                goto finish_critical_section;
            }
            else {
                Py_XSETREF(item_pyvalue, Py_NewRef(PySequence_Fast_GET_ITEM(obj, i)));
            }

            if (ndim == depth + 1) {
                /*
                 * Straight forward assignment of elements.  Note that it is
                 * possible for such an element to be a 0-D array or array-like.
                 * `PyArray_Pack` supports arrays as well as we want: We
                 * support exact NumPy arrays, but at this point ignore others.
                 * (Please see the `PyArray_Pack` function comment if this
                 * rightly confuses you.)
                 */
                char *item;
                item = (PyArray_BYTES(self) + i * PyArray_STRIDES(self)[0]);