Repository: apache/arrow
Pull request #45743: GH-43587: [Python] Remove no longer used serialize/deserialize PyArrow C++ code
Author: login: AlenkaF
Labels: name: Component: Python
Created: 2025-03-11T10:51:12Z | Merged: 2025-03-24T14:53:10Z

### Rationale for this change
We deprecated serialization functionality in C++ code in https://github.com/apache/arrow/pull/44064. We can now remove it.

### What changes are included in this PR?
Serialization functionality is removed from PyArrow C++ code.

### Are these changes tested?
Tests should pass.

### Are there any user-facing changes?
Serialization functionality is removed from PyArrow C++ code.

* GitHub Issue: #43587


Diff:
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index ac497e844e74..d07edaa6ff64 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -356,7 +356,6 @@ set(PYARROW_CPP_SRCS
     ${PYARROW_CPP_SOURCE_DIR}/common.cc
     ${PYARROW_CPP_SOURCE_DIR}/datetime.cc
     ${PYARROW_CPP_SOURCE_DIR}/decimal.cc
-    ${PYARROW_CPP_SOURCE_DIR}/deserialize.cc
     ${PYARROW_CPP_SOURCE_DIR}/extension_type.cc
     ${PYARROW_CPP_SOURCE_DIR}/gdb.cc
     ${PYARROW_CPP_SOURCE_DIR}/helpers.cc
@@ -369,7 +368,6 @@ set(PYARROW_CPP_SRCS
     ${PYARROW_CPP_SOURCE_DIR}/python_test.cc
     ${PYARROW_CPP_SOURCE_DIR}/python_to_arrow.cc
     ${PYARROW_CPP_SOURCE_DIR}/pyarrow.cc
-    ${PYARROW_CPP_SOURCE_DIR}/serialize.cc
     ${PYARROW_CPP_SOURCE_DIR}/udf.cc)
 set_source_files_properties(${PYARROW_CPP_SOURCE_DIR}/numpy_init.cc
                             PROPERTIES SKIP_PRECOMPILE_HEADERS ON
diff --git a/python/pyarrow/includes/libarrow_python.pxd b/python/pyarrow/includes/libarrow_python.pxd
index 96725c9c3862..cd4474f8345d 100644
--- a/python/pyarrow/includes/libarrow_python.pxd
+++ b/python/pyarrow/includes/libarrow_python.pxd
@@ -199,41 +199,6 @@ cdef extern from "arrow/python/api.h" namespace "arrow::py" nogil:
         unordered_set[c_string] extension_columns
         c_bool to_numpy
 
-    cdef cppclass CSerializedPyObject" arrow::py::SerializedPyObject":
-        shared_ptr[CRecordBatch] batch
-        vector[shared_ptr[CTensor]] tensors
-
-        CStatus WriteTo(COutputStream* dst)
-        CStatus GetComponents(CMemoryPool* pool, PyObject** dst)
-
-    CStatus SerializeObject(object context, object sequence,
-                            CSerializedPyObject* out)
-
-    CStatus DeserializeObject(object context,
-                              const CSerializedPyObject& obj,
-                              PyObject* base, PyObject** out)
-
-    CStatus ReadSerializedObject(CRandomAccessFile* src,
-                                 CSerializedPyObject* out)
-
-    cdef cppclass SparseTensorCounts:
-        SparseTensorCounts()
-        int coo
-        int csr
-        int csc
-        int csf
-        int ndim_csf
-        int num_total_tensors() const
-        int num_total_buffers() const
-
-    CStatus GetSerializedFromComponents(
-        int num_tensors,
-        const SparseTensorCounts& num_sparse_tensors,
-        int num_ndarrays,
-        int num_buffers,
-        object buffers,
-        CSerializedPyObject* out)
-
 
 cdef extern from "arrow/python/api.h" namespace "arrow::py::internal" nogil:
     cdef cppclass CTimePoint "arrow::py::internal::TimePoint":
diff --git a/python/pyarrow/src/arrow/python/api.h b/python/pyarrow/src/arrow/python/api.h
index a0b13d6d1301..e66bf49dfecf 100644
--- a/python/pyarrow/src/arrow/python/api.h
+++ b/python/pyarrow/src/arrow/python/api.h
@@ -20,11 +20,9 @@
 #include "arrow/python/arrow_to_pandas.h"
 #include "arrow/python/common.h"
 #include "arrow/python/datetime.h"
-#include "arrow/python/deserialize.h"
 #include "arrow/python/helpers.h"
 #include "arrow/python/inference.h"
 #include "arrow/python/io.h"
 #include "arrow/python/numpy_convert.h"
 #include "arrow/python/numpy_to_arrow.h"
 #include "arrow/python/python_to_arrow.h"
-#include "arrow/python/serialize.h"
diff --git a/python/pyarrow/src/arrow/python/deserialize.cc b/python/pyarrow/src/arrow/python/deserialize.cc
deleted file mode 100644
index ab300a182fa0..000000000000
--- a/python/pyarrow/src/arrow/python/deserialize.cc
+++ /dev/null
@@ -1,503 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-#include "arrow/python/deserialize.h"
-
-#include "arrow/python/numpy_interop.h"
-
-#include <cstdint>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-
-#include "arrow/array.h"
-#include "arrow/io/interfaces.h"
-#include "arrow/io/memory.h"
-#include "arrow/ipc/options.h"
-#include "arrow/ipc/reader.h"
-#include "arrow/ipc/util.h"
-#include "arrow/ipc/writer.h"
-#include "arrow/table.h"
-#include "arrow/util/checked_cast.h"
-#include "arrow/util/logging.h"
-#include "arrow/util/value_parsing.h"
-
-#include "arrow/python/common.h"
-#include "arrow/python/datetime.h"
-#include "arrow/python/helpers.h"
-#include "arrow/python/numpy_convert.h"
-#include "arrow/python/pyarrow.h"
-#include "arrow/python/serialize.h"
-#include "arrow/python/vendored/pythoncapi_compat.h"
-
-namespace arrow {
-
-using internal::checked_cast;
-using internal::ParseValue;
-
-namespace py {
-
-Status CallDeserializeCallback(PyObject* context, PyObject* value,
-                               PyObject** deserialized_object);
-
-Status DeserializeTuple(PyObject* context, const Array& array, int64_t start_idx,
-                        int64_t stop_idx, PyObject* base, const SerializedPyObject& blobs,
-                        PyObject** out);
-
-Status DeserializeList(PyObject* context, const Array& array, int64_t start_idx,
-                       int64_t stop_idx, PyObject* base, const SerializedPyObject& blobs,
-                       PyObject** out);
-
-Status DeserializeSet(PyObject* context, const Array& array, int64_t start_idx,
-                      int64_t stop_idx, PyObject* base, const SerializedPyObject& blobs,
-                      PyObject** out);
-
-Status DeserializeDict(PyObject* context, const Array& array, int64_t start_idx,
-                       int64_t stop_idx, PyObject* base, const SerializedPyObject& blobs,
-                       PyObject** out) {
-  const auto& data = checked_cast<const StructArray&>(array);
-  OwnedRef keys, vals;
-  OwnedRef result(PyDict_New());
-  RETURN_IF_PYERROR();
-
-  DCHECK_EQ(2, data.num_fields());
-
-  RETURN_NOT_OK(DeserializeList(context, *data.field(0), start_idx, stop_idx, base, blobs,
-                                keys.ref()));
-  RETURN_NOT_OK(DeserializeList(context, *data.field(1), start_idx, stop_idx, base, blobs,
-                                vals.ref()));
-  for (int64_t i = start_idx; i < stop_idx; ++i) {
-    // PyDict_SetItem behaves differently from PyList_SetItem and PyTuple_SetItem.
-    // The latter two steal references whereas PyDict_SetItem does not. So we need
-    // to make sure the reference count is decremented by letting the OwnedRef
-    // go out of scope at the end.
-    PyObject* key = PyList_GetItemRef(keys.obj(), i - start_idx);
-    RETURN_IF_PYERROR();
-    OwnedRef keyref(key);
-    PyObject* val = PyList_GetItemRef(vals.obj(), i - start_idx);
-    RETURN_IF_PYERROR();
-    OwnedRef valref(val);
-    int ret = PyDict_SetItem(result.obj(), key, val);
-    if (ret != 0) {
-      return ConvertPyError();
-    }
-  }
-  static PyObject* py_type = PyUnicode_FromString("_pytype_");
-  if (PyDict_Contains(result.obj(), py_type)) {
-    RETURN_NOT_OK(CallDeserializeCallback(context, result.obj(), out));
-  } else {
-    *out = result.detach();
-  }
-  return Status::OK();
-}
-
-Status DeserializeArray(int32_t index, PyObject* base, const SerializedPyObject& blobs,
-                        PyObject** out) {
-  RETURN_NOT_OK(py::TensorToNdarray(blobs.ndarrays[index], base, out));
-  // Mark the array as immutable
-  OwnedRef flags(PyObject_GetAttrString(*out, "flags"));
-  if (flags.obj() == NULL) {
-    return ConvertPyError();
-  }
-  if (PyObject_SetAttrString(flags.obj(), "writeable", Py_False) < 0) {
-    return ConvertPyError();
-  }
-  return Status::OK();
-}
-
-Status GetValue(PyObject* context, const Array& arr, int64_t index, int8_t type,
-                PyObject* base, const SerializedPyObject& blobs, PyObject** result) {
-  switch (type) {
-    case PythonType::NONE:
-      Py_INCREF(Py_None);
-      *result = Py_None;
-      return Status::OK();
-    case PythonType::BOOL:
-      *result = PyBool_FromLong(checked_cast<const BooleanArray&>(arr).Value(index));
-      return Status::OK();
-    case PythonType::PY2INT:
-    case PythonType::INT: {
-      *result = PyLong_FromSsize_t(checked_cast<const Int64Array&>(arr).Value(index));
-      return Status::OK();
-    }
-    case PythonType::BYTES: {
-      auto view = checked_cast<const BinaryArray&>(arr).GetView(index);
-      *result = PyBytes_FromStringAndSize(view.data(), view.length());
-      return CheckPyError();
-    }
-    case PythonType::STRING: {
-      auto view = checked_cast<const StringArray&>(arr).GetView(index);
-      *result = PyUnicode_FromStringAndSize(view.data(), view.length());
-      return CheckPyError();
-    }
-    case PythonType::HALF_FLOAT: {
-      *result = PyHalf_FromHalf(checked_cast<const HalfFloatArray&>(arr).Value(index));
-      RETURN_IF_PYERROR();
-      return Status::OK();
-    }
-    case PythonType::FLOAT:
-      *result = PyFloat_FromDouble(checked_cast<const FloatArray&>(arr).Value(index));
-      return Status::OK();
-    case PythonType::DOUBLE:
-      *result = PyFloat_FromDouble(checked_cast<const DoubleArray&>(arr).Value(index));
-      return Status::OK();
-    case PythonType::DATE64: {
-      RETURN_NOT_OK(internal::PyDateTime_from_int(
-          checked_cast<const Date64Array&>(arr).Value(index), TimeUnit::MICRO, result));
-      RETURN_IF_PYERROR();
-      return Status::OK();
-    }
-    case PythonType::LIST: {
-      const auto& l = checked_cast<const ListArray&>(arr);
-      return DeserializeList(context, *l.values(), l.value_offset(index),
-                             l.value_offset(index + 1), base, blobs, result);
-    }
-    case PythonType::DICT: {
-      const auto& l = checked_cast<const ListArray&>(arr);
-      return DeserializeDict(context, *l.values(), l.value_offset(index),
-                             l.value_offset(index + 1), base, blobs, result);
-    }
-    case PythonType::TUPLE: {
-      const auto& l = checked_cast<const ListArray&>(arr);
-      return DeserializeTuple(context, *l.values(), l.value_offset(index),
-                              l.value_offset(index + 1), base, blobs, result);
-    }
-    case PythonType::SET: {
-      const auto& l = checked_cast<const ListArray&>(arr);
-      return DeserializeSet(context, *l.values(), l.value_offset(index),
-                            l.value_offset(index + 1), base, blobs, result);
-    }
-    case PythonType::TENSOR: {
-      int32_t ref = checked_cast<const Int32Array&>(arr).Value(index);
-      *result = wrap_tensor(blobs.tensors[ref]);
-      return Status::OK();
-    }
-    case PythonType::SPARSECOOTENSOR: {
-      int32_t ref = checked_cast<const Int32Array&>(arr).Value(index);
-      const std::shared_ptr<SparseCOOTensor>& sparse_coo_tensor =
-          arrow::internal::checked_pointer_cast<SparseCOOTensor>(
-              blobs.sparse_tensors[ref]);
-      *result = wrap_sparse_coo_tensor(sparse_coo_tensor);
-      return Status::OK();
-    }
-    case PythonType::SPARSECSRMATRIX: {
-      int32_t ref = checked_cast<const Int32Array&>(arr).Value(index);
-      const std::shared_ptr<SparseCSRMatrix>& sparse_csr_matrix =
-          arrow::internal::checked_pointer_cast<SparseCSRMatrix>(
-              blobs.sparse_tensors[ref]);
-      *result = wrap_sparse_csr_matrix(sparse_csr_matrix);
-      return Status::OK();
-    }
-    case PythonType::SPARSECSCMATRIX: {
-      int32_t ref = checked_cast<const Int32Array&>(arr).Value(index);
-      const std::shared_ptr<SparseCSCMatrix>& sparse_csc_matrix =
-          arrow::internal::checked_pointer_cast<SparseCSCMatrix>(
-              blobs.sparse_tensors[ref]);
-      *result = wrap_sparse_csc_matrix(sparse_csc_matrix);
-      return Status::OK();
-    }
-    case PythonType::SPARSECSFTENSOR: {
-      int32_t ref = checked_cast<const Int32Array&>(arr).Value(index);
-      const std::shared_ptr<SparseCSFTensor>& sparse_csf_tensor =
-          arrow::internal::checked_pointer_cast<SparseCSFTensor>(
-              blobs.sparse_tensors[ref]);
-      *result = wrap_sparse_csf_tensor(sparse_csf_tensor);
-      return Status::OK();
-    }
-    case PythonType::NDARRAY: {
-      int32_t ref = checked_cast<const Int32Array&>(arr).Value(index);
-      return DeserializeArray(ref, base, blobs, result);
-    }
-    case PythonType::BUFFER: {
-      int32_t ref = checked_cast<const Int32Array&>(arr).Value(index);
-      *result = wrap_buffer(blobs.buffers[ref]);
-      return Status::OK();
-    }
-    default: {
-      ARROW_CHECK(false) << "union tag " << type << "' not recognized";
-    }
-  }
-  return Status::OK();
-}
-
-Status GetPythonTypes(const UnionArray& data, std::vector<int8_t>* result) {
-  ARROW_CHECK(result != nullptr);
-  auto type = data.type();
-  for (int i = 0; i < type->num_fields(); ++i) {
-    int8_t tag = 0;
-    const std::string& data = type->field(i)->name();
-    if (!ParseValue<Int8Type>(data.c_str(), data.size(), &tag)) {
-      return Status::SerializationError("Cannot convert string: \"",
-                                        type->field(i)->name(), "\" to int8_t");
-    }
-    result->push_back(tag);
-  }
-  return Status::OK();
-}
-
-template <typename CreateSequenceFn, typename SetItemFn>
-Status DeserializeSequence(PyObject* context, const Array& array, int64_t start_idx,
-                           int64_t stop_idx, PyObject* base,
-                           const SerializedPyObject& blobs,
-                           CreateSequenceFn&& create_sequence, SetItemFn&& set_item,
-                           PyObject** out) {
-  const auto& data = checked_cast<const DenseUnionArray&>(array);
-  OwnedRef result(create_sequence(stop_idx - start_idx));
-  RETURN_IF_PYERROR();
-  const int8_t* type_codes = data.raw_type_codes();
-  const int32_t* value_offsets = data.raw_value_offsets();
-  std::vector<int8_t> python_types;
-  RETURN_NOT_OK(GetPythonTypes(data, &python_types));
-  for (int64_t i = start_idx; i < stop_idx; ++i) {
-    const int64_t offset = value_offsets[i];
-    const uint8_t type = type_codes[i];
-    PyObject* value;
-    RETURN_NOT_OK(GetValue(context, *data.field(type), offset, python_types[type], base,
-                           blobs, &value));
-    RETURN_NOT_OK(set_item(result.obj(), i - start_idx, value));
-  }
-  *out = result.detach();
-  return Status::OK();
-}
-
-Status DeserializeList(PyObject* context, const Array& array, int64_t start_idx,
-                       int64_t stop_idx, PyObject* base, const SerializedPyObject& blobs,
-                       PyObject** out) {
-  return DeserializeSequence(
-      context, array, start_idx, stop_idx, base, blobs,
-      [](int64_t size) { return PyList_New(size); },
-      [](PyObject* seq, int64_t index, PyObject* item) {
-        PyList_SET_ITEM(seq, index, item);
-        return Status::OK();
-      },
-      out);
-}
-
-Status DeserializeTuple(PyObject* context, const Array& array, int64_t start_idx,
-                        int64_t stop_idx, PyObject* base, const SerializedPyObject& blobs,
-                        PyObject** out) {
-  return DeserializeSequence(
-      context, array, start_idx, stop_idx, base, blobs,
-      [](int64_t size) { return PyTuple_New(size); },
-      [](PyObject* seq, int64_t index, PyObject* item) {
-        PyTuple_SET_ITEM(seq, index, item);
-        return Status::OK();
-      },
-      out);
-}
-
-Status DeserializeSet(PyObject* context, const Array& array, int64_t start_idx,
-                      int64_t stop_idx, PyObject* base, const SerializedPyObject& blobs,
-                      PyObject** out) {
-  return DeserializeSequence(
-      context, array, start_idx, stop_idx, base, blobs,
-      [](int64_t size) { return PySet_New(nullptr); },
-      [](PyObject* seq, int64_t index, PyObject* item) {
-        int err = PySet_Add(seq, item);
-        Py_DECREF(item);
-        if (err < 0) {
-          RETURN_IF_PYERROR();
-        }
-        return Status::OK();
-      },
-      out);
-}
-
-Status ReadSerializedObject(io::RandomAccessFile* src, SerializedPyObject* out) {
-  int32_t num_tensors;
-  int32_t num_sparse_tensors;
-  int32_t num_ndarrays;
-  int32_t num_buffers;
-
-  // Read number of tensors
-  RETURN_NOT_OK(src->Read(sizeof(int32_t), reinterpret_cast<uint8_t*>(&num_tensors)));
-  RETURN_NOT_OK(
-      src->Read(sizeof(int32_t), reinterpret_cast<uint8_t*>(&num_sparse_tensors)));
-  RETURN_NOT_OK(src->Read(sizeof(int32_t), reinterpret_cast<uint8_t*>(&num_ndarrays)));
-  RETURN_NOT_OK(src->Read(sizeof(int32_t), reinterpret_cast<uint8_t*>(&num_buffers)));
-
-  // Align stream to 8-byte offset
-  RETURN_NOT_OK(ipc::AlignStream(src, ipc::kArrowIpcAlignment));
-  std::shared_ptr<RecordBatchReader> reader;
-  ARROW_ASSIGN_OR_RAISE(reader, ipc::RecordBatchStreamReader::Open(src));
-  RETURN_NOT_OK(reader->ReadNext(&out->batch));
-
-  /// Skip EOS marker
-  RETURN_NOT_OK(src->Advance(4));
-
-  /// Align stream so tensor bodies are 64-byte aligned
-  RETURN_NOT_OK(ipc::AlignStream(src, ipc::kTensorAlignment));
-
-  for (int i = 0; i < num_tensors; ++i) {
-    std::shared_ptr<Tensor> tensor;
-    ARROW_ASSIGN_OR_RAISE(tensor, ipc::ReadTensor(src));
-    RETURN_NOT_OK(ipc::AlignStream(src, ipc::kTensorAlignment));
-    out->tensors.push_back(tensor);
-  }
-
-  for (int i = 0; i < num_sparse_tensors; ++i) {
-    std::shared_ptr<SparseTensor> sparse_tensor;
-    ARROW_ASSIGN_OR_RAISE(sparse_tensor, ipc::ReadSparseTensor(src));
-    RETURN_NOT_OK(ipc::AlignStream(src, ipc::kTensorAlignment));
-    out->sparse_tensors.push_back(sparse_tensor);
-  }
-
-  for (int i = 0; i < num_ndarrays; ++i) {
-    std::shared_ptr<Tensor> ndarray;
-    ARROW_ASSIGN_OR_RAISE(ndarray, ipc::ReadTensor(src));
-    RETURN_NOT_OK(ipc::AlignStream(src, ipc::kTensorAlignment));
-    out->ndarrays.push_back(ndarray);
-  }
-
-  ARROW_ASSIGN_OR_RAISE(int64_t offset, src->Tell());
-  for (int i = 0; i < num_buffers; ++i) {
-    int64_t size;
-    RETURN_NOT_OK(src->ReadAt(offset, sizeof(int64_t), &size));
-    offset += sizeof(int64_t);
-    ARROW_ASSIGN_OR_RAISE(auto buffer, src->ReadAt(offset, size));
-    out->buffers.push_back(buffer);
-    offset += size;
-  }
-
-  return Status::OK();
-}
-
-Status DeserializeObject(PyObject* context, const SerializedPyObject& obj, PyObject* base,
-                         PyObject** out) {
-  PyAcquireGIL lock;
-  return DeserializeList(context, *obj.batch->column(0), 0, obj.batch->num_rows(), base,
-                         obj, out);
-}
-
-Status GetSerializedFromComponents(int num_tensors,
-                                   const SparseTensorCounts& num_sparse_tensors,
-                                   int num_ndarrays, int num_buffers, PyObject* data,
-                                   SerializedPyObject* out) {
-  PyAcquireGIL gil;
-  const Py_ssize_t data_length = PyList_Size(data);
-  RETURN_IF_PYERROR();
-
-  const Py_ssize_t expected_data_length = 1 + num_tensors * 2 +
-                                          num_sparse_tensors.num_total_buffers() +
-                                          num_ndarrays * 2 + num_buffers;
-  if (data_length != expected_data_length) {
-    return Status::Invalid("Invalid number of buffers in data");
-  }
-
-  auto GetBuffer = [&data](Py_ssize_t index, std::shared_ptr<Buffer>* out) {
-    ARROW_CHECK_LE(index, PyList_Size(data));
-    PyObject* py_buf = PyList_GetItemRef(data, index);
-    RETURN_IF_PYERROR();
-    OwnedRef py_buf_ref(py_buf);
-    return unwrap_buffer(py_buf).Value(out);
-  };
-
-  Py_ssize_t buffer_index = 0;
-
-  // Read the union batch describing object structure
-  {
-    std::shared_ptr<Buffer> data_buffer;
-    RETURN_NOT_OK(GetBuffer(buffer_index++, &data_buffer));
-    gil.release();
-    io::BufferReader buf_reader(data_buffer);
-    std::shared_ptr<RecordBatchReader> reader;
-    ARROW_ASSIGN_OR_RAISE(reader, ipc::RecordBatchStreamReader::Open(&buf_reader));
-    RETURN_NOT_OK(reader->ReadNext(&out->batch));
-    gil.acquire();
-  }
-
-  // Zero-copy reconstruct tensors
-  for (int i = 0; i < num_tensors; ++i) {
-    std::shared_ptr<Buffer> metadata;
-    std::shared_ptr<Buffer> body;
-    std::shared_ptr<Tensor> tensor;
-    RETURN_NOT_OK(GetBuffer(buffer_index++, &metadata));
-    RETURN_NOT_OK(GetBuffer(buffer_index++, &body));
-
-    ipc::Message message(metadata, body);
-
-    ARROW_ASSIGN_OR_RAISE(tensor, ipc::ReadTensor(message));
-    out->tensors.emplace_back(std::move(tensor));
-  }
-
-  // Zero-copy reconstruct sparse tensors
-  for (int i = 0, n = num_sparse_tensors.num_total_tensors(); i < n; ++i) {
-    ipc::IpcPayload payload;
-    RETURN_NOT_OK(GetBuffer(buffer_index++, &payload.metadata));
-
-    ARROW_ASSIGN_OR_RAISE(
-        size_t num_bodies,
-        ipc::internal::ReadSparseTensorBodyBufferCount(*payload.metadata));
-
-    payload.body_buffers.reserve(num_bodies);
-    for (size_t i = 0; i < num_bodies; ++i) {
-      std::shared_ptr<Buffer> body;
-      RETURN_NOT_OK(GetBuffer(buffer_index++, &body));
-      payload.body_buffers.emplace_back(body);
-    }
-
-    std::shared_ptr<SparseTensor> sparse_tensor;
-    ARROW_ASSIGN_OR_RAISE(sparse_tensor, ipc::internal::ReadSparseTensorPayload(payload));
-    out->sparse_tensors.emplace_back(std::move(sparse_tensor));
-  }
-
-  // Zero-copy reconstruct tensors for numpy ndarrays
-  for (int i = 0; i < num_ndarrays; ++i) {
-    std::shared_ptr<Buffer> metadata;
-    std::shared_ptr<Buffer> body;
-    std::shared_ptr<Tensor> tensor;
-    RETURN_NOT_OK(GetBuffer(buffer_index++, &metadata));
-    RETURN_NOT_OK(GetBuffer(buffer_index++, &body));
-
-    ipc::Message message(metadata, body);
-
-    ARROW_ASSIGN_OR_RAISE(tensor, ipc::ReadTensor(message));
-    out->ndarrays.emplace_back(std::move(tensor));
-  }
-
-  // Unwrap and append buffers
-  for (int i = 0; i < num_buffers; ++i) {
-    std::shared_ptr<Buffer> buffer;
-    RETURN_NOT_OK(GetBuffer(buffer_index++, &buffer));
-    out->buffers.emplace_back(std::move(buffer));
-  }
-
-  return Status::OK();
-}
-
-Status DeserializeNdarray(const SerializedPyObject& object,
-                          std::shared_ptr<Tensor>* out) {
-  if (object.ndarrays.size() != 1) {
-    return Status::Invalid("Object is not an Ndarray");
-  }
-  *out = object.ndarrays[0];
-  return Status::OK();
-}
-
-Status NdarrayFromBuffer(std::shared_ptr<Buffer> src, std::shared_ptr<Tensor>* out) {
-  io::BufferReader reader(src);
-  SerializedPyObject object;
-  RETURN_NOT_OK(ReadSerializedObject(&reader, &object));
-  return DeserializeNdarray(object, out);
-}
-
-}  // namespace py
-}  // namespace arrow
diff --git a/python/pyarrow/src/arrow/python/deserialize.h b/python/pyarrow/src/arrow/python/deserialize.h
deleted file mode 100644
index fe1d73622a3d..000000000000
--- a/python/pyarrow/src/arrow/python/deserialize.h
+++ /dev/null
@@ -1,112 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-#pragma once
-
-#include <cstdint>
-#include <memory>
-#include <vector>
-
-#include "arrow/python/serialize.h"
-#include "arrow/python/visibility.h"
-#include "arrow/status.h"
-#include "arrow/util/macros.h"
-
-namespace arrow {
-
-class RecordBatch;
-class Tensor;
-
-namespace io {
-
-class RandomAccessFile;
-
-}  // namespace io
-
-namespace py {
-
-struct ARROW_PYTHON_EXPORT SparseTensorCounts {
-  int coo;
-  int csr;
-  int csc;
-  int csf;
-  int ndim_csf;
-
-  int num_total_tensors() const { return coo + csr + csc + csf; }
-  int num_total_buffers() const {
-    return coo * 3 + csr * 4 + csc * 4 + 2 * ndim_csf + csf;
-  }
-};
-
-/// \brief Read serialized Python sequence from file interface using Arrow IPC
-/// \param[in] src a RandomAccessFile
-/// \param[out] out the reconstructed data
-/// \return Status
-ARROW_DEPRECATED("Deprecated in 18.0.0. Will be removed in 20.0.0")
-ARROW_PYTHON_EXPORT
-Status ReadSerializedObject(io::RandomAccessFile* src, SerializedPyObject* out);
-
-/// \brief Reconstruct SerializedPyObject from representation produced by
-/// SerializedPyObject::GetComponents.
-///
-/// \param[in] num_tensors number of tensors in the object
-/// \param[in] num_sparse_tensors number of sparse tensors in the object
-/// \param[in] num_ndarrays number of numpy Ndarrays in the object
-/// \param[in] num_buffers number of buffers in the object
-/// \param[in] data a list containing pyarrow.Buffer instances. It must be 1 +
-/// num_tensors * 2 + num_coo_tensors * 3 + num_csr_tensors * 4 + num_csc_tensors * 4 +
-/// num_csf_tensors * (2 * ndim_csf + 3) + num_buffers in length
-/// \param[out] out the reconstructed object
-/// \return Status
-ARROW_DEPRECATED("Deprecated in 18.0.0. Will be removed in 20.0.0")
-ARROW_PYTHON_EXPORT
-Status GetSerializedFromComponents(int num_tensors,
-                                   const SparseTensorCounts& num_sparse_tensors,
-                                   int num_ndarrays, int num_buffers, PyObject* data,
-                                   SerializedPyObject* out);
-
-/// \brief Reconstruct Python object from Arrow-serialized representation
-/// \param[in] context Serialization context which contains custom serialization
-/// and deserialization callbacks. Can be any Python object with a
-/// _serialize_callback method for serialization and a _deserialize_callback
-/// method for deserialization. If context is None, no custom serialization
-/// will be attempted.
-/// \param[in] object Object to deserialize
-/// \param[in] base a Python object holding the underlying data that any NumPy
-/// arrays will reference, to avoid premature deallocation
-/// \param[out] out The returned object
-/// \return Status
-/// This acquires the GIL
-ARROW_DEPRECATED("Deprecated in 18.0.0. Will be removed in 20.0.0")
-ARROW_PYTHON_EXPORT
-Status DeserializeObject(PyObject* context, const SerializedPyObject& object,
-                         PyObject* base, PyObject** out);
-
-/// \brief Reconstruct Ndarray from Arrow-serialized representation
-/// \param[in] object Object to deserialize
-/// \param[out] out The deserialized tensor
-/// \return Status
-ARROW_DEPRECATED("Deprecated in 18.0.0. Will be removed in 20.0.0")
-ARROW_PYTHON_EXPORT
-Status DeserializeNdarray(const SerializedPyObject& object, std::shared_ptr<Tensor>* out);
-
-ARROW_DEPRECATED("Deprecated in 18.0.0. Will be removed in 20.0.0")
-ARROW_PYTHON_EXPORT
-Status NdarrayFromBuffer(std::shared_ptr<Buffer> src, std::shared_ptr<Tensor>* out);
-
-}  // namespace py
-}  // namespace arrow
diff --git a/python/pyarrow/src/arrow/python/serialize.cc b/python/pyarrow/src/arrow/python/serialize.cc
deleted file mode 100644
index ad079cbd9c70..000000000000
--- a/python/pyarrow/src/arrow/python/serialize.cc
+++ /dev/null
@@ -1,798 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-#include "arrow/python/serialize.h"
-#include "arrow/python/numpy_interop.h"
-
-#include <cstdint>
-#include <limits>
-#include <memory>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-
-#include "arrow/array.h"
-#include "arrow/array/builder_binary.h"
-#include "arrow/array/builder_nested.h"
-#include "arrow/array/builder_primitive.h"
-#include "arrow/array/builder_union.h"
-#include "arrow/io/interfaces.h"
-#include "arrow/io/memory.h"
-#include "arrow/ipc/util.h"
-#include "arrow/ipc/writer.h"
-#include "arrow/record_batch.h"
-#include "arrow/result.h"
-#include "arrow/tensor.h"
-#include "arrow/util/logging.h"
-
-#include "arrow/python/common.h"
-#include "arrow/python/datetime.h"
-#include "arrow/python/helpers.h"
-#include "arrow/python/iterators.h"
-#include "arrow/python/numpy_convert.h"
-#include "arrow/python/platform.h"
-#include "arrow/python/pyarrow.h"
-
-constexpr int32_t kMaxRecursionDepth = 100;
-
-namespace arrow {
-
-using internal::checked_cast;
-
-namespace py {
-
-class SequenceBuilder;
-class DictBuilder;
-
-Status Append(PyObject* context, PyObject* elem, SequenceBuilder* builder,
-              int32_t recursion_depth, SerializedPyObject* blobs_out);
-
-// A Sequence is a heterogeneous collections of elements. It can contain
-// scalar Python types, lists, tuples, dictionaries, tensors and sparse tensors.
-class SequenceBuilder {
- public:
-  explicit SequenceBuilder(MemoryPool* pool = default_memory_pool())
-      : pool_(pool),
-        types_(::arrow::int8(), pool),
-        offsets_(::arrow::int32(), pool),
-        type_map_(PythonType::NUM_PYTHON_TYPES, -1) {
-    auto null_builder = std::make_shared<NullBuilder>(pool);
-    auto initial_ty = dense_union({field("0", null())});
-    builder_.reset(new DenseUnionBuilder(pool, {null_builder}, initial_ty));
-  }
-
-  // Appending a none to the sequence
-  Status AppendNone() { return builder_->AppendNull(); }
-
-  template <typename BuilderType, typename MakeBuilderFn>
-  Status CreateAndUpdate(std::shared_ptr<BuilderType>* child_builder, int8_t tag,
-                         MakeBuilderFn make_builder) {
-    if (!*child_builder) {
-      child_builder->reset(make_builder());
-      std::ostringstream convert;
-      convert.imbue(std::locale::classic());
-      convert << static_cast<int>(tag);
-      type_map_[tag] = builder_->AppendChild(*child_builder, convert.str());
-    }
-    return builder_->Append(type_map_[tag]);
-  }
-
-  template <typename BuilderType, typename T>
-  Status AppendPrimitive(std::shared_ptr<BuilderType>* child_builder, const T val,
-                         int8_t tag) {
-    RETURN_NOT_OK(
-        CreateAndUpdate(child_builder, tag, [this]() { return new BuilderType(pool_); }));
-    return (*child_builder)->Append(val);
-  }
-
-  // Appending a boolean to the sequence
-  Status AppendBool(const bool data) {
-    return AppendPrimitive(&bools_, data, PythonType::BOOL);
-  }
-
-  // Appending an int64_t to the sequence
-  Status AppendInt64(const int64_t data) {
-    return AppendPrimitive(&ints_, data, PythonType::INT);
-  }
-
-  // Append a list of bytes to the sequence
-  Status AppendBytes(const uint8_t* data, int32_t length) {
-    RETURN_NOT_OK(CreateAndUpdate(&bytes_, PythonType::BYTES,
-                                  [this]() { return new BinaryBuilder(pool_); }));
-    return bytes_->Append(data, length);
-  }
-
-  // Appending a string to the sequence
-  Status AppendString(const char* data, int32_t length) {
-    RETURN_NOT_OK(CreateAndUpdate(&strings_, PythonType::STRING,
-                                  [this]() { return new StringBuilder(pool_); }));
-    return strings_->Append(data, length);
-  }
-
-  // Appending a half_float to the sequence
-  Status AppendHalfFloat(const npy_half data) {
-    return AppendPrimitive(&half_floats_, data, PythonType::HALF_FLOAT);
-  }
-
-  // Appending a float to the sequence
-  Status AppendFloat(const float data) {
-    return AppendPrimitive(&floats_, data, PythonType::FLOAT);
-  }
-
-  // Appending a double to the sequence
-  Status AppendDouble(const double data) {
-    return AppendPrimitive(&doubles_, data, PythonType::DOUBLE);
-  }
-
-  // Appending a Date64 timestamp to the sequence
-  Status AppendDate64(const int64_t timestamp) {
-    return AppendPrimitive(&date64s_, timestamp, PythonType::DATE64);
-  }
-
-  // Appending a tensor to the sequence
-  //
-  // \param tensor_index Index of the tensor in the object.
-  Status AppendTensor(const int32_t tensor_index) {
-    RETURN_NOT_OK(CreateAndUpdate(&tensor_indices_, PythonType::TENSOR,
-                                  [this]() { return new Int32Builder(pool_); }));
-    return tensor_indices_->Append(tensor_index);
-  }
-
-  // Appending a sparse coo tensor to the sequence
-  //
-  // \param sparse_coo_tensor_index Index of the sparse coo tensor in the object.
-  Status AppendSparseCOOTensor(const int32_t sparse_coo_tensor_index) {
-    RETURN_NOT_OK(CreateAndUpdate(&sparse_coo_tensor_indices_,
-                                  PythonType::SPARSECOOTENSOR,
-                                  [this]() { return new Int32Builder(pool_); }));
-    return sparse_coo_tensor_indices_->Append(sparse_coo_tensor_index);
-  }
-
-  // Appending a sparse csr matrix to the sequence
-  //
-  // \param sparse_csr_matrix_index Index of the sparse csr matrix in the object.
-  Status AppendSparseCSRMatrix(const int32_t sparse_csr_matrix_index) {
-    RETURN_NOT_OK(CreateAndUpdate(&sparse_csr_matrix_indices_,
-                                  PythonType::SPARSECSRMATRIX,
-                                  [this]() { return new Int32Builder(pool_); }));
-    return sparse_csr_matrix_indices_->Append(sparse_csr_matrix_index);
-  }
-
-  // Appending a sparse csc matrix to the sequence
-  //
-  // \param sparse_csc_matrix_index Index of the sparse csc matrix in the object.
-  Status AppendSparseCSCMatrix(const int32_t sparse_csc_matrix_index) {
-    RETURN_NOT_OK(CreateAndUpdate(&sparse_csc_matrix_indices_,
-                                  PythonType::SPARSECSCMATRIX,
-                                  [this]() { return new Int32Builder(pool_); }));
-    return sparse_csc_matrix_indices_->Append(sparse_csc_matrix_index);
-  }
-
-  // Appending a sparse csf tensor to the sequence
-  //
-  // \param sparse_csf_tensor_index Index of the sparse csf tensor in the object.
-  Status AppendSparseCSFTensor(const int32_t sparse_csf_tensor_index) {
-    RETURN_NOT_OK(CreateAndUpdate(&sparse_csf_tensor_indices_,
-                                  PythonType::SPARSECSFTENSOR,
-                                  [this]() { return new Int32Builder(pool_); }));
-    return sparse_csf_tensor_indices_->Append(sparse_csf_tensor_index);
-  }
-
-  // Appending a numpy ndarray to the sequence
-  //
-  // \param tensor_index Index of the tensor in the object.
-  Status AppendNdarray(const int32_t ndarray_index) {
-    RETURN_NOT_OK(CreateAndUpdate(&ndarray_indices_, PythonType::NDARRAY,
-                                  [this]() { return new Int32Builder(pool_); }));
-    return ndarray_indices_->Append(ndarray_index);
-  }
-
-  // Appending a buffer to the sequence
-  //
-  // \param buffer_index Index of the buffer in the object.
-  Status AppendBuffer(const int32_t buffer_index) {
-    RETURN_NOT_OK(CreateAndUpdate(&buffer_indices_, PythonType::BUFFER,
-                                  [this]() { return new Int32Builder(pool_); }));
-    return buffer_indices_->Append(buffer_index);
-  }
-
-  Status AppendSequence(PyObject* context, PyObject* sequence, int8_t tag,
-                        std::shared_ptr<ListBuilder>& target_sequence,
-                        std::unique_ptr<SequenceBuilder>& values, int32_t recursion_depth,
-                        SerializedPyObject* blobs_out) {
-    if (recursion_depth >= kMaxRecursionDepth) {
-      return Status::NotImplemented(
-          "This object exceeds the maximum recursion depth. It may contain itself "
-          "recursively.");
-    }
-    RETURN_NOT_OK(CreateAndUpdate(&target_sequence, tag, [this, &values]() {
-      values.reset(new SequenceBuilder(pool_));
-      return new ListBuilder(pool_, values->builder());
-    }));
-    RETURN_NOT_OK(target_sequence->Append());
-    return internal::VisitIterable(
-        sequence, [&](PyObject* obj, bool* keep_going /* unused */) {
-          return Append(context, obj, values.get(), recursion_depth, blobs_out);
-        });
-  }
-
-  Status AppendList(PyObject* context, PyObject* list, int32_t recursion_depth,
-                    SerializedPyObject* blobs_out) {
-    return AppendSequence(context, list, PythonType::LIST, lists_, list_values_,
-                          recursion_depth + 1, blobs_out);
-  }
-
-  Status AppendTuple(PyObject* context, PyObject* tuple, int32_t recursion_depth,
-                     SerializedPyObject* blobs_out) {
-    return AppendSequence(context, tuple, PythonType::TUPLE, tuples_, tuple_values_,
-                          recursion_depth + 1, blobs_out);
-  }
-
-  Status AppendSet(PyObject* context, PyObject* set, int32_t recursion_depth,
-                   SerializedPyObject* blobs_out) {
-    return AppendSequence(context, set, PythonType::SET, sets_, set_values_,
-                          recursion_depth + 1, blobs_out);
-  }
-
-  Status AppendDict(PyObject* context, PyObject* dict, int32_t recursion_depth,
-                    SerializedPyObject* blobs_out);
-
-  // Finish building the sequence and return the result.
-  // Input arrays may be nullptr
-  Status Finish(std::shared_ptr<Array>* out) { return builder_->Finish(out); }
-
-  std::shared_ptr<DenseUnionBuilder> builder() { return builder_; }
-
- private:
-  MemoryPool* pool_;
-
-  Int8Builder types_;
-  Int32Builder offsets_;
-
-  /// Mapping from PythonType to child index
-  std::vector<int8_t> type_map_;
-
-  std::shared_ptr<BooleanBuilder> bools_;
-  std::shared_ptr<Int64Builder> ints_;
-  std::shared_ptr<BinaryBuilder> bytes_;
-  std::shared_ptr<StringBuilder> strings_;
-  std::shared_ptr<HalfFloatBuilder> half_floats_;
-  std::shared_ptr<FloatBuilder> floats_;
-  std::shared_ptr<DoubleBuilder> doubles_;
-  std::shared_ptr<Date64Builder> date64s_;
-
-  std::unique_ptr<SequenceBuilder> list_values_;
-  std::shared_ptr<ListBuilder> lists_;
-  std::unique_ptr<DictBuilder> dict_values_;
-  std::shared_ptr<ListBuilder> dicts_;
-  std::unique_ptr<SequenceBuilder> tuple_values_;
-  std::shared_ptr<ListBuilder> tuples_;
-  std::unique_ptr<SequenceBuilder> set_values_;
-  std::shared_ptr<ListBuilder> sets_;
-
-  std::shared_ptr<Int32Builder> tensor_indices_;
-  std::shared_ptr<Int32Builder> sparse_coo_tensor_indices_;
-  std::shared_ptr<Int32Builder> sparse_csr_matrix_indices_;
-  std::shared_ptr<Int32Builder> sparse_csc_matrix_indices_;
-  std::shared_ptr<Int32Builder> sparse_csf_tensor_indices_;
-  std::shared_ptr<Int32Builder> ndarray_indices_;
-  std::shared_ptr<Int32Builder> buffer_indices_;
-
-  std::shared_ptr<DenseUnionBuilder> builder_;
-};
-
-// Constructing dictionaries of key/value pairs. Sequences of
-// keys and values are built separately using a pair of
-// SequenceBuilders. The resulting Arrow representation
-// can be obtained via the Finish method.
-class DictBuilder {
- public:
-  explicit DictBuilder(MemoryPool* pool = nullptr) : keys_(pool), vals_(pool) {
-    builder_.reset(new StructBuilder(struct_({field("keys", dense_union(FieldVector{})),
-                                              field("vals", dense_union(FieldVector{}))}),
-                                     pool, {keys_.builder(), vals_.builder()}));
-  }
-
-  // Builder for the keys of the dictionary
-  SequenceBuilder& keys() { return keys_; }
-  // Builder for the values of the dictionary
-  SequenceBuilder& vals() { return vals_; }
-
-  // Construct an Arrow StructArray representing the dictionary.
-  // Contains a field "keys" for the keys and "vals" for the values.
-  Status Finish(std::shared_ptr<Array>* out) { return builder_->Finish(out); }
-
-  std::shared_ptr<StructBuilder> builder() { return builder_; }
-
- private:
-  SequenceBuilder keys_;
-  SequenceBuilder vals_;
-  std::shared_ptr<StructBuilder> builder_;
-};
-
-Status SequenceBuilder::AppendDict(PyObject* context, PyObject* dict,
-                                   int32_t recursion_depth,
-                                   SerializedPyObject* blobs_out) {
-  if (recursion_depth >= kMaxRecursionDepth) {
-    return Status::NotImplemented(
-        "This object exceeds the maximum recursion depth. It may contain itself "
-        "recursively.");
-  }
-  RETURN_NOT_OK(CreateAndUpdate(&dicts_, PythonType::DICT, [this]() {
-    dict_values_.reset(new DictBuilder(pool_));
-    return new ListBuilder(pool_, dict_values_->builder());
-  }));
-  RETURN_NOT_OK(dicts_->Append());
-  PyObject* key;
-  PyObject* value;
-  Py_ssize_t pos = 0;
-  while (PyDict_Next(dict, &pos, &key, &value)) {
-    RETURN_NOT_OK(dict_values_->builder()->Append());
-    RETURN_NOT_OK(
-        Append(context, key, &dict_values_->keys(), recursion_depth + 1, blobs_out));
-    RETURN_NOT_OK(
-        Append(context, value, &dict_values_->vals(), recursion_depth + 1, blobs_out));
-  }
-
-  // This block is used to decrement the reference counts of the results
-  // returned by the serialization callback, which is called in AppendArray,
-  // in DeserializeDict and in Append
-  static PyObject* py_type = PyUnicode_FromString("_pytype_");
-  if (PyDict_Contains(dict, py_type)) {
-    // If the dictionary contains the key "_pytype_", then the user has to
-    // have registered a callback.
-    if (context == Py_None) {
-      return Status::Invalid("No serialization callback set");
-    }
-    Py_XDECREF(dict);
-  }
-  return Status::OK();
-}
-
-Status CallCustomCallback(PyObject* context, PyObject* method_name, PyObject* elem,
-                          PyObject** result) {
-  if (context == Py_None) {
-    *result = NULL;
-    return Status::SerializationError("error while calling callback on ",
-                                      internal::PyObject_StdStringRepr(elem),
-                                      ": handler not registered");
-  } else {
-    *result = PyObject_CallMethodObjArgs(context, method_name, elem, NULL);
-    return CheckPyError();
-  }
-}
-
-Status CallSerializeCallback(PyObject* context, PyObject* value,
-                             PyObject** serialized_object) {
-  OwnedRef method_name(PyUnicode_FromString("_serialize_callback"));
-  RETURN_NOT_OK(CallCustomCallback(context, method_name.obj(), value, serialized_object));
-  if (!PyDict_Check(*serialized_object)) {
-    return Status::TypeError("serialization callback must return a valid dictionary");
-  }
-  return Status::OK();
-}
-
-Status CallDeserializeCallback(PyObject* context, PyObject* value,
-                               PyObject** deserialized_object) {
-  OwnedRef method_name(PyUnicode_FromString("_deserialize_callback"));
-  return CallCustomCallback(context, method_name.obj(), value, deserialized_object);
-}
-
-Status AppendArray(PyObject* context, PyArrayObject* array, SequenceBuilder* builder,
-                   int32_t recursion_depth, SerializedPyObject* blobs_out);
-
-template <typename NumpyScalarObject>
-Status AppendIntegerScalar(PyObject* obj, SequenceBuilder* builder) {
-  int64_t value = reinterpret_cast<NumpyScalarObject*>(obj)->obval;
-  return builder->AppendInt64(value);
-}
-
-// Append a potentially 64-bit wide unsigned Numpy scalar.
-// Must check for overflow as we reinterpret it as signed int64.
-template <typename NumpyScalarObject>
-Status AppendLargeUnsignedScalar(PyObject* obj, SequenceBuilder* builder) {
-  constexpr uint64_t max_value = std::numeric_limits<int64_t>::max();
-
-  uint64_t value = reinterpret_cast<NumpyScalarObject*>(obj)->obval;
-  if (value > max_value) {
-    return Status::Invalid("cannot serialize Numpy uint64 scalar >= 2**63");
-  }
-  return builder->AppendInt64(static_cast<int64_t>(value));
-}
-
-Status AppendScalar(PyObject* obj, SequenceBuilder* builder) {
-  if (PyArray_IsScalar(obj, Bool)) {
-    return builder->AppendBool(reinterpret_cast<PyBoolScalarObject*>(obj)->obval != 0);
-  } else if (PyArray_IsScalar(obj, Half)) {
-    return builder->AppendHalfFloat(reinterpret_cast<PyHalfScalarObject*>(obj)->obval);
-  } else if (PyArray_IsScalar(obj, Float)) {
-    return builder->AppendFloat(reinterpret_cast<PyFloatScalarObject*>(obj)->obval);
-  } else if (PyArray_IsScalar(obj, Double)) {
-    return builder->AppendDouble(reinterpret_cast<PyDoubleScalarObject*>(obj)->obval);
-  }
-  if (PyArray_IsScalar(obj, Byte)) {
-    return AppendIntegerScalar<PyByteScalarObject>(obj, builder);
-  } else if (PyArray_IsScalar(obj, Short)) {
-    return AppendIntegerScalar<PyShortScalarObject>(obj, builder);
-  } else if (PyArray_IsScalar(obj, Int)) {
-    return AppendIntegerScalar<PyIntScalarObject>(obj, builder);
-  } else if (PyArray_IsScalar(obj, Long)) {
-    return AppendIntegerScalar<PyLongScalarObject>(obj, builder);
-  } else if (PyArray_IsScalar(obj, LongLong)) {
-    return AppendIntegerScalar<PyLongLongScalarObject>(obj, builder);
-  } else if (PyArray_IsScalar(obj, Int64)) {
-    return AppendIntegerScalar<PyInt64ScalarObject>(obj, builder);
-  } else if (PyArray_IsScalar(obj, UByte)) {
-    return AppendIntegerScalar<PyUByteScalarObject>(obj, builder);
-  } else if (PyArray_IsScalar(obj, UShort)) {
-    return AppendIntegerScalar<PyUShortScalarObject>(obj, builder);
-  } else if (PyArray_IsScalar(obj, UInt)) {
-    return AppendIntegerScalar<PyUIntScalarObject>(obj, builder);
-  } else if (PyArray_IsScalar(obj, ULong)) {
-    return AppendLargeUnsignedScalar<PyULongScalarObject>(obj, builder);
-  } else if (PyArray_IsScalar(obj, ULongLong)) {
-    return AppendLargeUnsignedScalar<PyULongLongScalarObject>(obj, builder);
-  } else if (PyArray_IsScalar(obj, UInt64)) {
-    return AppendLargeUnsignedScalar<PyUInt64ScalarObject>(obj, builder);
-  }
-  return Status::NotImplemented("Numpy scalar type not recognized");
-}
-
-Status Append(PyObject* context, PyObject* elem, SequenceBuilder* builder,
-              int32_t recursion_depth, SerializedPyObject* blobs_out) {
-  // The bool case must precede the int case (PyInt_Check passes for bools)
-  if (PyBool_Check(elem)) {
-    RETURN_NOT_OK(builder->AppendBool(elem == Py_True));
-  } else if (PyArray_DescrFromScalar(elem)->type_num == NPY_HALF) {
-    npy_half halffloat = reinterpret_cast<PyHalfScalarObject*>(elem)->obval;
-    RETURN_NOT_OK(builder->AppendHalfFloat(halffloat));
-  } else if (PyFloat_Check(elem)) {
-    RETURN_NOT_OK(builder->AppendDouble(PyFloat_AS_DOUBLE(elem)));
-  } else if (PyLong_Check(elem)) {
-    int overflow = 0;
-    int64_t data = PyLong_AsLongLongAndOverflow(elem, &overflow);
-    if (!overflow) {
-      RETURN_NOT_OK(builder->AppendInt64(data));
-    } else {
-      // Attempt to serialize the object using the custom callback.
-      PyObject* serialized_object;
-      // The reference count of serialized_object will be decremented in SerializeDict
-      RETURN_NOT_OK(CallSerializeCallback(context, elem, &serialized_object));
-      RETURN_NOT_OK(
-          builder->AppendDict(context, serialized_object, recursion_depth, blobs_out));
-    }
-  } else if (PyBytes_Check(elem)) {
-    auto data = reinterpret_cast<uint8_t*>(PyBytes_AS_STRING(elem));
-    int32_t size = -1;
-    RETURN_NOT_OK(internal::CastSize(PyBytes_GET_SIZE(elem), &size));
-    RETURN_NOT_OK(builder->AppendBytes(data, size));
-  } else if (PyUnicode_Check(elem)) {
-    ARROW_ASSIGN_OR_RAISE(auto view, PyBytesView::FromUnicode(elem));
-    int32_t size = -1;
-    RETURN_NOT_OK(internal::CastSize(view.size, &size));
-    RETURN_NOT_OK(builder->AppendString(view.bytes, size));
-  } else if (PyList_CheckExact(elem)) {
-    RETURN_NOT_OK(builder->AppendList(context, elem, recursion_depth, blobs_out));
-  } else if (PyDict_CheckExact(elem)) {
-    RETURN_NOT_OK(builder->AppendDict(context, elem, recursion_depth, blobs_out));
-  } else if (PyTuple_CheckExact(elem)) {
-    RETURN_NOT_OK(builder->AppendTuple(context, elem, recursion_depth, blobs_out));
-  } else if (PySet_Check(elem)) {
-    RETURN_NOT_OK(builder->AppendSet(context, elem, recursion_depth, blobs_out));
-  } else if (PyArray_IsScalar(elem, Generic)) {
-    RETURN_NOT_OK(AppendScalar(elem, builder));
-  } else if (PyArray_CheckExact(elem)) {
-    RETURN_NOT_OK(AppendArray(context, reinterpret_cast<PyArrayObject*>(elem), builder,
-                              recursion_depth, blobs_out));
-  } else if (elem == Py_None) {
-    RETURN_NOT_OK(builder->AppendNone());
-  } else if (PyDateTime_Check(elem)) {
-    PyDateTime_DateTime* datetime = reinterpret_cast<PyDateTime_DateTime*>(elem);
-    RETURN_NOT_OK(builder->AppendDate64(internal::PyDateTime_to_us(datetime)));
-  } else if (is_buffer(elem)) {
-    RETURN_NOT_OK(builder->AppendBuffer(static_cast<int32_t>(blobs_out->buffers.size())));
-    ARROW_ASSIGN_OR_RAISE(auto buffer, unwrap_buffer(elem));
-    blobs_out->buffers.push_back(buffer);
-  } else if (is_tensor(elem)) {
-    RETURN_NOT_OK(builder->AppendTensor(static_cast<int32_t>(blobs_out->tensors.size())));
-    ARROW_ASSIGN_OR_RAISE(auto tensor, unwrap_tensor(elem));
-    blobs_out->tensors.push_back(tensor);
-  } else if (is_sparse_coo_tensor(elem)) {
-    RETURN_NOT_OK(builder->AppendSparseCOOTensor(
-        static_cast<int32_t>(blobs_out->sparse_tensors.size())));
-    ARROW_ASSIGN_OR_RAISE(auto tensor, unwrap_sparse_coo_tensor(elem));
-    blobs_out->sparse_tensors.push_back(tensor);
-  } else if (is_sparse_csr_matrix(elem)) {
-    RETURN_NOT_OK(builder->AppendSparseCSRMatrix(
-        static_cast<int32_t>(blobs_out->sparse_tensors.size())));
-    ARROW_ASSIGN_OR_RAISE(auto matrix, unwrap_sparse_csr_matrix(elem));
-    blobs_out->sparse_tensors.push_back(matrix);
-  } else if (is_sparse_csc_matrix(elem)) {
-    RETURN_NOT_OK(builder->AppendSparseCSCMatrix(
-        static_cast<int32_t>(blobs_out->sparse_tensors.size())));
-    ARROW_ASSIGN_OR_RAISE(auto matrix, unwrap_sparse_csc_matrix(elem));
-    blobs_out->sparse_tensors.push_back(matrix);
-  } else if (is_sparse_csf_tensor(elem)) {
-    RETURN_NOT_OK(builder->AppendSparseCSFTensor(
-        static_cast<int32_t>(blobs_out->sparse_tensors.size())));
-    ARROW_ASSIGN_OR_RAISE(auto tensor, unwrap_sparse_csf_tensor(elem));
-    blobs_out->sparse_tensors.push_back(tensor);
-  } else {
-    // Attempt to serialize the object using the custom callback.
-    PyObject* serialized_object;
-    // The reference count of serialized_object will be decremented in SerializeDict
-    RETURN_NOT_OK(CallSerializeCallback(context, elem, &serialized_object));
-    RETURN_NOT_OK(
-        builder->AppendDict(context, serialized_object, recursion_depth, blobs_out));
-  }
-  return Status::OK();
-}
-
-Status AppendArray(PyObject* context, PyArrayObject* array, SequenceBuilder* builder,
-                   int32_t recursion_depth, SerializedPyObject* blobs_out) {
-  int dtype = PyArray_TYPE(array);
-  switch (dtype) {
-    case NPY_UINT8:
-    case NPY_INT8:
-    case NPY_UINT16:
-    case NPY_INT16:
-    case NPY_UINT32:
-    case NPY_INT32:
-    case NPY_UINT64:
-    case NPY_INT64:
-    case NPY_HALF:
-    case NPY_FLOAT:
-    case NPY_DOUBLE: {
-      RETURN_NOT_OK(
-          builder->AppendNdarray(static_cast<int32_t>(blobs_out->ndarrays.size())));
-      std::shared_ptr<Tensor> tensor;
-      RETURN_NOT_OK(NdarrayToTensor(default_memory_pool(),
-                                    reinterpret_cast<PyObject*>(array), {}, &tensor));
-      blobs_out->ndarrays.push_back(tensor);
-    } break;
-    default: {
-      PyObject* serialized_object;
-      // The reference count of serialized_object will be decremented in SerializeDict
-      RETURN_NOT_OK(CallSerializeCallback(context, reinterpret_cast<PyObject*>(array),
-                                          &serialized_object));
-      RETURN_NOT_OK(builder->AppendDict(context, serialized_object, recursion_depth + 1,
-                                        blobs_out));
-    }
-  }
-  return Status::OK();
-}
-
-std::shared_ptr<RecordBatch> MakeBatch(std::shared_ptr<Array> data) {
-  auto field = std::make_shared<Field>("list", data->type());
-  auto schema = ::arrow::schema({field});
-  return RecordBatch::Make(schema, data->length(), {data});
-}
-
-Status SerializeObject(PyObject* context, PyObject* sequence, SerializedPyObject* out) {
-  PyAcquireGIL lock;
-  SequenceBuilder builder;
-  RETURN_NOT_OK(internal::VisitIterable(
-      sequence, [&](PyObject* obj, bool* keep_going /* unused */) {
-        return Append(context, obj, &builder, 0, out);
-      }));
-  std::shared_ptr<Array> array;
-  RETURN_NOT_OK(builder.Finish(&array));
-  out->batch = MakeBatch(array);
-  return Status::OK();
-}
-
-Status SerializeNdarray(std::shared_ptr<Tensor> tensor, SerializedPyObject* out) {
-  std::shared_ptr<Array> array;
-  SequenceBuilder builder;
-  RETURN_NOT_OK(builder.AppendNdarray(static_cast<int32_t>(out->ndarrays.size())));
-  out->ndarrays.push_back(tensor);
-  RETURN_NOT_OK(builder.Finish(&array));
-  out->batch = MakeBatch(array);
-  return Status::OK();
-}
-
-Status WriteNdarrayHeader(std::shared_ptr<DataType> dtype,
-                          const std::vector<int64_t>& shape, int64_t tensor_num_bytes,
-                          io::OutputStream* dst) {
-  auto empty_tensor = std::make_shared<Tensor>(
-      dtype, std::make_shared<Buffer>(nullptr, tensor_num_bytes), shape);
-  SerializedPyObject serialized_tensor;
-  RETURN_NOT_OK(SerializeNdarray(empty_tensor, &serialized_tensor));
-  return serialized_tensor.WriteTo(dst);
-}
-
-SerializedPyObject::SerializedPyObject()
-    : ipc_options(ipc::IpcWriteOptions::Defaults()) {}
-
-Status SerializedPyObject::WriteTo(io::OutputStream* dst) {
-  int32_t num_tensors = static_cast<int32_t>(this->tensors.size());
-  int32_t num_sparse_tensors = static_cast<int32_t>(this->sparse_tensors.size());
-  int32_t num_ndarrays = static_cast<int32_t>(this->ndarrays.size());
-  int32_t num_buffers = static_cast<int32_t>(this->buffers.size());
-  RETURN_NOT_OK(
-      dst->Write(reinterpret_cast<const uint8_t*>(&num_tensors), sizeof(int32_t)));
-  RETURN_NOT_OK(
-      dst->Write(reinterpret_cast<const uint8_t*>(&num_sparse_tensors), sizeof(int32_t)));
-  RETURN_NOT_OK(
-      dst->Write(reinterpret_cast<const uint8_t*>(&num_ndarrays), sizeof(int32_t)));
-  RETURN_NOT_OK(
-      dst->Write(reinterpret_cast<const uint8_t*>(&num_buffers), sizeof(int32_t)));
-
-  // Align stream to 8-byte offset
-  RETURN_NOT_OK(ipc::AlignStream(dst, ipc::kArrowIpcAlignment));
-  RETURN_NOT_OK(ipc::WriteRecordBatchStream({this->batch}, this->ipc_options, dst));
-
-  // Align stream to 64-byte offset so tensor bodies are 64-byte aligned
-  RETURN_NOT_OK(ipc::AlignStream(dst, ipc::kTensorAlignment));
-
-  int32_t metadata_length;
-  int64_t body_length;
-  for (const auto& tensor : this->tensors) {
-    RETURN_NOT_OK(ipc::WriteTensor(*tensor, dst, &metadata_length, &body_length));
-    RETURN_NOT_OK(ipc::AlignStream(dst, ipc::kTensorAlignment));
-  }
-
-  for (const auto& sparse_tensor : this->sparse_tensors) {
-    RETURN_NOT_OK(
-        ipc::WriteSparseTensor(*sparse_tensor, dst, &metadata_length, &body_length));
-    RETURN_NOT_OK(ipc::AlignStream(dst, ipc::kTensorAlignment));
-  }
-
-  for (const auto& tensor : this->ndarrays) {
-    RETURN_NOT_OK(ipc::WriteTensor(*tensor, dst, &metadata_length, &body_length));
-    RETURN_NOT_OK(ipc::AlignStream(dst, ipc::kTensorAlignment));
-  }
-
-  for (const auto& buffer : this->buffers) {
-    int64_t size = buffer->size();
-    RETURN_NOT_OK(dst->Write(reinterpret_cast<const uint8_t*>(&size), sizeof(int64_t)));
-    RETURN_NOT_OK(dst->Write(buffer->data(), size));
-  }
-
-  return Status::OK();
-}
-
-namespace {
-
-Status CountSparseTensors(
-    const std::vector<std::shared_ptr<SparseTensor>>& sparse_tensors, PyObject** out) {
-  OwnedRef num_sparse_tensors(PyDict_New());
-  size_t num_coo = 0;
-  size_t num_csr = 0;
-  size_t num_csc = 0;
-  size_t num_csf = 0;
-  size_t ndim_csf = 0;
-
-  for (const auto& sparse_tensor : sparse_tensors) {
-    switch (sparse_tensor->format_id()) {
-      case SparseTensorFormat::COO:
-        ++num_coo;
-        break;
-      case SparseTensorFormat::CSR:
-        ++num_csr;
-        break;
-      case SparseTensorFormat::CSC:
-        ++num_csc;
-        break;
-      case SparseTensorFormat::CSF:
-        ++num_csf;
-        ndim_csf += sparse_tensor->ndim();
-        break;
-    }
-  }
-
-  PyDict_SetItemString(num_sparse_tensors.obj(), "coo", PyLong_FromSize_t(num_coo));
-  PyDict_SetItemString(num_sparse_tensors.obj(), "csr", PyLong_FromSize_t(num_csr));
-  PyDict_SetItemString(num_sparse_tensors.obj(), "csc", PyLong_FromSize_t(num_csc));
-  PyDict_SetItemString(num_sparse_tensors.obj(), "csf", PyLong_FromSize_t(num_csf));
-  PyDict_SetItemString(num_sparse_tensors.obj(), "ndim_csf", PyLong_FromSize_t(ndim_csf));
-  RETURN_IF_PYERROR();
-
-  *out = num_sparse_tensors.detach();
-  return Status::OK();
-}
-
-}  // namespace
-
-Status SerializedPyObject::GetComponents(MemoryPool* memory_pool, PyObject** out) {
-  PyAcquireGIL py_gil;
-
-  OwnedRef result(PyDict_New());
-  PyObject* buffers = PyList_New(0);
-  PyObject* num_sparse_tensors = nullptr;
-
-  // TODO(wesm): Not sure how pedantic we need to be about checking the return
-  // values of these functions. There are other places where we do not check
-  // PyDict_SetItem/SetItemString return value, but these failures would be
-  // quite esoteric
-  PyDict_SetItemString(result.obj(), "num_tensors",
-                       PyLong_FromSize_t(this->tensors.size()));
-  RETURN_NOT_OK(CountSparseTensors(this->sparse_tensors, &num_sparse_tensors));
-  PyDict_SetItemString(result.obj(), "num_sparse_tensors", num_sparse_tensors);
-  PyDict_SetItemString(result.obj(), "ndim_csf", num_sparse_tensors);
-  PyDict_SetItemString(result.obj(), "num_ndarrays",
-                       PyLong_FromSize_t(this->ndarrays.size()));
-  PyDict_SetItemString(result.obj(), "num_buffers",
-                       PyLong_FromSize_t(this->buffers.size()));
-  PyDict_SetItemString(result.obj(), "data", buffers);
-  RETURN_IF_PYERROR();
-
-  Py_DECREF(buffers);
-
-  auto PushBuffer = [&buffers](const std::shared_ptr<Buffer>& buffer) {
-    PyObject* wrapped_buffer = wrap_buffer(buffer);
-    RETURN_IF_PYERROR();
-    if (PyList_Append(buffers, wrapped_buffer) < 0) {
-      Py_DECREF(wrapped_buffer);
-      RETURN_IF_PYERROR();
-    }
-    Py_DECREF(wrapped_buffer);
-    return Status::OK();
-  };
-
-  constexpr int64_t kInitialCapacity = 1024;
-
-  // Write the record batch describing the object structure
-  py_gil.release();
-  ARROW_ASSIGN_OR_RAISE(auto stream,
-                        io::BufferOutputStream::Create(kInitialCapacity, memory_pool));
-  RETURN_NOT_OK(
-      ipc::WriteRecordBatchStream({this->batch}, this->ipc_options, stream.get()));
-  ARROW_ASSIGN_OR_RAISE(auto buffer, stream->Finish());
-  py_gil.acquire();
-
-  RETURN_NOT_OK(PushBuffer(buffer));
-
-  // For each tensor, get a metadata buffer and a buffer for the body
-  for (const auto& tensor : this->tensors) {
-    ARROW_ASSIGN_OR_RAISE(std::unique_ptr<ipc::Message> message,
-                          ipc::GetTensorMessage(*tensor, memory_pool));
-    RETURN_NOT_OK(PushBuffer(message->metadata()));
-    RETURN_NOT_OK(PushBuffer(message->body()));
-  }
-
-  // For each sparse tensor, get a metadata buffer and buffers containing index and data
-  for (const auto& sparse_tensor : this->sparse_tensors) {
-    ipc::IpcPayload payload;
-    RETURN_NOT_OK(ipc::GetSparseTensorPayload(*sparse_tensor, memory_pool, &payload));
-    RETURN_NOT_OK(PushBuffer(payload.metadata));
-    for (const auto& body : payload.body_buffers) {
-      RETURN_NOT_OK(PushBuffer(body));
-    }
-  }
-
-  // For each ndarray, get a metadata buffer and a buffer for the body
-  for (const auto& ndarray : this->ndarrays) {
-    ARROW_ASSIGN_OR_RAISE(std::unique_ptr<ipc::Message> message,
-                          ipc::GetTensorMessage(*ndarray, memory_pool));
-    RETURN_NOT_OK(PushBuffer(message->metadata()));
-    RETURN_NOT_OK(PushBuffer(message->body()));
-  }
-
-  for (const auto& buf : this->buffers) {
-    RETURN_NOT_OK(PushBuffer(buf));
-  }
-
-  *out = result.detach();
-  return Status::OK();
-}
-
-}  // namespace py
-}  // namespace arrow
diff --git a/python/pyarrow/src/arrow/python/serialize.h b/python/pyarrow/src/arrow/python/serialize.h
deleted file mode 100644
index af6d2d81a61c..000000000000
--- a/python/pyarrow/src/arrow/python/serialize.h
+++ /dev/null
@@ -1,149 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-#pragma once
-
-#include <memory>
-#include <vector>
-
-#include "arrow/ipc/options.h"
-#include "arrow/python/visibility.h"
-#include "arrow/sparse_tensor.h"
-#include "arrow/status.h"
-#include "arrow/util/macros.h"
-
-// Forward declaring PyObject, see
-// https://mail.python.org/pipermail/python-dev/2003-August/037601.html
-#ifndef PyObject_HEAD
-struct _object;
-typedef _object PyObject;
-#endif
-
-namespace arrow {
-
-class Buffer;
-class DataType;
-class MemoryPool;
-class RecordBatch;
-class Tensor;
-
-namespace io {
-
-class OutputStream;
-
-}  // namespace io
-
-namespace py {
-
-struct ARROW_PYTHON_EXPORT SerializedPyObject {
-  std::shared_ptr<RecordBatch> batch;
-  std::vector<std::shared_ptr<Tensor>> tensors;
-  std::vector<std::shared_ptr<SparseTensor>> sparse_tensors;
-  std::vector<std::shared_ptr<Tensor>> ndarrays;
-  std::vector<std::shared_ptr<Buffer>> buffers;
-  ipc::IpcWriteOptions ipc_options;
-
-  SerializedPyObject();
-
-  /// \brief Write serialized Python object to OutputStream
-  /// \param[in,out] dst an OutputStream
-  /// \return Status
-  Status WriteTo(io::OutputStream* dst);
-
-  /// \brief Convert SerializedPyObject to a dict containing the message
-  /// components as Buffer instances with minimal memory allocation
-  ///
-  /// {
-  ///   'num_tensors': M,
-  ///   'num_sparse_tensors': N,
-  ///   'num_buffers': K,
-  ///   'data': [Buffer]
-  /// }
-  ///
-  /// Each tensor is written as two buffers, one for the metadata and one for
-  /// the body. Therefore, the number of buffers in 'data' is 2 * M + 2 * N + K + 1,
-  /// with the first buffer containing the serialized record batch containing
-  /// the UnionArray that describes the whole object
-  Status GetComponents(MemoryPool* pool, PyObject** out);
-};
-
-/// \brief Serialize Python sequence as a SerializedPyObject.
-/// \param[in] context Serialization context which contains custom serialization
-/// and deserialization callbacks. Can be any Python object with a
-/// _serialize_callback method for serialization and a _deserialize_callback
-/// method for deserialization. If context is None, no custom serialization
-/// will be attempted.
-/// \param[in] sequence A Python sequence object to serialize to Arrow data
-/// structures
-/// \param[out] out The serialized representation
-/// \return Status
-///
-/// Release GIL before calling
-ARROW_DEPRECATED("Deprecated in 18.0.0. Will be removed in 20.0.0")
-ARROW_PYTHON_EXPORT
-Status SerializeObject(PyObject* context, PyObject* sequence, SerializedPyObject* out);
-
-/// \brief Serialize an Arrow Tensor as a SerializedPyObject.
-/// \param[in] tensor Tensor to be serialized
-/// \param[out] out The serialized representation
-/// \return Status
-ARROW_DEPRECATED("Deprecated in 18.0.0. Will be removed in 20.0.0")
-ARROW_PYTHON_EXPORT
-Status SerializeTensor(std::shared_ptr<Tensor> tensor, py::SerializedPyObject* out);
-
-/// \brief Write the Tensor metadata header to an OutputStream.
-/// \param[in] dtype DataType of the Tensor
-/// \param[in] shape The shape of the tensor
-/// \param[in] tensor_num_bytes The length of the Tensor data in bytes
-/// \param[in] dst The OutputStream to write the Tensor header to
-/// \return Status
-ARROW_DEPRECATED("Deprecated in 18.0.0. Will be removed in 20.0.0")
-ARROW_PYTHON_EXPORT
-Status WriteNdarrayHeader(std::shared_ptr<DataType> dtype,
-                          const std::vector<int64_t>& shape, int64_t tensor_num_bytes,
-                          io::OutputStream* dst);
-
-struct PythonType {
-  enum type {
-    NONE,
-    BOOL,
-    INT,
-    PY2INT,  // Kept for compatibility
-    BYTES,
-    STRING,
-    HALF_FLOAT,
-    FLOAT,
-    DOUBLE,
-    DATE64,
-    LIST,
-    DICT,
-    TUPLE,
-    SET,
-    TENSOR,
-    NDARRAY,
-    BUFFER,
-    SPARSECOOTENSOR,
-    SPARSECSRMATRIX,
-    SPARSECSCMATRIX,
-    SPARSECSFTENSOR,
-    NUM_PYTHON_TYPES
-  };
-};
-
-}  // namespace py
-
-}  // namespace arrow


Reviews:
user: login: pitrou
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-11T12:07:36Z
user: login: AlenkaF
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-11T12:18:09Z
user: login: pitrou
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-11T12:18:55Z
user: login: AlenkaF
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-11T12:23:50Z
user: login: pitrou
body: Thank you @AlenkaF !
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-24T14:39:19Z

Review comments:
diff_hunk: @@ -321,183 +321,5 @@ Status DeserializeSet(PyObject* context, const Array& array, int64_t start_idx,
       out);
 }
 
path: python/pyarrow/src/arrow/python/deserialize.cc
user: login: pitrou
body: Is the rest of the file still being used? I would expect us to entirely remove `deserialize.h` and `deserialize.cc`.
created_at: 2025-03-11T12:05:19Z
author_association: MEMBER
diff_hunk: @@ -585,19 +585,6 @@ std::shared_ptr<RecordBatch> MakeBatch(std::shared_ptr<Array> data) {
   return RecordBatch::Make(schema, data->length(), {data});
 }
 
path: python/pyarrow/src/arrow/python/serialize.cc
user: login: pitrou
body: Same question for `serialize.h` and `serialize.cc`.
created_at: 2025-03-11T12:05:55Z
author_association: MEMBER
diff_hunk: @@ -321,183 +321,5 @@ Status DeserializeSet(PyObject* context, const Array& array, int64_t start_idx,
       out);
 }
 
