This is an automated email from the ASF dual-hosted git repository.

raulcd pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/main by this push:
     new b60d43701b GH-48473: [CI][Python] Require numpy 2.0 (#50769)
b60d43701b is described below

commit b60d43701bae097566bda6b96cd514690ca9dc9d
Author: Ádám Lippai <[email protected]>
AuthorDate: Fri Aug 21 13:16:28 2026 -0400

    GH-48473: [CI][Python] Require numpy 2.0 (#50769)
    
    ### Rationale for this change
    Require Numpy 2.0 to support newer features, allow newer numpy API/ABI
    
    ### What changes are included in this PR?
    
    - Numpy 2.0+ requirement
    - Pandas 2.2.2+ requirement (first wheel compatible with numpy 2.0)
    - Pyodide 0.27.1 usage (numpy 2.0 api)
    
    ### Are there any user-facing changes?
    Yes, in contrast to pyarrow 25, after merging this PR pyarrow 26 can't be 
used with Numpy 1.x or pandas 2.0-2.2.1
    
    **This PR includes breaking changes to public APIs.**
    
    This PR was created using GPT-5.6-Sol-xhigh, every line read & reviewed by 
me.
    * GitHub Issue: #48473
    
    Lead-authored-by: Adam Lippai <[email protected]>
    Co-authored-by: Ádám Lippai <[email protected]>
    Co-authored-by: Raúl Cumplido <[email protected]>
    Co-authored-by: Joris Van den Bossche <[email protected]>
    Signed-off-by: Raúl Cumplido <[email protected]>
---
 .github/workflows/python.yml                    | 10 ++---
 ci/conda_env_python.txt                         |  2 +-
 ci/docker/conda-python-emscripten.dockerfile    |  2 +-
 compose.yaml                                    |  2 +-
 cpp/src/arrow/util/float16_test.cc              |  4 +-
 dev/tasks/tasks.yml                             |  4 +-
 docs/source/python/extending_types.rst          |  4 +-
 docs/source/python/install.rst                  |  4 +-
 python/CMakeLists.txt                           | 15 +++-----
 python/benchmarks/common.py                     |  2 +-
 python/pyarrow/array.pxi                        | 33 +++++++---------
 python/pyarrow/feather.py                       |  5 ---
 python/pyarrow/lib.pyx                          |  4 ++
 python/pyarrow/pandas-shim.pxi                  | 50 +++++--------------------
 python/pyarrow/pandas_compat.py                 | 34 +++++------------
 python/pyarrow/src/arrow/python/numpy_interop.h | 25 ++++---------
 python/pyarrow/tests/parquet/test_pandas.py     | 15 +++-----
 python/pyarrow/tests/test_array.py              | 22 +++--------
 python/pyarrow/tests/test_convert_builtin.py    |  2 +-
 python/pyarrow/tests/test_dlpack.py             | 18 ++-------
 python/pyarrow/tests/test_extension_type.py     | 19 ++++------
 python/pyarrow/tests/test_feather.py            | 14 -------
 python/pyarrow/tests/test_io.py                 |  8 +---
 python/pyarrow/tests/test_pandas.py             | 18 ++++-----
 python/pyarrow/tests/test_schema.py             |  2 +-
 python/pyarrow/tests/test_table.py              | 12 +-----
 python/pyarrow/tests/test_types.py              |  4 +-
 python/pyarrow/types.pxi                        |  5 +--
 python/pyproject.toml                           |  2 +-
 python/requirements-build.txt                   |  2 +-
 python/requirements-wheel-test.txt              |  4 +-
 python/setup.cfg                                |  1 -
 32 files changed, 108 insertions(+), 240 deletions(-)

diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml
index 9b35d6f84d..d4710e1158 100644
--- a/.github/workflows/python.yml
+++ b/.github/workflows/python.yml
@@ -67,7 +67,7 @@ jobs:
         name:
           - conda-python-docs
           - conda-python-3.12-nopandas
-          - conda-python-3.11-pandas-2.0.3
+          - conda-python-3.11-pandas-2.2.2-numpy-2.0.2
           - conda-python-3.14-pandas-latest
           - conda-python-3.13-no-numpy
         include:
@@ -79,12 +79,12 @@ jobs:
             image: conda-python
             title: AMD64 Conda Python 3.12 Without Pandas
             python: "3.12"
-          - name: conda-python-3.11-pandas-2.0.3
+          - name: conda-python-3.11-pandas-2.2.2-numpy-2.0.2
             image: conda-python-pandas
-            title: AMD64 Conda Python 3.11 Pandas 2.0.3
+            title: AMD64 Conda Python 3.11 Pandas 2.2.2 NumPy 2.0.2
             python: "3.11"
-            pandas: "2.0.3"
-            numpy: "1.23.2"
+            pandas: "2.2.2"
+            numpy: "2.0.2"
           - name: conda-python-3.14-pandas-latest
             image: conda-python-pandas
             title: AMD64 Conda Python 3.14 Pandas latest
diff --git a/ci/conda_env_python.txt b/ci/conda_env_python.txt
index a0dca4eac1..4602446043 100644
--- a/ci/conda_env_python.txt
+++ b/ci/conda_env_python.txt
@@ -25,7 +25,7 @@ cloudpickle
 fsspec
 hypothesis
 libcst>=1.8.6
-numpy>=1.16.6
+numpy>=2.0
 pytest
 pytest-faulthandler
 s3fs>=2023.10.0
diff --git a/ci/docker/conda-python-emscripten.dockerfile 
b/ci/docker/conda-python-emscripten.dockerfile
index 5154d8c419..e5fb9ff238 100644
--- a/ci/docker/conda-python-emscripten.dockerfile
+++ b/ci/docker/conda-python-emscripten.dockerfile
@@ -22,7 +22,7 @@ ARG python="3.12"
 FROM --platform=linux/${arch} ${repo}:${arch_short}-conda-python-${python}
 
 ARG selenium_version="4.41.0"
-ARG pyodide_version="0.26.0"
+ARG pyodide_version="0.27.1"
 ARG chrome_version="latest"
 ARG required_python_min="(3,12)"
 # fail if python version < 3.12
diff --git a/compose.yaml b/compose.yaml
index dd42cde609..6fa4e6fee5 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -981,7 +981,7 @@ services:
         arch_short: ${ARCH_SHORT}
         clang_tools: ${CLANG_TOOLS}
         llvm: ${LLVM}
-        pyodide_version: "0.26.0"
+        pyodide_version: "0.27.1"
         chrome_version: "latest"
         selenium_version: "4.41.0"
         required_python_min: "(3,12)"
diff --git a/cpp/src/arrow/util/float16_test.cc 
b/cpp/src/arrow/util/float16_test.cc
index 284bf71883..bdf168ed39 100644
--- a/cpp/src/arrow/util/float16_test.cc
+++ b/cpp/src/arrow/util/float16_test.cc
@@ -109,7 +109,7 @@ class Float16ConversionTest : public ::testing::Test {
 
 template <>
 void Float16ConversionTest<float>::TestRoundTrip() {
-  // Expected values were also manually validated with numpy-1.24.3
+  // Expected values were also manually validated with NumPy 1.24.3
   const RoundTripTestCase test_cases[] = {
       // +/-0.0f
       {F32(0x80000000u), 0b1000000000000000u, -0.0f},
@@ -148,7 +148,7 @@ void Float16ConversionTest<float>::TestRoundTrip() {
 
 template <>
 void Float16ConversionTest<double>::TestRoundTrip() {
-  // Expected values were also manually validated with numpy-1.24.3
+  // Expected values were also manually validated with NumPy 1.24.3
   const RoundTripTestCase test_cases[] = {
       // +/-0.0
       {F64(0x8000000000000000u), 0b1000000000000000u, -0.0},
diff --git a/dev/tasks/tasks.yml b/dev/tasks/tasks.yml
index a2230c2fba..523e9a1fbf 100644
--- a/dev/tasks/tasks.yml
+++ b/dev/tasks/tasks.yml
@@ -730,9 +730,9 @@ tasks:
 
   ############################## Integration tests ############################
 
-{% for python_version, pandas_version, numpy_version, cache_leaf in [("3.11", 
"2.0.3", "1.23.2", True),
+{% for python_version, pandas_version, numpy_version, cache_leaf in [("3.11", 
"2.2.2", "2.0.2", True),
                                                                      ("3.12", 
"latest", "latest", False),
-                                                                     ("3.13", 
"latest", "1.26.2", False),
+                                                                     ("3.13", 
"latest", "2.1.3", False),
                                                                      ("3.13", 
"latest", "latest", False),
                                                                      ("3.14", 
"nightly", "nightly", False),
                                                                      ("3.14", 
"upstream_devel", "nightly", False)] %}
diff --git a/docs/source/python/extending_types.rst 
b/docs/source/python/extending_types.rst
index fec04c182a..c38d27f937 100644
--- a/docs/source/python/extending_types.rst
+++ b/docs/source/python/extending_types.rst
@@ -328,9 +328,9 @@ a built-in :class:`ExtensionArray` object. Nevertheless, 
one could want to subcl
 type. Arrow allows to do so by adding a special method ``__arrow_ext_class__`` 
to the
 definition of the extension type.
 
-For instance, let us consider the example from the `Numpy Quickstart 
<https://docs.scipy.org/doc/numpy-1.13.0/user/quickstart.html>`_ of points in 
3D space.
+For instance, let us consider the example from the `NumPy Quickstart 
<https://numpy.org/doc/stable/user/quickstart.html>`_ of points in 3D space.
 We can store these as a fixed-size list, where we wish to be able to extract
-the data as a 2-D Numpy array ``(N, 3)`` without any copy:
+the data as a 2-D NumPy array ``(N, 3)`` without any copy:
 
 .. code-block:: python
 
diff --git a/docs/source/python/install.rst b/docs/source/python/install.rst
index d076ca9643..b57c462f99 100644
--- a/docs/source/python/install.rst
+++ b/docs/source/python/install.rst
@@ -73,8 +73,8 @@ Dependencies
 
 Optional dependencies
 
-* **NumPy 1.23.2** or higher.
-* **pandas 2.0.3** or higher,
+* **NumPy 2.0** or higher.
+* **pandas 2.2.2** or higher,
 * **cffi**.
 
 Additional packages PyArrow is compatible with are :ref:`fsspec 
<filesystem-fsspec>`
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index 408153bb43..4bea6e7d8d 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -167,16 +167,7 @@ if($ENV{PYODIDE})
   set(Python3_INCLUDE_DIR $ENV{PYTHONINCLUDE})
   set(Python3_LIBRARY $ENV{CPYTHONLIB})
   set(Python3_EXECUTABLE)
-  execute_process(COMMAND ${Python3_EXECUTABLE} -c
-                          "import numpy; print(numpy.__version__)"
-                  OUTPUT_VARIABLE PYODIDE_NUMPY_VERSION
-                  OUTPUT_STRIP_TRAILING_WHITESPACE)
-  string(REGEX MATCH "^([0-9]+)" PYODIDE_NUMPY_MAJOR_VERSION 
${PYODIDE_NUMPY_VERSION})
-  if(PYODIDE_NUMPY_MAJOR_VERSION GREATER_EQUAL 2)
-    set(Python3_NumPy_INCLUDE_DIR $ENV{NUMPY_LIB}/_core/include)
-  else()
-    set(Python3_NumPy_INCLUDE_DIR $ENV{NUMPY_LIB}/core/include)
-  endif()
+  set(Python3_NumPy_INCLUDE_DIR $ENV{NUMPY_LIB}/_core/include)
   set(ENV{_PYTHON_SYSCONFIGDATA_NAME} $ENV{SYSCONFIG_NAME})
   # we set the c and cxx compiler manually to bypass pywasmcross
   # which is pyodide's way of messing with C++ build parameters.
@@ -278,6 +269,10 @@ set(EXECUTABLE_OUTPUT_PATH 
"${BUILD_OUTPUT_ROOT_DIRECTORY}")
 
 # Python and Numpy libraries
 find_package(Python3Alt REQUIRED)
+if(Python3_NumPy_VERSION AND Python3_NumPy_VERSION VERSION_LESS "2.0")
+  message(FATAL_ERROR "PyArrow requires NumPy 2.0 or newer, found 
${Python3_NumPy_VERSION}"
+  )
+endif()
 message(STATUS "Found NumPy version: ${Python3_NumPy_VERSION}")
 message(STATUS "NumPy include dir: ${NUMPY_INCLUDE_DIRS}")
 
diff --git a/python/benchmarks/common.py b/python/benchmarks/common.py
index 8317ff3171..4e27b22295 100644
--- a/python/benchmarks/common.py
+++ b/python/benchmarks/common.py
@@ -149,7 +149,7 @@ class BuiltinsGenerator(object):
         Generate a list of Python bools with *none_prob* probability of
         an entry being None.
         """
-        # Make sure we get Python bools, not np.bool_
+        # Make sure we get Python bools, not np.bool
         data = [bool(x >= 0.5) for x in self.rnd.uniform(0.0, 1.0, n)]
         assert len(data) == n
         self.sprinkle_nones(data, none_prob)
diff --git a/python/pyarrow/array.pxi b/python/pyarrow/array.pxi
index d8bbd001fd..e31feb1cb0 100644
--- a/python/pyarrow/array.pxi
+++ b/python/pyarrow/array.pxi
@@ -342,7 +342,7 @@ def array(object obj, type=None, mask=None, size=None, 
from_pandas=None,
                 values = values.data
 
         if mask is not None:
-            if mask.dtype != np.bool_:
+            if mask.dtype != np.bool:
                 raise TypeError("Mask must be boolean dtype")
             if mask.ndim != 1:
                 raise ValueError("Mask must be 1D array")
@@ -945,14 +945,12 @@ cdef class _PandasConvertible(_Weakrefable):
             Cast integers with nulls to objects
         date_as_object : bool, default True
             Cast dates to objects. If False, convert to datetime64 dtype with
-            the equivalent time unit (if supported). Note: in pandas version
-            < 2.0, only datetime64[ns] conversion is supported.
+            the equivalent time unit (if supported).
         timestamp_as_object : bool, default False
-            Cast non-nanosecond timestamps (np.datetime64) to objects. This is
-            useful in pandas version 1.x if you have timestamps that don't fit
-            in the normal date range of nanosecond timestamps (1678 CE-2262 
CE).
-            Non-nanosecond timestamps are supported in pandas version 2.0.
-            If False, all timestamps are converted to datetime64 dtype.
+            Cast non-nanosecond timestamps (np.datetime64) to objects. This can
+            be useful when Python datetime objects are required, such as for
+            compatibility with code expecting object dtype. If False, all
+            timestamps are converted to datetime64 dtype.
         use_threads : bool, default True
             Whether to parallelize the conversion using multiple threads.
         deduplicate_objects : bool, default True
@@ -963,9 +961,8 @@ cdef class _PandasConvertible(_Weakrefable):
             DataFrame index, if present
         safe : bool, default True
             For certain data types, a cast is needed in order to store the
-            data in a pandas DataFrame or Series (e.g. timestamps are always
-            stored as nanoseconds in pandas). This option controls whether it
-            is a safe cast or not.
+            data in a pandas DataFrame or Series. This option controls whether
+            it is a safe cast or not.
         split_blocks : bool, default False
             If True, generate one internal "block" for each column when
             creating a pandas.DataFrame from a RecordBatch or Table. While this
@@ -1002,12 +999,10 @@ cdef class _PandasConvertible(_Weakrefable):
             default conversion should be used for that type. If you have
             a dictionary mapping, you can pass ``dict.get`` as function.
         coerce_temporal_nanoseconds : bool, default False
-            Only applicable to pandas version >= 2.0.
             A legacy option to coerce date32, date64, duration, and timestamp
-            time units to nanoseconds when converting to pandas. This is the
-            default behavior in pandas version 1.x. Set this option to True if
-            you'd like to use this coercion when using pandas version >= 2.0
-            for backwards compatibility (not recommended otherwise).
+            time units to nanoseconds when converting to pandas. Set this
+            option only if nanosecond coercion is required for compatibility
+            with older application behavior.
 
         Returns
         -------
@@ -5130,7 +5125,7 @@ cdef class Bool8Array(ExtensionArray):
         """
         if not writable:
             try:
-                return self.storage.to_numpy().view(np.bool_)
+                return self.storage.to_numpy().view(np.bool)
             except ArrowInvalid as e:
                 if zero_copy_only:
                     raise e
@@ -5171,7 +5166,7 @@ cdef class Bool8Array(ExtensionArray):
         --------
         >>> import pyarrow as pa
         >>> import numpy as np
-        >>> arr = np.array([True, False, True], dtype=np.bool_)
+        >>> arr = np.array([True, False, True], dtype=np.bool)
         >>> pa.Bool8Array.from_numpy(arr)
         <pyarrow.lib.Bool8Array object at ...>
         [
@@ -5184,7 +5179,7 @@ cdef class Bool8Array(ExtensionArray):
         if obj.ndim != 1:
             raise ValueError(f"Cannot convert {obj.ndim}-D array to bool8 
array")
 
-        if obj.dtype not in [np.bool_, np.int8]:
+        if obj.dtype not in [np.bool, np.int8]:
             raise TypeError(f"Array dtype {obj.dtype} incompatible with bool8 
storage")
 
         storage_arr = array(obj.view(np.int8), type=int8())
diff --git a/python/pyarrow/feather.py b/python/pyarrow/feather.py
index 60d59b0e0b..effe30ba9b 100644
--- a/python/pyarrow/feather.py
+++ b/python/pyarrow/feather.py
@@ -150,11 +150,6 @@ def write_feather(df, dest, compression=None, 
compression_level=None,
             DeprecationWarning,
             stacklevel=2
         )
-    if _pandas_api.have_pandas:
-        if (_pandas_api.has_sparse and
-                isinstance(df, _pandas_api.pd.SparseDataFrame)):
-            df = df.to_dense()
-
     if _pandas_api.is_data_frame(df):
         # Feather v1 creates a new column in the resultant Table to
         # store index information if index type is not RangeIndex
diff --git a/python/pyarrow/lib.pyx b/python/pyarrow/lib.pyx
index 7e97177a6e..d949720ca8 100644
--- a/python/pyarrow/lib.pyx
+++ b/python/pyarrow/lib.pyx
@@ -38,6 +38,10 @@ cimport cpython as cp
 
 # Initialize NumPy C API only if numpy was able to be imported
 if np is not None:
+    if int(np.__version__.partition('.')[0]) < 2:
+        raise ImportError(
+            f"pyarrow requires NumPy 2.0 or newer, found {np.__version__}"
+        )
     arrow_init_numpy()
 
 # Initialize PyArrow C++ API
diff --git a/python/pyarrow/pandas-shim.pxi b/python/pyarrow/pandas-shim.pxi
index 94802da6b0..603b2b09d4 100644
--- a/python/pyarrow/pandas-shim.pxi
+++ b/python/pyarrow/pandas-shim.pxi
@@ -35,10 +35,8 @@ cdef class _PandasAPIShim(object):
         object _pd, _types_api, _compat_module
         object _data_frame, _index, _series, _categorical_type
         object _datetimetz_type, _extension_array, _extension_dtype
-        object _array_like_types, _is_extension_array_dtype, _lock
-        bint has_sparse
-        bint _pd024
-        bint _is_ge_v21, _is_ge_v23, _is_ge_v3, _is_ge_v3_strict
+        object _array_like_types, _lock
+        bint _is_ge_v23, _is_ge_v3, _is_ge_v3_strict
 
     def __init__(self):
         self._lock = Lock()
@@ -62,23 +60,22 @@ cdef class _PandasAPIShim(object):
         self._version = pd.__version__
         self._loose_version = Version(pd.__version__)
 
-        if self._loose_version < Version('2.0.3'):
+        if self._loose_version < Version('2.2.2'):
             self._have_pandas = False
             if raise_:
                 raise ImportError(
-                    f"pyarrow requires pandas 2.0.3 or above, pandas 
{self._version} is "
+                    f"pyarrow requires pandas 2.2.2 or above, pandas 
{self._version} is "
                     "installed"
                 )
             else:
                 warnings.warn(
-                    f"pyarrow requires pandas 2.0.3 or above, pandas 
{self._version} is "
+                    f"pyarrow requires pandas 2.2.2 or above, pandas 
{self._version} is "
                     "installed. Therefore, pandas-specific integration is not "
                     "used.",
                     stacklevel=2
                 )
                 return
 
-        self._is_ge_v21 = self._loose_version >= Version('2.1.0')
         self._is_ge_v23 = self._loose_version >= Version('2.3.0.dev0')
         self._is_ge_v3 = self._loose_version >= Version('3.0.0.dev0')
         self._is_ge_v3_strict = self._loose_version >= Version('3.0.0')
@@ -93,12 +90,9 @@ cdef class _PandasAPIShim(object):
             self._series, self._index, self._categorical_type,
             self._extension_array)
         self._extension_dtype = pd.api.extensions.ExtensionDtype
-        self._is_extension_array_dtype = (
-            pd.api.types.is_extension_array_dtype)
         self._types_api = pd.api.types
         self._datetimetz_type = pd.api.types.DatetimeTZDtype
         self._have_pandas = True
-        self.has_sparse = False
 
     cdef inline _check_import(self, bint raise_=True):
         if not self._tried_importing_pandas:
@@ -142,17 +136,11 @@ cdef class _PandasAPIShim(object):
 
     cpdef infer_dtype(self, obj):
         self._check_import()
-        try:
-            return self._types_api.infer_dtype(obj, skipna=False)
-        except AttributeError:
-            return self._pd.lib.infer_dtype(obj)
+        return self._types_api.infer_dtype(obj, skipna=False)
 
     cpdef pandas_dtype(self, dtype):
         self._check_import()
-        try:
-            return self._types_api.pandas_dtype(dtype)
-        except AttributeError:
-            return None
+        return self._types_api.pandas_dtype(dtype)
 
     @property
     def loose_version(self):
@@ -164,10 +152,6 @@ cdef class _PandasAPIShim(object):
         self._check_import()
         return self._version
 
-    def is_ge_v21(self):
-        self._check_import()
-        return self._is_ge_v21
-
     def is_ge_v23(self):
         self._check_import()
         return self._is_ge_v23
@@ -183,12 +167,7 @@ cdef class _PandasAPIShim(object):
     def uses_string_dtype(self):
         if self.is_ge_v3_strict():
             return True
-        try:
-            if self.is_ge_v23() and self.pd.options.future.infer_string:
-                return True
-        except:
-            pass
-        return False
+        return self.is_ge_v23() and self.pd.options.future.infer_string
 
     @property
     def categorical_type(self):
@@ -223,10 +202,7 @@ cdef class _PandasAPIShim(object):
 
     cpdef is_extension_array_dtype(self, obj):
         self._check_import()
-        if self._is_extension_array_dtype:
-            return self._is_extension_array_dtype(obj)
-        else:
-            return False
+        return self._types_api.is_extension_array_dtype(obj)
 
     cpdef is_sparse(self, obj):
         if self._have_pandas_internal():
@@ -265,13 +241,5 @@ cdef class _PandasAPIShim(object):
             return obj.array
         return obj.values
 
-    def get_rangeindex_attribute(self, level, name):
-        # public start/stop/step attributes added in pandas 0.25.0
-        self._check_import()
-        if hasattr(level, name):
-            return getattr(level, name)
-        return getattr(level, '_' + name)
-
-
 cdef _PandasAPIShim pandas_api = _PandasAPIShim()
 _pandas_api = pandas_api
diff --git a/python/pyarrow/pandas_compat.py b/python/pyarrow/pandas_compat.py
index ccb89fc05d..36d336d513 100644
--- a/python/pyarrow/pandas_compat.py
+++ b/python/pyarrow/pandas_compat.py
@@ -93,7 +93,7 @@ def get_numpy_logical_type_map():
     global _numpy_logical_type_map
     if not _numpy_logical_type_map:
         _numpy_logical_type_map.update({
-            np.bool_: 'bool',
+            np.bool: 'bool',
             np.int8: 'int8',
             np.int16: 'int16',
             np.int32: 'int32',
@@ -277,7 +277,7 @@ def construct_metadata(columns_to_convert, df, 
column_names, index_levels,
     else:
         index_descriptors = index_column_metadata = column_indexes = []
 
-    attributes = df.attrs if hasattr(df, "attrs") else {}
+    attributes = df.attrs
 
     try:
         json.dumps(attributes)
@@ -537,13 +537,12 @@ def _level_name(name):
 
 
 def _get_range_index_descriptor(level):
-    # public start/stop/step attributes added in pandas 0.25.0
     return {
         'kind': 'range',
         'name': _level_name(level.name),
-        'start': _pandas_api.get_rangeindex_attribute(level, 'start'),
-        'stop': _pandas_api.get_rangeindex_attribute(level, 'stop'),
-        'step': _pandas_api.get_rangeindex_attribute(level, 'step')
+        'start': level.start,
+        'stop': level.stop,
+        'step': level.step
     }
 
 
@@ -759,17 +758,9 @@ def _reconstruct_block(item, columns=None, 
extension_columns=None, return_block=
     elif 'timezone' in item:
         unit, _ = np.datetime_data(block_arr.dtype)
         dtype = make_datetimetz(unit, item['timezone'])
-        if _pandas_api.is_ge_v21():
-            arr = _pandas_api.pd.array(
-                block_arr.view("int64"), dtype=dtype, copy=False
-            )
-        else:
-            arr = block_arr
-            if return_block:
-                block = _int.make_block(block_arr, placement=placement,
-                                        klass=_int.DatetimeTZBlock,
-                                        dtype=dtype)
-                return block
+        arr = _pandas_api.pd.array(
+            block_arr.view("int64"), dtype=dtype, copy=False
+        )
     elif 'py_array' in item:
         # create ExtensionBlock
         arr = item['py_array']
@@ -846,10 +837,7 @@ def table_to_dataframe(
         ]
         axes = [columns, index]
         mgr = BlockManager(blocks, axes)
-        if _pandas_api.is_ge_v21():
-            df = DataFrame._from_mgr(mgr, mgr.axes)
-        else:
-            df = DataFrame(mgr)
+        df = DataFrame._from_mgr(mgr, mgr.axes)
 
         df.attrs = attributes
 
@@ -884,10 +872,6 @@ def _get_extension_dtypes(table, columns_metadata, 
types_mapper, options, catego
 
     ext_columns = {}
 
-    # older pandas version that does not yet support extension dtypes
-    if _pandas_api.extension_dtype is None:
-        return ext_columns
-
     # use the specified mapping of built-in arrow types to pandas dtypes
     if types_mapper:
         for field in table.schema:
diff --git a/python/pyarrow/src/arrow/python/numpy_interop.h 
b/python/pyarrow/src/arrow/python/numpy_interop.h
index a83ae4a62b..40e02d5bec 100644
--- a/python/pyarrow/src/arrow/python/numpy_interop.h
+++ b/python/pyarrow/src/arrow/python/numpy_interop.h
@@ -19,16 +19,14 @@
 
 #include "arrow/python/platform.h"  // IWYU pragma: export
 
+// Require the NumPy 2.0 C API and hide deprecated APIs.
+#define NPY_TARGET_VERSION NPY_2_0_API_VERSION
+#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION
+
 #include <numpy/numpyconfig.h>  // IWYU pragma: export
 
-// Don't use the deprecated Numpy functions
-#ifdef NPY_1_7_API_VERSION
-#  define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#else
-#  define NPY_ARRAY_NOTSWAPPED NPY_NOTSWAPPED
-#  define NPY_ARRAY_ALIGNED NPY_ALIGNED
-#  define NPY_ARRAY_WRITEABLE NPY_WRITEABLE
-#  define NPY_ARRAY_UPDATEIFCOPY NPY_UPDATEIFCOPY
+#if NPY_ABI_VERSION < 0x02000000
+#  error "PyArrow requires NumPy 2.0 or newer"
 #endif
 
 // This is required to be able to access the NumPy C API properly in C++ files
@@ -67,20 +65,13 @@
 #  define NPY_INT32_IS_INT 0
 #endif
 
-// Backported NumPy 2 API (can be removed if numpy 2 is required)
-#if NPY_ABI_VERSION < 0x02000000
-#  define PyDataType_ELSIZE(descr) ((descr)->elsize)
-#  define PyDataType_C_METADATA(descr) ((descr)->c_metadata)
-#  define PyDataType_FIELDS(descr) ((descr)->fields)
-#endif
-
 namespace arrow {
 namespace py {
 
 inline int import_numpy() {
 #ifdef NUMPY_IMPORT_ARRAY
-  import_array1(-1);
-  import_umath1(-1);
+  if (PyArray_ImportNumPyAPI() < 0) return -1;
+  if (PyUFunc_ImportUFuncAPI() < 0) return -1;
 #endif
 
   return 0;
diff --git a/python/pyarrow/tests/parquet/test_pandas.py 
b/python/pyarrow/tests/parquet/test_pandas.py
index d12b43f4e0..f919770ee9 100644
--- a/python/pyarrow/tests/parquet/test_pandas.py
+++ b/python/pyarrow/tests/parquet/test_pandas.py
@@ -27,7 +27,6 @@ import pytest
 import pyarrow as pa
 from pyarrow.fs import LocalFileSystem, SubTreeFileSystem
 from pyarrow.util import guid
-from pyarrow.vendored.version import Version
 
 try:
     import pyarrow.parquet as pq
@@ -430,22 +429,20 @@ carat        cut  color  clarity  depth  table  price     
x     y     z
 
 @pytest.mark.pandas
 def test_backwards_compatible_column_metadata_handling(datadir):
-    if Version("2.2.0") <= Version(pd.__version__):
-        # TODO: regression in pandas
-        # https://github.com/pandas-dev/pandas/issues/56775
-        pytest.skip("Regression in pandas 2.2.0")
+    dates = pd.date_range(
+        "2017-01-01", periods=3, tz='Europe/Brussels'
+    ).as_unit("ns")
     expected = pd.DataFrame(
         {'a': [1, 2, 3], 'b': [.1, .2, .3],
-         'c': pd.date_range("2017-01-01", periods=3, tz='Europe/Brussels')})
+         'c': dates})
     expected.index = pd.MultiIndex.from_arrays(
-        [['a', 'b', 'c'],
-         pd.date_range("2017-01-01", periods=3, tz='Europe/Brussels')],
+        [['a', 'b', 'c'], dates],
         names=['index', None])
 
     path = datadir / 'v0.7.1.column-metadata-handling.parquet'
     table = _read_table(path)
     result = table.to_pandas()
-    tm.assert_frame_equal(result, expected)
+    tm.assert_frame_equal(result, expected, check_freq=False)
 
     table = _read_table(
         path, columns=['a'])
diff --git a/python/pyarrow/tests/test_array.py 
b/python/pyarrow/tests/test_array.py
index bc4e521dca..a1e3616c9c 100644
--- a/python/pyarrow/tests/test_array.py
+++ b/python/pyarrow/tests/test_array.py
@@ -34,7 +34,6 @@ except ImportError:
 
 import pyarrow as pa
 import pyarrow.tests.strategies as past
-from pyarrow.vendored.version import Version
 import pyarrow.compute as pc
 
 
@@ -3800,21 +3799,12 @@ def test_numpy_array_protocol():
     result = np.asarray(arr)
     np.testing.assert_array_equal(result, expected)
 
-    if Version(np.__version__) < Version("2.0.0.dev0"):
-        # copy keyword is not strict and not passed down to __array__
-        result = np.array(arr, copy=False)
-        np.testing.assert_array_equal(result, expected)
+    with pytest.raises(ValueError, match="Unable to avoid a copy"):
+        np.array(arr, copy=False)
 
-        result = np.array(arr, dtype="float64", copy=False)
-        np.testing.assert_array_equal(result, expected)
-    else:
-        # starting with numpy 2.0, the copy=False keyword is assumed to be 
strict
-        with pytest.raises(ValueError, match="Unable to avoid a copy"):
-            np.array(arr, copy=False)
-
-        arr = pa.array([1, 2, 3])
-        with pytest.raises(ValueError):
-            np.array(arr, dtype="float64", copy=False)
+    arr = pa.array([1, 2, 3])
+    with pytest.raises(ValueError):
+        np.array(arr, dtype="float64", copy=False)
 
     # copy=True -> not yet passed by numpy, so we have to call this directly 
to test
     arr = pa.array([1, 2, 3])
@@ -4436,7 +4426,7 @@ def test_non_cpu_array():
     ctx = cuda.Context(0)
 
     data = np.arange(4, dtype=np.int32)
-    validity = np.array([True, False, True, False], dtype=np.bool_)
+    validity = np.array([True, False, True, False], dtype=np.bool)
     cuda_data_buf = ctx.buffer_from_data(data)
     cuda_validity_buf = ctx.buffer_from_data(validity)
     arr = pa.Array.from_buffers(pa.int32(), 4, [None, cuda_data_buf])
diff --git a/python/pyarrow/tests/test_convert_builtin.py 
b/python/pyarrow/tests/test_convert_builtin.py
index bb2813f3b5..3ac7be88ea 100644
--- a/python/pyarrow/tests/test_convert_builtin.py
+++ b/python/pyarrow/tests/test_convert_builtin.py
@@ -240,7 +240,7 @@ def test_sequence_boolean(seq):
 @pytest.mark.numpy
 @parametrize_with_sequence_types
 def test_sequence_numpy_boolean(seq):
-    expected = [np.bool_(True), None, np.bool_(False), None]
+    expected = [np.bool(True), None, np.bool(False), None]
     arr = pa.array(seq(expected))
     assert arr.type == pa.bool_()
     assert arr.to_pylist() == [True, None, False, None]
diff --git a/python/pyarrow/tests/test_dlpack.py 
b/python/pyarrow/tests/test_dlpack.py
index 09a510122f..7f5f98d866 100644
--- a/python/pyarrow/tests/test_dlpack.py
+++ b/python/pyarrow/tests/test_dlpack.py
@@ -92,11 +92,6 @@ def check_bytes_allocated(f):
     ]
 )
 def test_dlpack(value_type, np_type_str):
-    if Version(np.__version__) < Version("1.24.0"):
-        pytest.skip("No dlpack support in numpy versions older than 1.22.0, "
-                    "strict keyword in assert_array_equal added in numpy 
version "
-                    "1.24.0")
-
     expected = np.array([1, 2, 3], dtype=np.dtype(np_type_str))
     arr = pa.array(expected, type=value_type)
     check_dlpack_export(arr, expected)
@@ -130,11 +125,6 @@ def test_dlpack(value_type, np_type_str):
                           np.int8, np.int16, np.int32, np.int64,
                           np.float16, np.float32, np.float64,])
 def test_tensor_dlpack(np_type):
-    if Version(np.__version__) < Version("1.24.0"):
-        pytest.skip("No dlpack support in numpy versions older than 1.22.0, "
-                    "strict keyword in assert_array_equal added in numpy 
version "
-                    "1.24.0")
-
     arr = np.array([1, 2, 3, 4, 5, 6, 1, 1])
     expected = np.array(arr, dtype=np_type).reshape((2, 2, 2), order='C')
     t = pa.Tensor.from_numpy(expected)
@@ -227,8 +217,9 @@ def test_dlpack_versioned_roundtrip(obj):
 
 @check_bytes_allocated
 def test_dlpack_copy_is_writeable():
-    if Version(np.__version__) < Version("2.1.0"):
-        pytest.skip("Read-only DLPack flag requires numpy 2.1.0 or later")
+    # NumPy did not set the writeable flag on DLPack imports before 2.2.5.
+    if Version(np.__version__) < Version("2.2.5"):
+        pytest.skip("Writable DLPack imports require numpy 2.2.5 or later")
 
     arr = pa.array([1, 2, 3], type=pa.int32())
 
@@ -244,9 +235,6 @@ def test_dlpack_copy_is_writeable():
 
 
 def test_dlpack_not_supported():
-    if Version(np.__version__) < Version("1.22.0"):
-        pytest.skip("No dlpack support in numpy versions older than 1.22.0.")
-
     arr = pa.array([1, None, 3])
     with pytest.raises(TypeError, match="Can only use DLPack "
                        "on arrays with no nulls."):
diff --git a/python/pyarrow/tests/test_extension_type.py 
b/python/pyarrow/tests/test_extension_type.py
index f98bd4dfe5..bdd898767b 100644
--- a/python/pyarrow/tests/test_extension_type.py
+++ b/python/pyarrow/tests/test_extension_type.py
@@ -31,7 +31,6 @@ except ImportError:
     np = None
 
 import pyarrow as pa
-from pyarrow.vendored.version import Version
 
 
 @contextlib.contextmanager
@@ -1965,13 +1964,9 @@ def 
test_extension_to_pandas_storage_type(registered_period_type):
     assert result["ext"].dtype == pandas_dtype
 
     import pandas as pd
-    # Skip tests for 2.0.x, See: GH-35821
-    if (
-        Version(pd.__version__) >= Version("2.1.0")
-    ):
-        # Check the usage of types_mapper
-        result = table.to_pandas(types_mapper=pd.ArrowDtype)
-        assert isinstance(result["ext"].dtype, pd.ArrowDtype)
+    # Check the usage of types_mapper
+    result = table.to_pandas(types_mapper=pd.ArrowDtype)
+    assert isinstance(result["ext"].dtype, pd.ArrowDtype)
 
 
 def test_tensor_type_is_picklable(pickle_module):
@@ -2127,7 +2122,7 @@ def test_bool8_to_numpy_conversion():
     )
 
     # zero-copy possible with non-null array
-    np_arr_no_nulls = np.array([True, False, True, True], dtype=np.bool_)
+    np_arr_no_nulls = np.array([True, False, True, True], dtype=np.bool)
     arr_no_nulls = pa.ExtensionArray.from_storage(
         pa.bool8(),
         pa.array([-1, 0, 1, 2], pa.int8()),
@@ -2149,7 +2144,7 @@ def test_bool8_to_numpy_conversion():
 
 @pytest.mark.numpy
 def test_bool8_from_numpy_conversion():
-    np_arr_no_nulls = np.array([True, False, True, True], dtype=np.bool_)
+    np_arr_no_nulls = np.array([True, False, True, True], dtype=np.bool)
     canonical_bool8_arr_no_nulls = pa.ExtensionArray.from_storage(
         pa.bool8(),
         pa.array([1, 0, 1, 1], pa.int8()),
@@ -2167,14 +2162,14 @@ def test_bool8_from_numpy_conversion():
         match="Cannot convert 2-D array to bool8 array",
     ):
         pa.Bool8Array.from_numpy(
-            np.array([[True, False], [False, True]], dtype=np.bool_),
+            np.array([[True, False], [False, True]], dtype=np.bool),
         )
 
     with pytest.raises(
         ValueError,
         match="Cannot convert 0-D array to bool8 array",
     ):
-        pa.Bool8Array.from_numpy(np.bool_())
+        pa.Bool8Array.from_numpy(np.bool())
 
     # must use compatible storage type
     with pytest.raises(
diff --git a/python/pyarrow/tests/test_feather.py 
b/python/pyarrow/tests/test_feather.py
index 8c9e7eb437..d9cee7a31d 100644
--- a/python/pyarrow/tests/test_feather.py
+++ b/python/pyarrow/tests/test_feather.py
@@ -588,20 +588,6 @@ def test_filelike_objects(version):
     assert_frame_equal(result, df)
 
 
[email protected]
[email protected]("ignore:Sparse:FutureWarning")
[email protected]("ignore:DataFrame.to_sparse:FutureWarning")
-def test_sparse_dataframe(version):
-    if not pa.pandas_compat._pandas_api.has_sparse:
-        pytest.skip("version of pandas does not support SparseDataFrame")
-    # GH #221
-    data = {'A': [0, 1, 2],
-            'B': [1, 0, 1]}
-    df = pd.DataFrame(data).to_sparse(fill_value=1)
-    expected = df.to_dense()
-    _check_pandas_roundtrip(df, expected, version=version)
-
-
 @pytest.mark.pandas
 def test_duplicate_columns_pandas():
 
diff --git a/python/pyarrow/tests/test_io.py b/python/pyarrow/tests/test_io.py
index 3d4ba997b3..8494a0ee66 100644
--- a/python/pyarrow/tests/test_io.py
+++ b/python/pyarrow/tests/test_io.py
@@ -917,16 +917,12 @@ def test_compression_level(compression):
         with pytest.raises(ValueError):
             codec.decompress(compressed_bytes)
 
-    # The ability to set a seed this way is not present on older versions of
-    # numpy (currently in our python 3.6 CI build).  Some inputs might just
-    # happen to compress the same between the two levels so using seeded
-    # random numbers is necessary to help get more reliable results
+    # Some inputs might just happen to compress the same between the two 
levels,
+    # so using seeded random numbers makes the results more reliable.
     #
     # The goal of this part is to ensure the compression_level is being
     # passed down to the C++ layer, not to verify the compression algs
     # themselves
-    if not hasattr(np.random, 'default_rng'):
-        pytest.skip('Requires newer version of numpy')
     rng = np.random.default_rng(seed=42)
     values = rng.integers(0, 100, 1000)
     arr = pa.array(values)
diff --git a/python/pyarrow/tests/test_pandas.py 
b/python/pyarrow/tests/test_pandas.py
index 0f35edd516..dd20a0aa97 100644
--- a/python/pyarrow/tests/test_pandas.py
+++ b/python/pyarrow/tests/test_pandas.py
@@ -254,13 +254,13 @@ class TestConvertMetadata:
         result = table.to_pandas()
         tm.assert_frame_equal(result, df)
         assert isinstance(result.index, pd.RangeIndex)
-        assert _pandas_api.get_rangeindex_attribute(result.index, 'step') == 2
+        assert result.index.step == 2
         assert result.index.name == index_name
 
         result2 = table_no_index_name.to_pandas()
         tm.assert_frame_equal(result2, df2)
         assert isinstance(result2.index, pd.RangeIndex)
-        assert _pandas_api.get_rangeindex_attribute(result2.index, 'step') == 1
+        assert result2.index.step == 1
         assert result2.index.name is None
 
     def test_range_index_force_serialization(self):
@@ -1686,10 +1686,6 @@ class TestConvertDateTimeLikeTypes:
         expected = pd.Series([None, date(1991, 1, 1), None])
         assert pa.Array.from_pandas(expected).equals(result)
 
-    @pytest.mark.skipif(
-        np is not None and Version('1.16.0') <= Version(
-            np.__version__) < Version('1.16.1'),
-        reason='Until numpy/numpy#12745 is resolved')
     def test_fixed_offset_timezone(self):
         df = pd.DataFrame({
             'a': [
@@ -2783,7 +2779,7 @@ class TestConvertStructTypes:
 
     def test_from_numpy(self):
         dt = np.dtype([('x', np.int32),
-                       (('y_title', 'y'), np.bool_)])
+                       (('y_title', 'y'), np.bool)])
         ty = pa.struct([pa.field('x', pa.int32()),
                         pa.field('y', pa.bool_())])
 
@@ -2797,7 +2793,7 @@ class TestConvertStructTypes:
                                    {'x': 43, 'y': False}]
 
         # With mask
-        arr = pa.array(data, mask=np.bool_([False, True]), type=ty)
+        arr = pa.array(data, mask=np.bool([False, True]), type=ty)
         assert arr.to_pylist() == [{'x': 42, 'y': True}, None]
 
         # Trivial struct type
@@ -2815,7 +2811,7 @@ class TestConvertStructTypes:
     def test_from_numpy_nested(self):
         # Note: an object field inside a struct
         dt = np.dtype([('x', np.dtype([('xx', np.int8),
-                                       ('yy', np.bool_)])),
+                                       ('yy', np.bool)])),
                        ('y', np.int16),
                        ('z', np.object_)])
         # Note: itemsize is not necessarily a multiple of sizeof(object)
@@ -2899,7 +2895,7 @@ class TestConvertStructTypes:
         ty = pa.struct([pa.field('x', pa.int32()),
                         pa.field('y', pa.bool_())])
         dt = np.dtype([('x', np.int32),
-                       ('z', np.bool_)])
+                       ('z', np.bool)])
 
         data = np.array([], dtype=dt)
         with pytest.raises(ValueError,
@@ -3859,7 +3855,7 @@ def test_array_uses_memory_pool():
     # ARROW-6570
     N = 10000
     arr = pa.array(np.arange(N, dtype=np.int64),
-                   mask=np.random.randint(0, 2, size=N).astype(np.bool_))
+                   mask=np.random.randint(0, 2, size=N).astype(np.bool))
 
     # In the case the gc is caught loading
     gc.collect()
diff --git a/python/pyarrow/tests/test_schema.py 
b/python/pyarrow/tests/test_schema.py
index db44ad3211..27b6419f9a 100644
--- a/python/pyarrow/tests/test_schema.py
+++ b/python/pyarrow/tests/test_schema.py
@@ -51,7 +51,7 @@ def test_type_to_pandas_dtype():
     M8 = np.dtype('datetime64[ms]')
     cases = [
         (pa.null(), np.object_),
-        (pa.bool_(), np.bool_),
+        (pa.bool_(), np.bool),
         (pa.int8(), np.int8),
         (pa.int16(), np.int16),
         (pa.int32(), np.int32),
diff --git a/python/pyarrow/tests/test_table.py 
b/python/pyarrow/tests/test_table.py
index cb010f4387..bf6e5773dd 100644
--- a/python/pyarrow/tests/test_table.py
+++ b/python/pyarrow/tests/test_table.py
@@ -28,7 +28,6 @@ import pytest
 import pyarrow as pa
 import pyarrow.compute as pc
 from pyarrow.interchange import from_dataframe
-from pyarrow.vendored.version import Version
 
 
 def test_chunked_array_basics():
@@ -3556,16 +3555,9 @@ def test_numpy_asarray(constructor):
 @pytest.mark.parametrize("constructor", [pa.table, pa.record_batch])
 def test_numpy_array_protocol(constructor):
     table = constructor([[1, 2, 3], [4.0, 5.0, 6.0]], names=["a", "b"])
-    expected = np.array([[1, 4], [2, 5], [3, 6]], dtype="float64")
 
-    if Version(np.__version__) < Version("2.0.0.dev0"):
-        # copy keyword is not strict and not passed down to __array__
-        result = np.array(table, copy=False)
-        np.testing.assert_array_equal(result, expected)
-    else:
-        # starting with numpy 2.0, the copy=False keyword is assumed to be 
strict
-        with pytest.raises(ValueError, match="Unable to avoid a copy"):
-            np.array(table, copy=False)
+    with pytest.raises(ValueError, match="Unable to avoid a copy"):
+        np.array(table, copy=False)
 
 
 @pytest.mark.acero
diff --git a/python/pyarrow/tests/test_types.py 
b/python/pyarrow/tests/test_types.py
index 9b5c5efe1c..e0d74775e0 100644
--- a/python/pyarrow/tests/test_types.py
+++ b/python/pyarrow/tests/test_types.py
@@ -1350,8 +1350,8 @@ def test_is_boolean_value():
     assert pa.types.is_boolean_value(True)
     assert pa.types.is_boolean_value(False)
     if np is not None:
-        assert pa.types.is_boolean_value(np.bool_(True))
-        assert pa.types.is_boolean_value(np.bool_(False))
+        assert pa.types.is_boolean_value(np.bool(True))
+        assert pa.types.is_boolean_value(np.bool(False))
 
 
 @h.settings(suppress_health_check=(h.HealthCheck.too_slow,))
diff --git a/python/pyarrow/types.pxi b/python/pyarrow/types.pxi
index 8eea34224a..f9530a3436 100644
--- a/python/pyarrow/types.pxi
+++ b/python/pyarrow/types.pxi
@@ -31,8 +31,7 @@ import sys
 import warnings
 from cython import sizeof
 
-# These are imprecise because the type (in pandas 0.x) depends on the presence
-# of nulls
+# These are imprecise because the type depends on the presence of nulls
 cdef dict _pandas_type_map = {}
 
 
@@ -41,7 +40,7 @@ def _get_pandas_type_map():
     if not _pandas_type_map:
         _pandas_type_map.update({
             _Type_NA: np.object_,  # NaNs
-            _Type_BOOL: np.bool_,
+            _Type_BOOL: np.bool,
             _Type_INT8: np.int8,
             _Type_INT16: np.int16,
             _Type_INT32: np.int32,
diff --git a/python/pyproject.toml b/python/pyproject.toml
index 7b7ba8ba73..68c1a807dd 100644
--- a/python/pyproject.toml
+++ b/python/pyproject.toml
@@ -22,7 +22,7 @@ requires = [
     # Needed for build-time stub docstring extraction
     "libcst>=1.9.0; python_version >= '3.15'",
     "libcst>=1.8.6; python_version < '3.15'",
-    "numpy>=1.25",
+    "numpy>=2.0",
     "setuptools_scm[toml]>=8",
 ]
 build-backend = "scikit_build_core.build"
diff --git a/python/requirements-build.txt b/python/requirements-build.txt
index 65acde0c80..353c042fab 100644
--- a/python/requirements-build.txt
+++ b/python/requirements-build.txt
@@ -2,6 +2,6 @@ build
 cython>=3.1
 libcst>=1.9.0; python_version >= "3.15"
 libcst>=1.8.6; python_version < "3.15"
-numpy>=1.25
+numpy>=2.0
 scikit-build-core>=1.0
 setuptools_scm>=8
diff --git a/python/requirements-wheel-test.txt 
b/python/requirements-wheel-test.txt
index 00c0fcc468..ce75cbc3c0 100644
--- a/python/requirements-wheel-test.txt
+++ b/python/requirements-wheel-test.txt
@@ -12,9 +12,7 @@ tzdata; sys_platform == 'win32'
 # version. However, there is no need to make this strictly the oldest version,
 # so it can be broadened to have a single version specification across 
platforms.
 # (`~=x.y.z` specifies a compatible release as `>=x.y.z, == x.y.*`)
-numpy~=1.23.2; python_version < "3.11"
-numpy~=1.23.2; python_version == "3.11"
-numpy~=1.26.0; python_version == "3.12"
+numpy~=2.0.0; python_version <= "3.12"
 numpy~=2.1.0; python_version == "3.13"
 numpy~=2.3.3; python_version == "3.14"
 numpy~=2.5.2; python_version == "3.15"  # TODO: 2.5.2 is for 3.15-rc1, after 
release, update to correct version  
diff --git a/python/setup.cfg b/python/setup.cfg
index 58e401185a..28a05b2180 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -24,7 +24,6 @@ addopts = --ignore=scripts
 filterwarnings =
     # https://github.com/apache/arrow/issues/49227
     ignore:pyarrow.gandiva is deprecated:FutureWarning
-    error:The SparseDataFrame:FutureWarning
     # https://github.com/apache/arrow/issues/38239
     ignore:Setting custom ClientSession:DeprecationWarning
     # https://github.com/apache/arrow/issues/49255

Reply via email to