Repository: ibis-project/ibis

Issue:
feat: remove `limit` kwarg from to_csv, to_parquet, etc
### Is your feature request related to a problem?

I feel bad immediately coming in here and giving feedback on https://github.com/ibis-project/ibis/commit/fecca421ad405465c4c001afa4e2976c07164530 after it just landed, it looks fantastic! Thank you for implementing it. But I didn't see it earlier have a suggestion on the API

Can we remove the `limit` kwarg? Instead of `table.to_csv(path, limit=100)`, can't a user always do `table.limit(100).to_csv(path)` instead? This would be breaking to remove this, so if we wanted to do this then we should do it before we ever release this API.

### Describe the solution you'd like

see above.

### What version of ibis are you running?

main

### What backend(s) are you using, if any?

all

### Code of Conduct

- [X] I agree to follow this project's Code of Conduct


Discussion:
Thanks for the suggestion! I'd agree with this change, and would even be for deprecating the `limit` kwarg from `execute` for the same reasons. The existence of this kwarg is mostly historical, I'd hope new users should use `.limit` instead.
+1 from me too, `t.limit(n)` FTW!
You can even use `t.head(n)` pandas-style if you want!

Resolving patch:
diff --git a/ibis/backends/base/__init__.py b/ibis/backends/base/__init__.py
--- a/ibis/backends/base/__init__.py
+++ b/ibis/backends/base/__init__.py
@@ -376,7 +376,6 @@ def to_parquet(
         path: str | Path,
         *,
         params: Mapping[ir.Scalar, Any] | None = None,
-        limit: int | str | None = None,
         **kwargs: Any,
     ) -> None:
         """Write the results of executing the given expression to a parquet file.
@@ -392,9 +391,6 @@ def to_parquet(
             The data source. A string or Path to the parquet file.
         params
             Mapping of scalar parameter expressions to value.
-        limit
-            An integer to effect a specific row limit. A value of `None` means
-            "no limit". The default is in `ibis/config.py`.
         **kwargs
             Additional keyword arguments passed to pyarrow.parquet.ParquetWriter
 
@@ -403,7 +399,7 @@ def to_parquet(
         self._import_pyarrow()
         import pyarrow.parquet as pq
 
-        batch_reader = expr.to_pyarrow_batches(params=params, limit=limit)
+        batch_reader = expr.to_pyarrow_batches(params=params)
 
         with pq.ParquetWriter(path, batch_reader.schema) as writer:
             for batch in batch_reader:
@@ -416,7 +412,6 @@ def to_csv(
         path: str | Path,
         *,
         params: Mapping[ir.Scalar, Any] | None = None,
-        limit: int | str | None = None,
         **kwargs: Any,
     ) -> None:
         """Write the results of executing the given expression to a CSV file.