path: python/pyarrow/src/arrow/python/deserialize.cc
user: login: AlenkaF
body: `SparseTensorCounts` is exposed and defined in [libarrow_python.pxd](https://github.com/apache/arrow/blob/b3d218c819283bafe973dc7deb5214324f4a68b2/python/pyarrow/includes/libarrow_python.pxd#L219) though not used anywhere except in the removed `GetSerializedFromComponents`. Should it also be deprecated first?
created_at: 2025-03-11T12:18:09Z
author_association: MEMBER
diff_hunk: @@ -321,183 +321,5 @@ Status DeserializeSet(PyObject* context, const Array& array, int64_t start_idx,
       out);
 }
 
path: python/pyarrow/src/arrow/python/deserialize.cc
user: login: pitrou
body: No, we can remove it as well.
created_at: 2025-03-11T12:18:55Z
author_association: MEMBER
diff_hunk: @@ -321,183 +321,5 @@ Status DeserializeSet(PyObject* context, const Array& array, int64_t start_idx,
       out);
 }
 
path: python/pyarrow/src/arrow/python/deserialize.cc
user: login: AlenkaF
body: It is similar for others so I will remove files altogether.
created_at: 2025-03-11T12:23:50Z
author_association: MEMBER

PR comments:
user: login: pitrou
created_at: 2025-03-11T13:54:38Z
body: @github-actions crossbow submit -g python
author_association: MEMBER
user: login: pitrou
created_at: 2025-03-11T13:55:27Z
body: Thanks for the update @AlenkaF ! We should also remove the corresponding declarations in `pyarrow/includes/libarrow_python.pxd`, no?
author_association: MEMBER
user: login: github-actions[bot]
created_at: 2025-03-11T13:57:23Z
body: Revision: 031ebd21915ba73151849eecdf86df48a639ba93

