diff --git a/numpy/core/src/multiarray/dtype_transfer.c b/numpy/core/src/multiarray/dtype_transfer.c index aa8cc84ffa6f..50db627eafd3 100644 --- a/numpy/core/src/multiarray/dtype_transfer.c +++ b/numpy/core/src/multiarray/dtype_transfer.c @@ -322,11 +322,11 @@ strided_to_strided_object_to_any( while (N > 0) { memcpy(&src_ref, src, sizeof(src_ref)); - if (PyArray_Pack(data->descr, dst, src_ref) < 0) { + if (PyArray_Pack(data->descr, dst, src_ref ? src_ref : Py_None) < 0) { return -1; } - if (data->move_references) { + if (data->move_references && src_ref != NULL) { Py_DECREF(src_ref); memset(src, 0, sizeof(src_ref)); } diff --git a/numpy/core/tests/test_casting_unittests.py b/numpy/core/tests/test_casting_unittests.py index 1c465fea1d1c..8398b3cad1f0 100644 --- a/numpy/core/tests/test_casting_unittests.py +++ b/numpy/core/tests/test_casting_unittests.py @@ -657,3 +657,20 @@ def test_void_and_structured_with_subarray(self, casting): expected = casting == "unsafe" assert np.can_cast("V4", dtype, casting=casting) == expected assert np.can_cast(dtype, "V4", casting=casting) == expected + + @pytest.mark.parametrize("dtype", np.typecodes["All"]) + def test_object_casts_NULL_None_equivalence(self, dtype): + # None to casts may succeed or fail, but a NULL'ed array must + # behave the same as one filled with None's. + arr_normal = np.array([None] * 5) + arr_NULLs = np.empty_like([None] * 5) + # If the check fails (maybe it should) the test would lose its purpose: + assert arr_NULLs.tobytes() == b"\x00" * arr_NULLs.nbytes + + try: + expected = arr_normal.astype(dtype) + except TypeError: + with pytest.raises(TypeError): + arr_NULLs.astype(dtype) + else: + assert_array_equal(expected, arr_NULLs.astype(dtype))