@@ -432,9 +427,6 @@ def to_csv(
             The data source. A string or Path to the CSV file.
         params
             Mapping of scalar parameter expressions to value.
-        limit
-            An integer to effect a specific row limit. A value of `None` means
-            "no limit". The default is in `ibis/config.py`.
         **kwargs
             Additional keyword arguments passed to pyarrow.csv.CSVWriter
 
@@ -443,7 +435,7 @@ def to_csv(
         self._import_pyarrow()
         import pyarrow.csv as pcsv
 
-        batch_reader = expr.to_pyarrow_batches(params=params, limit=limit)
+        batch_reader = expr.to_pyarrow_batches(params=params)
 
         with pcsv.CSVWriter(path, batch_reader.schema) as writer:
             for batch in batch_reader:
diff --git a/ibis/expr/types/core.py b/ibis/expr/types/core.py
--- a/ibis/expr/types/core.py
+++ b/ibis/expr/types/core.py
@@ -406,7 +406,6 @@ def to_parquet(
         path: str | Path,
         *,
         params: Mapping[ir.Scalar, Any] | None = None,
-        limit: int | str | None = None,
         **kwargs: Any,
     ) -> None:
         """Write the results of executing the given expression to a parquet file
@@ -420,17 +419,12 @@ def to_parquet(
             The data source. A string or Path to the parquet file.
         params
             Mapping of scalar parameter expressions to value.
-        limit
-            An integer to effect a specific row limit. A value of `None` means
-            "no limit". The default is in `ibis/config.py`.
         **kwargs
             Additional keyword arguments passed to pyarrow.parquet.ParquetWriter
 
         https://arrow.apache.org/docs/python/generated/pyarrow.parquet.ParquetWriter.html
         """
-        self._find_backend(use_default=True).to_parquet(
-            self, path, limit=limit, **kwargs
-        )
+        self._find_backend(use_default=True).to_parquet(self, path, **kwargs)
 
     @experimental
     def to_csv(
@@ -438,7 +432,6 @@ def to_csv(
         path: str | Path,
         *,
         params: Mapping[ir.Scalar, Any] | None = None,
-        limit: int | str | None = None,
         **kwargs: Any,
     ) -> None:
         """Write the results of executing the given expression to a CSV file
@@ -452,15 +445,12 @@ def to_csv(
             The data source. A string or Path to the CSV file.
         params
             Mapping of scalar parameter expressions to value.
-        limit
-            An integer to effect a specific row limit. A value of `None` means
-            "no limit". The default is in `ibis/config.py`.
         **kwargs
             Additional keyword arguments passed to pyarrow.csv.CSVWriter
 
         https://arrow.apache.org/docs/python/generated/pyarrow.csv.CSVWriter.html
         """
-        self._find_backend(use_default=True).to_csv(self, path, limit=limit, **kwargs)
+        self._find_backend(use_default=True).to_csv(self, path, **kwargs)
 
     def unbind(self) -> ir.Table:
         """Return an expression built on `UnboundTable` instead of backend-specific objects."""


Test patch:
diff --git a/ibis/backends/tests/test_export.py b/ibis/backends/tests/test_export.py
--- a/ibis/backends/tests/test_export.py
+++ b/ibis/backends/tests/test_export.py
@@ -153,31 +153,25 @@ def test_no_pyarrow_message(awards_players, monkeypatch):
         awards_players.to_pyarrow()
 
 
-@pytest.mark.parametrize("limit", limit_no_limit)
-def test_table_to_parquet(tmp_path, backend, limit, awards_players):
+@pytest.mark.notimpl(["dask", "impala", "pyspark"])
+def test_table_to_parquet(tmp_path, backend, awards_players):
     outparquet = tmp_path / "out.parquet"
-    awards_players.to_parquet(outparquet, limit=limit)
+    awards_players.to_parquet(outparquet)
 
     df = pd.read_parquet(outparquet)
 
-    backend.assert_frame_equal(awards_players.execute(limit=limit), df)
-
-    if limit is not None:
-        assert len(df) == limit
+    backend.assert_frame_equal(awards_players.execute(), df)
 
 
-@pytest.mark.parametrize("limit", limit_no_limit)
-def test_table_to_csv(tmp_path, backend, limit, awards_players):
+@pytest.mark.notimpl(["dask", "impala", "pyspark"])
+def test_table_to_csv(tmp_path, backend, awards_players):
     outcsv = tmp_path / "out.csv"
 
     # avoid pandas NaNonense
     awards_players = awards_players.select("playerID", "awardID", "yearID", "lgID")
 
-    awards_players.to_csv(outcsv, limit=limit)
+    awards_players.to_csv(outcsv)
 
     df = pd.read_csv(outcsv, dtype=awards_players.schema().to_pandas())
 
-    backend.assert_frame_equal(awards_players.execute(limit=limit), df)
-
-    if limit is not None:
-        assert len(df) == limit
+    backend.assert_frame_equal(awards_players.execute(), df)