Submitted crossbow builds: [ursacomputing/crossbow @ actions-593c8c5c15](https://github.com/ursacomputing/crossbow/branches/all?query=actions-593c8c5c15)

|Task|Status|
|----|------|
|example-python-minimal-build-fedora-conda|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-example-python-minimal-build-fedora-conda)](https://github.com/ursacomputing/crossbow/actions/runs/13789824799/job/38566498361)|
|example-python-minimal-build-ubuntu-venv|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-example-python-minimal-build-ubuntu-venv)](https://github.com/ursacomputing/crossbow/actions/runs/13789823990/job/38566492352)|
|test-conda-python-3.10|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.10)](https://github.com/ursacomputing/crossbow/actions/runs/13789824323/job/38566493482)|
|test-conda-python-3.10-hdfs-2.9.2|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.10-hdfs-2.9.2)](https://github.com/ursacomputing/crossbow/actions/runs/13789824577/job/38566496730)|
|test-conda-python-3.10-hdfs-3.2.1|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.10-hdfs-3.2.1)](https://github.com/ursacomputing/crossbow/actions/runs/13789824355/job/38566493701)|
|test-conda-python-3.10-pandas-latest-numpy-latest|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.10-pandas-latest-numpy-latest)](https://github.com/ursacomputing/crossbow/actions/runs/13789824591/job/38566495471)|
|test-conda-python-3.11|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.11)](https://github.com/ursacomputing/crossbow/actions/runs/13789824549/job/38566496254)|
|test-conda-python-3.11-dask-latest|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.11-dask-latest)](https://github.com/ursacomputing/crossbow/actions/runs/13789824583/job/38566496778)|
|test-conda-python-3.11-dask-upstream_devel|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.11-dask-upstream_devel)](https://github.com/ursacomputing/crossbow/actions/runs/13789824734/job/38566498206)|
|test-conda-python-3.11-hypothesis|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.11-hypothesis)](https://github.com/ursacomputing/crossbow/actions/runs/13789824547/job/38566496255)|
|test-conda-python-3.11-pandas-latest-numpy-1.26|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.11-pandas-latest-numpy-1.26)](https://github.com/ursacomputing/crossbow/actions/runs/13789824852/job/38566498243)|
|test-conda-python-3.11-pandas-latest-numpy-latest|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.11-pandas-latest-numpy-latest)](https://github.com/ursacomputing/crossbow/actions/runs/13789825015/job/38566500728)|
|test-conda-python-3.11-pandas-nightly-numpy-nightly|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.11-pandas-nightly-numpy-nightly)](https://github.com/ursacomputing/crossbow/actions/runs/13789824316/job/38566493475)|
|test-conda-python-3.11-pandas-upstream_devel-numpy-nightly|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.11-pandas-upstream_devel-numpy-nightly)](https://github.com/ursacomputing/crossbow/actions/runs/13789824567/job/38566496323)|
|test-conda-python-3.11-spark-master|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.11-spark-master)](https://github.com/ursacomputing/crossbow/actions/runs/13789825006/job/38566500785)|
|test-conda-python-3.12|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.12)](https://github.com/ursacomputing/crossbow/actions/runs/13789824362/job/38566493885)|
|test-conda-python-3.12-cpython-debug|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.12-cpython-debug)](https://github.com/ursacomputing/crossbow/actions/runs/13789824580/job/38566496780)|
|test-conda-python-3.13|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.13)](https://github.com/ursacomputing/crossbow/actions/runs/13789824244/job/38566493383)|
|test-conda-python-3.9|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.9)](https://github.com/ursacomputing/crossbow/actions/runs/13789824629/job/38566498247)|
|test-conda-python-3.9-pandas-1.1.3-numpy-1.19.5|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-3.9-pandas-1.1.3-numpy-1.19.5)](https://github.com/ursacomputing/crossbow/actions/runs/13789824356/job/38566493821)|
|test-conda-python-emscripten|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-conda-python-emscripten)](https://github.com/ursacomputing/crossbow/actions/runs/13789823994/job/38566492355)|
|test-cuda-python-ubuntu-22.04-cuda-11.7.1|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-cuda-python-ubuntu-22.04-cuda-11.7.1)](https://github.com/ursacomputing/crossbow/actions/runs/13789823739/job/38566491675)|
|test-debian-12-python-3-amd64|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-debian-12-python-3-amd64)](https://github.com/ursacomputing/crossbow/actions/runs/13789824579/job/38566495516)|
|test-debian-12-python-3-i386|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-debian-12-python-3-i386)](https://github.com/ursacomputing/crossbow/actions/runs/13789824599/job/38566495495)|
|test-fedora-39-python-3|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-fedora-39-python-3)](https://github.com/ursacomputing/crossbow/actions/runs/13789824552/job/38566496245)|
|test-ubuntu-22.04-python-3|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-ubuntu-22.04-python-3)](https://github.com/ursacomputing/crossbow/actions/runs/13789824971/job/38566500839)|
|test-ubuntu-22.04-python-313-freethreading|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-ubuntu-22.04-python-313-freethreading)](https://github.com/ursacomputing/crossbow/actions/runs/13789824627/job/38566496867)|
|test-ubuntu-24.04-python-3|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-593c8c5c15-github-test-ubuntu-24.04-python-3)](https://github.com/ursacomputing/crossbow/actions/runs/13789824888/job/38566498620)|
author_association: NONE
user: login: AlenkaF
created_at: 2025-03-11T14:20:34Z
body: > Thanks for the update @AlenkaF ! We should also remove the corresponding declarations in `pyarrow/includes/libarrow_python.pxd`, no?

🤦‍♀️  Will do, sorry.
author_association: MEMBER
user: login: AlenkaF
created_at: 2025-03-18T09:26:54Z
body: @github-actions crossbow submit -g python
author_association: MEMBER
user: login: github-actions[bot]
created_at: 2025-03-18T09:29:46Z
body: Revision: a1555923b501d81d2909e3712f8aa7cd8861e10b

Submitted crossbow builds: [ursacomputing/crossbow @ actions-0c97e04e08](https://github.com/ursacomputing/crossbow/branches/all?query=actions-0c97e04e08)

|Task|Status|
|----|------|
|example-python-minimal-build-fedora-conda|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-example-python-minimal-build-fedora-conda)](https://github.com/ursacomputing/crossbow/actions/runs/13919759112/job/38950031640)|
|example-python-minimal-build-ubuntu-venv|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-example-python-minimal-build-ubuntu-venv)](https://github.com/ursacomputing/crossbow/actions/runs/13919760566/job/38950037455)|
|test-conda-python-3.10|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.10)](https://github.com/ursacomputing/crossbow/actions/runs/13919759148/job/38950032022)|
|test-conda-python-3.10-hdfs-2.9.2|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.10-hdfs-2.9.2)](https://github.com/ursacomputing/crossbow/actions/runs/13919759852/job/38950035865)|
|test-conda-python-3.10-hdfs-3.2.1|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.10-hdfs-3.2.1)](https://github.com/ursacomputing/crossbow/actions/runs/13919759670/job/38950034018)|
|test-conda-python-3.10-pandas-latest-numpy-latest|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.10-pandas-latest-numpy-latest)](https://github.com/ursacomputing/crossbow/actions/runs/13919759767/job/38950035138)|
|test-conda-python-3.11|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.11)](https://github.com/ursacomputing/crossbow/actions/runs/13919759225/job/38950032661)|
|test-conda-python-3.11-dask-latest|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.11-dask-latest)](https://github.com/ursacomputing/crossbow/actions/runs/13919759465/job/38950033647)|
|test-conda-python-3.11-dask-upstream_devel|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.11-dask-upstream_devel)](https://github.com/ursacomputing/crossbow/actions/runs/13919759116/job/38950031623)|
|test-conda-python-3.11-hypothesis|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.11-hypothesis)](https://github.com/ursacomputing/crossbow/actions/runs/13919760360/job/38950036991)|
|test-conda-python-3.11-pandas-latest-numpy-1.26|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.11-pandas-latest-numpy-1.26)](https://github.com/ursacomputing/crossbow/actions/runs/13919759803/job/38950035140)|
|test-conda-python-3.11-pandas-latest-numpy-latest|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.11-pandas-latest-numpy-latest)](https://github.com/ursacomputing/crossbow/actions/runs/13919759655/job/38950033823)|
|test-conda-python-3.11-pandas-nightly-numpy-nightly|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.11-pandas-nightly-numpy-nightly)](https://github.com/ursacomputing/crossbow/actions/runs/13919759302/job/38950033606)|
|test-conda-python-3.11-pandas-upstream_devel-numpy-nightly|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.11-pandas-upstream_devel-numpy-nightly)](https://github.com/ursacomputing/crossbow/actions/runs/13919759643/job/38950033294)|
|test-conda-python-3.11-spark-master|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.11-spark-master)](https://github.com/ursacomputing/crossbow/actions/runs/13919760562/job/38950037631)|
|test-conda-python-3.12|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.12)](https://github.com/ursacomputing/crossbow/actions/runs/13919759233/job/38950032527)|
|test-conda-python-3.12-cpython-debug|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.12-cpython-debug)](https://github.com/ursacomputing/crossbow/actions/runs/13919760103/job/38950036672)|
|test-conda-python-3.13|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.13)](https://github.com/ursacomputing/crossbow/actions/runs/13919760359/job/38950037036)|
|test-conda-python-3.9|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.9)](https://github.com/ursacomputing/crossbow/actions/runs/13919759119/job/38950031741)|
|test-conda-python-3.9-pandas-1.1.3-numpy-1.19.5|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-3.9-pandas-1.1.3-numpy-1.19.5)](https://github.com/ursacomputing/crossbow/actions/runs/13919759679/job/38950034041)|
|test-conda-python-emscripten|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-conda-python-emscripten)](https://github.com/ursacomputing/crossbow/actions/runs/13919761116/job/38950038909)|
|test-cuda-python-ubuntu-22.04-cuda-11.7.1|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-cuda-python-ubuntu-22.04-cuda-11.7.1)](https://github.com/ursacomputing/crossbow/actions/runs/13919759667/job/38950033886)|
|test-debian-12-python-3-amd64|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-debian-12-python-3-amd64)](https://github.com/ursacomputing/crossbow/actions/runs/13919759712/job/38950034455)|
|test-debian-12-python-3-i386|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-debian-12-python-3-i386)](https://github.com/ursacomputing/crossbow/actions/runs/13919759121/job/38950031606)|
|test-fedora-39-python-3|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-fedora-39-python-3)](https://github.com/ursacomputing/crossbow/actions/runs/13919759796/job/38950035256)|
|test-ubuntu-22.04-python-3|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-ubuntu-22.04-python-3)](https://github.com/ursacomputing/crossbow/actions/runs/13919759847/job/38950034866)|
|test-ubuntu-22.04-python-313-freethreading|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-ubuntu-22.04-python-313-freethreading)](https://github.com/ursacomputing/crossbow/actions/runs/13919759854/job/38950035858)|
|test-ubuntu-24.04-python-3|[![GitHub Actions](https://github.com/ursacomputing/crossbow/actions/workflows/crossbow.yml/badge.svg?branch=actions-0c97e04e08-github-test-ubuntu-24.04-python-3)](https://github.com/ursacomputing/crossbow/actions/runs/13919760155/job/38950036568)|
author_association: NONE
user: login: conbench-apache-arrow[bot]
created_at: 2025-03-24T21:52:28Z
body: After merging your PR, Conbench analyzed the 4 benchmarking runs that have been run so far on merge-commit 5c0015d34b35b7cb6b5275f7b450f1e580f74d4f.

There were no benchmark performance regressions. 🎉

The [full Conbench report](https://github.com/apache/arrow/runs/39328960889) has more details. It also includes information about 4 possible false positives for unstable benchmarks that are known to sometimes produce them.
author_association: NONE