Skip to content

Commit 65244af

Browse files
GH-49232: [Python] deprecate feather python (#49590)
### Rationale for this change Arrow C++ is deprecating the Feather reader/writer (#49231) so we should update the Python functions too. Feather V2 is exactly the Arrow IPC file format, making the separate `pyarrow.feather` module redundant. Users should migrate to `pyarrow.ipc`. ### What changes are included in this PR? - Added `FutureWarning` deprecation warnings to all four public APIs in `pyarrow.feather`: `write_feather()`, `read_feather()`, `read_table()`, and `FeatherDataset` - Extracted `_read_table_internal()` so that `read_feather()` calling `read_table()` internally does not produce double warnings - Updated `FeatherDataset.read_table()` to use the internal function for the same reason - Added `.. deprecated:: 24.0.0` directives to all four public API docstrings - Added module-level `pytestmark` filter in `test_feather.py` to suppress warnings in existing tests - Added 5 new tests verifying deprecation warnings are emitted correctly (including a no-double-warning test) - Added `@ pytest.mark.filterwarnings` to 5 test functions in `test_dataset.py` that use feather as a utility - Updated `feather.rst` with a deprecation notice and migration guide (including a note that `pyarrow.ipc` does not compress by default unlike `feather.write_feather`) - Updated `formats.rst` to mark the Feather API section as deprecated ### Are these changes tested? Yes. Five new tests verify that each deprecated function emits exactly one `FutureWarning`, and existing tests continue to pass with module/function-level warning filters. ### Are there any user-facing changes? Yes — calling `pyarrow.feather.write_feather()`, `read_feather()`, `read_table()`, or `FeatherDataset()` now emits a `FutureWarning` directing users to `pyarrow.ipc` equivalents. Existing functionality is unchanged. * GitHub Issue: #49232 Lead-authored-by: Piyush Kanti Chanda <piyush.chanda@databricks.com> Co-authored-by: Isaac Signed-off-by: AlenkaF <frim.alenka@gmail.com>
1 parent bd8c6cc commit 65244af

5 files changed

Lines changed: 173 additions & 24 deletions

File tree

docs/source/python/api/formats.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,14 @@ CSV Files
4242

4343
.. _api.feather:
4444

45-
Feather Files
46-
-------------
45+
Feather Files (Deprecated)
46+
--------------------------
4747

4848
.. currentmodule:: pyarrow.feather
4949

50+
.. deprecated:: 24.0.0
51+
The Feather API is deprecated. Use the :ref:`IPC <ipc>` API instead.
52+
5053
.. autosummary::
5154
:toctree: ../generated/
5255

docs/source/python/feather.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
Feather File Format
2323
===================
2424

25+
.. deprecated:: 24.0.0
26+
The ``pyarrow.feather`` module is deprecated. Feather V2 is the Arrow IPC
27+
file format. Use :mod:`pyarrow.ipc` instead. See :ref:`ipc` for details.
28+
2529
Feather is a portable file format for storing Arrow tables or data frames (from
2630
languages like Python or R) that utilizes the :ref:`Arrow IPC format <ipc>`
2731
internally. Feather was created early in the Arrow project as a proof of
@@ -107,3 +111,35 @@ Writing Version 1 (V1) Files
107111
For compatibility with libraries without support for Version 2 files, you can
108112
write the version 1 format by passing ``version=1`` to ``write_feather``. We
109113
intend to maintain read support for V1 for the foreseeable future.
114+
115+
Migration to IPC
116+
----------------
117+
118+
Since Feather V2 is the Arrow IPC file format, you can use the
119+
:mod:`pyarrow.ipc` module as a direct replacement:
120+
121+
.. code-block:: python
122+
123+
import pyarrow as pa
124+
import pyarrow.ipc
125+
126+
table = pa.table({"col1": [1, 2, 3], "col2": ["a", "b", "c"]})
127+
128+
# Writing (replaces feather.write_feather)
129+
options = pa.ipc.IpcWriteOptions(compression='lz4')
130+
with pa.ipc.new_file("data.arrow", table.schema, options=options) as writer:
131+
writer.write_table(table)
132+
133+
# Reading (replaces feather.read_table)
134+
with pa.ipc.open_file("data.arrow") as reader:
135+
result = reader.read_all()
136+
137+
.. note::
138+
139+
``feather.write_feather`` defaults to LZ4 compression, while
140+
``ipc.new_file`` does not compress by default. To preserve the same
141+
behavior, pass ``compression='lz4'`` via
142+
:class:`~pyarrow.ipc.IpcWriteOptions` as shown above.
143+
144+
For reading multiple files, use the :mod:`pyarrow.dataset` module with
145+
``format='ipc'`` instead of :class:`~pyarrow.feather.FeatherDataset`.

python/pyarrow/feather.py

Lines changed: 79 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from collections.abc import Sequence
2020
import os
21+
import warnings
2122

2223
from pyarrow.pandas_compat import _pandas_api # noqa
2324
from pyarrow.lib import (Codec, Table, # noqa
@@ -31,6 +32,9 @@ class FeatherDataset:
3132
"""
3233
Encapsulates details of reading a list of Feather files.
3334
35+
.. deprecated:: 24.0.0
36+
Use :func:`pyarrow.dataset.dataset` with ``format='ipc'`` instead.
37+
3438
Parameters
3539
----------
3640
path_or_paths : List[str]
@@ -40,6 +44,12 @@ class FeatherDataset:
4044
"""
4145

4246
def __init__(self, path_or_paths, validate_schema=True):
47+
warnings.warn(
48+
"pyarrow.feather.FeatherDataset is deprecated as of 24.0.0. "
49+
"Use pyarrow.dataset.dataset() with format='ipc' instead.",
50+
FutureWarning,
51+
stacklevel=2
52+
)
4353
self.paths = path_or_paths
4454
self.validate_schema = validate_schema
4555

@@ -57,12 +67,12 @@ def read_table(self, columns=None):
5767
pyarrow.Table
5868
Content of the file as a table (of columns)
5969
"""
60-
_fil = read_table(self.paths[0], columns=columns)
70+
_fil = _read_table_internal(self.paths[0], columns=columns)
6171
self._tables = [_fil]
6272
self.schema = _fil.schema
6373

6474
for path in self.paths[1:]:
65-
table = read_table(path, columns=columns)
75+
table = _read_table_internal(path, columns=columns)
6676
if self.validate_schema:
6777
self.validate_schemas(path, table)
6878
self._tables.append(table)
@@ -117,6 +127,11 @@ def write_feather(df, dest, compression=None, compression_level=None,
117127
"""
118128
Write a pandas.DataFrame to Feather format.
119129
130+
.. deprecated:: 24.0.0
131+
Use :func:`pyarrow.ipc.new_file` /
132+
:class:`pyarrow.ipc.RecordBatchFileWriter` instead.
133+
Feather V2 is the Arrow IPC file format.
134+
120135
Parameters
121136
----------
122137
df : pandas.DataFrame or pyarrow.Table
@@ -137,6 +152,13 @@ def write_feather(df, dest, compression=None, compression_level=None,
137152
Feather file version. Version 2 is the current. Version 1 is the more
138153
limited legacy format
139154
"""
155+
warnings.warn(
156+
"pyarrow.feather.write_feather is deprecated as of 24.0.0. "
157+
"Use pyarrow.ipc.new_file() / RecordBatchFileWriter instead. "
158+
"Feather V2 is the Arrow IPC file format.",
159+
FutureWarning,
160+
stacklevel=2
161+
)
140162
if _pandas_api.have_pandas:
141163
if (_pandas_api.has_sparse and
142164
isinstance(df, _pandas_api.pd.SparseDataFrame)):
@@ -201,6 +223,11 @@ def read_feather(source, columns=None, use_threads=True,
201223
Read a pandas.DataFrame from Feather format. To read as pyarrow.Table use
202224
feather.read_table.
203225
226+
.. deprecated:: 24.0.0
227+
Use :func:`pyarrow.ipc.open_file` /
228+
:class:`pyarrow.ipc.RecordBatchFileReader` instead.
229+
Feather V2 is the Arrow IPC file format.
230+
204231
Parameters
205232
----------
206233
source : str file path, or file-like object
@@ -222,31 +249,23 @@ def read_feather(source, columns=None, use_threads=True,
222249
df : pandas.DataFrame
223250
The contents of the Feather file as a pandas.DataFrame
224251
"""
225-
return (read_table(
252+
warnings.warn(
253+
"pyarrow.feather.read_feather is deprecated as of 24.0.0. "
254+
"Use pyarrow.ipc.open_file() / RecordBatchFileReader instead. "
255+
"Feather V2 is the Arrow IPC file format.",
256+
FutureWarning,
257+
stacklevel=2
258+
)
259+
return (_read_table_internal(
226260
source, columns=columns, memory_map=memory_map,
227261
use_threads=use_threads).to_pandas(use_threads=use_threads, **kwargs))
228262

229263

230-
def read_table(source, columns=None, memory_map=False, use_threads=True):
264+
def _read_table_internal(source, columns=None, memory_map=False,
265+
use_threads=True):
231266
"""
232-
Read a pyarrow.Table from Feather format
233-
234-
Parameters
235-
----------
236-
source : str file path, or file-like object
237-
You can use MemoryMappedFile as source, for explicitly use memory map.
238-
columns : sequence, optional
239-
Only read a specific set of columns. If not provided, all columns are
240-
read.
241-
memory_map : boolean, default False
242-
Use memory mapping when opening file on disk, when source is a str
243-
use_threads : bool, default True
244-
Whether to parallelize reading using multiple threads.
245-
246-
Returns
247-
-------
248-
table : pyarrow.Table
249-
The contents of the Feather file as a pyarrow.Table
267+
Internal implementation for reading a Feather file as a pyarrow.Table.
268+
Does not emit deprecation warnings.
250269
"""
251270
reader = _feather.FeatherReader(
252271
source, use_memory_map=memory_map, use_threads=use_threads)
@@ -277,3 +296,41 @@ def read_table(source, columns=None, memory_map=False, use_threads=True):
277296
else:
278297
# follow exact order / selection of names
279298
return table.select(columns)
299+
300+
301+
def read_table(source, columns=None, memory_map=False, use_threads=True):
302+
"""
303+
Read a pyarrow.Table from Feather format
304+
305+
.. deprecated:: 24.0.0
306+
Use :func:`pyarrow.ipc.open_file` /
307+
:class:`pyarrow.ipc.RecordBatchFileReader` instead.
308+
Feather V2 is the Arrow IPC file format.
309+
310+
Parameters
311+
----------
312+
source : str file path, or file-like object
313+
You can use MemoryMappedFile as source, for explicitly use memory map.
314+
columns : sequence, optional
315+
Only read a specific set of columns. If not provided, all columns are
316+
read.
317+
memory_map : boolean, default False
318+
Use memory mapping when opening file on disk, when source is a str
319+
use_threads : bool, default True
320+
Whether to parallelize reading using multiple threads.
321+
322+
Returns
323+
-------
324+
table : pyarrow.Table
325+
The contents of the Feather file as a pyarrow.Table
326+
"""
327+
warnings.warn(
328+
"pyarrow.feather.read_table is deprecated as of 24.0.0. "
329+
"Use pyarrow.ipc.open_file() / RecordBatchFileReader instead. "
330+
"Feather V2 is the Arrow IPC file format.",
331+
FutureWarning,
332+
stacklevel=2
333+
)
334+
return _read_table_internal(source, columns=columns,
335+
memory_map=memory_map,
336+
use_threads=use_threads)

python/pyarrow/tests/test_dataset.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1927,6 +1927,7 @@ def test_fragments_parquet_subset_with_nested_fields(tempdir):
19271927

19281928
@pytest.mark.pandas
19291929
@pytest.mark.parquet
1930+
@pytest.mark.filterwarnings("ignore:pyarrow.feather:FutureWarning")
19301931
def test_fragments_repr(tempdir, dataset):
19311932
# partitioned parquet dataset
19321933
fragment = list(dataset.get_fragments())[0]
@@ -3699,6 +3700,7 @@ def test_column_names_encoding(tempdir, dataset_reader):
36993700
assert dataset_transcoded.to_table().equals(expected_table)
37003701

37013702

3703+
@pytest.mark.filterwarnings("ignore:pyarrow.feather:FutureWarning")
37023704
def test_feather_format(tempdir, dataset_reader):
37033705
from pyarrow.feather import write_feather
37043706

@@ -4080,6 +4082,7 @@ def test_dataset_project_null_column(tempdir, dataset_reader):
40804082
assert dataset_reader.to_table(dataset).equals(expected)
40814083

40824084

4085+
@pytest.mark.filterwarnings("ignore:pyarrow.feather:FutureWarning")
40834086
def test_dataset_project_columns(tempdir, dataset_reader):
40844087
# basic column re-projection with expressions
40854088
from pyarrow import feather
@@ -4431,6 +4434,7 @@ def test_write_dataset_with_dataset(tempdir):
44314434

44324435

44334436
@pytest.mark.pandas
4437+
@pytest.mark.filterwarnings("ignore:pyarrow.feather:FutureWarning")
44344438
def test_write_dataset_existing_data(tempdir):
44354439
directory = tempdir / 'ds'
44364440
table = pa.table({'b': ['x', 'y', 'z'], 'c': [1, 2, 3]})
@@ -5054,6 +5058,7 @@ def test_write_dataset_arrow_schema_metadata(tempdir):
50545058
assert result["a"].type.tz == "Europe/Brussels"
50555059

50565060

5061+
@pytest.mark.filterwarnings("ignore:pyarrow.feather:FutureWarning")
50575062
def test_write_dataset_schema_metadata(tempdir):
50585063
# ensure that schema metadata gets written
50595064
from pyarrow import feather

python/pyarrow/tests/test_feather.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import os
2020
import sys
2121
import tempfile
22+
import warnings
2223
import pytest
2324
import hypothesis as h
2425
import hypothesis.strategies as st
@@ -40,6 +41,12 @@
4041
except ImportError:
4142
pass
4243

44+
# Suppress deprecation warnings for existing tests since pyarrow.feather
45+
# is deprecated as of 24.0.0
46+
pytestmark = pytest.mark.filterwarnings(
47+
"ignore:pyarrow.feather:FutureWarning"
48+
)
49+
4350

4451
@pytest.fixture(scope='module')
4552
def datadir(base_datadir):
@@ -882,3 +889,44 @@ def test_feather_datetime_resolution_arrow_to_pandas(tempdir):
882889

883890
assert expected_0 == result['date'][0]
884891
assert expected_1 == result['date'][1]
892+
893+
894+
# --- Deprecation warning tests ---
895+
896+
@pytest.mark.pandas
897+
@pytest.mark.filterwarnings("default:pyarrow.feather:FutureWarning")
898+
def test_feather_deprecation_warnings(tempdir):
899+
table = pa.table({"a": [1, 2, 3]})
900+
path = str(tempdir / "test.feather")
901+
902+
with pytest.warns(FutureWarning, match="write_feather is deprecated"):
903+
write_feather(table, path)
904+
905+
with pytest.warns(FutureWarning, match="read_table is deprecated"):
906+
read_table(path)
907+
908+
with pytest.warns(FutureWarning, match="read_feather is deprecated"):
909+
read_feather(path)
910+
911+
912+
@pytest.mark.filterwarnings("default:pyarrow.feather:FutureWarning")
913+
def test_feather_dataset_deprecated():
914+
with pytest.warns(FutureWarning, match="FeatherDataset is deprecated"):
915+
FeatherDataset([])
916+
917+
918+
@pytest.mark.pandas
919+
@pytest.mark.filterwarnings("default:pyarrow.feather:FutureWarning")
920+
def test_read_feather_no_double_warning(tempdir):
921+
"""Verify read_feather emits exactly one FutureWarning, not two."""
922+
table = pa.table({"a": [1, 2, 3]})
923+
path = str(tempdir / "test.feather")
924+
with warnings.catch_warnings():
925+
warnings.simplefilter("ignore", FutureWarning)
926+
write_feather(table, path)
927+
with warnings.catch_warnings(record=True) as w:
928+
warnings.simplefilter("always")
929+
read_feather(path)
930+
future_warnings = [x for x in w if issubclass(x.category,
931+
FutureWarning)]
932+
assert len(future_warnings) == 1

0 commit comments

Comments
 (0)