Repository: aws/aws-sdk-pandas

Issue:
Allow to_csv to write empty dataframe
Similar to #1921, Id like the to_csv to write an empty dataframe

We use the SDK to generate dynamic CSV files.

There are datasets that are correctly empty, so we'd like to generate a csv file with no rows.

In #1921 it seems as though the enhancement was rejected because they didn't want to add more arguments to the method.

Maybe we can add a property to the class to allow it to work with EmptyDataFrames?

```python
from awswrangler import s3

class GlueJob:

    def __init__(self, args: dict):
        s3.AllowEmptyDataFrame = True
```

We attempted as a workaround to add an empty row to dataframe, but that produces an undesirable file.
```csv
Column1|Column2|Column3|Column4|Column5
|||| # <= Dont want this there
```


Discussion:
Thanks @nabil-becker. 
I'll put together a PR and we'll review the enhancement then.

Resolving patch:
diff --git a/awswrangler/distributed/ray/modin/s3/_write_text.py b/awswrangler/distributed/ray/modin/s3/_write_text.py
--- a/awswrangler/distributed/ray/modin/s3/_write_text.py
+++ b/awswrangler/distributed/ray/modin/s3/_write_text.py
@@ -80,7 +80,7 @@ def _to_text_distributed(  # pylint: disable=unused-argument
     **pandas_kwargs: Any,
 ) -> List[str]:
     if df.empty is True:
-        raise exceptions.EmptyDataFrame("DataFrame cannot be empty.")
+        _logger.warning("Empty DataFrame will be written.")
 
     if bucketing:
         # Add bucket id to the prefix
diff --git a/awswrangler/s3/_write.py b/awswrangler/s3/_write.py
--- a/awswrangler/s3/_write.py
+++ b/awswrangler/s3/_write.py
@@ -60,7 +60,7 @@ def _validate_args(
     execution_engine: Enum,
 ) -> None:
     if df.empty is True:
-        raise exceptions.EmptyDataFrame("DataFrame cannot be empty.")
+        _logger.warning("Empty DataFrame will be written.")
     if dataset is False:
         if path is None:
             raise exceptions.InvalidArgumentValue("If dataset is False, the `path` argument must be passed.")
diff --git a/awswrangler/s3/_write_text.py b/awswrangler/s3/_write_text.py
--- a/awswrangler/s3/_write_text.py
+++ b/awswrangler/s3/_write_text.py
@@ -49,7 +49,7 @@ def _to_text(  # pylint: disable=unused-argument
 ) -> List[str]:
     s3_client = s3_client if s3_client else _utils.client(service_name="s3")
     if df.empty is True:
-        raise exceptions.EmptyDataFrame("DataFrame cannot be empty.")
+        _logger.warning("Empty DataFrame will be written.")
     if path is None and path_root is not None:
         file_path: str = (
             f"{path_root}{filename_prefix}.{file_format}{_COMPRESSION_2_EXT.get(pandas_kwargs.get('compression'))}"


Test patch:
diff --git a/tests/unit/test_moto.py b/tests/unit/test_moto.py
--- a/tests/unit/test_moto.py
+++ b/tests/unit/test_moto.py
@@ -11,7 +11,7 @@
 from botocore.exceptions import ClientError
 
 import awswrangler as wr
-from awswrangler.exceptions import EmptyDataFrame, InvalidArgumentCombination, InvalidArgumentValue
+from awswrangler.exceptions import InvalidArgumentCombination, InvalidArgumentValue
 
 from .._utils import ensure_data_types, get_df_csv, get_df_list
 
@@ -394,10 +394,9 @@ def test_to_csv_valid_argument_combination_when_dataset_true_succeed(moto_s3):
     wr.s3.to_csv(df=get_df_csv(), path=path, index=False, dataset=True, mode="append")
 
 
-def test_to_csv_data_empty_raise_succeed(moto_s3):
+def test_to_csv_data_empty(moto_s3):
     path = "s3://bucket/test.csv"
-    with pytest.raises(EmptyDataFrame):
-        wr.s3.to_csv(df=pd.DataFrame(), path=path, index=False)
+    wr.s3.to_csv(df=pd.DataFrame(), path=path, index=False)
 
 
 def test_parquet(moto_s3):
diff --git a/tests/unit/test_s3.py b/tests/unit/test_s3.py
--- a/tests/unit/test_s3.py
+++ b/tests/unit/test_s3.py
@@ -122,12 +122,20 @@ def test_missing_or_wrong_path(path, glue_database, glue_table):
         wr.s3.to_parquet(df=df, path=wrong_path, dataset=True, database=glue_database, table=glue_table)
 
 
-def test_s3_empty_dfs():
+@pytest.mark.xfail(is_ray_modin, raises=ValueError, reason="Ray dataset cannot write empty DF")
+def test_s3_empty_dfs(path):
     df = pd.DataFrame()
-    with pytest.raises(wr.exceptions.EmptyDataFrame):
-        wr.s3.to_parquet(df=df, path="")
-    with pytest.raises(wr.exceptions.EmptyDataFrame):
-        wr.s3.to_csv(df=df, path="")
+    file_path = f"{path}empty"
+    assert df.empty
+
+    wr.s3.to_csv(df, f"{file_path}.csv")
+    wr.s3.to_json(df, f"{file_path}.json")
+
+    df_read_csv = wr.s3.read_csv(f"{file_path}.csv")
+    assert df_read_csv.empty
+
+    df_read_json = wr.s3.read_json(f"{file_path}.json")
+    assert df_read_json.empty
 
 
 def test_absent_object(path):
diff --git a/tests/unit/test_s3_text.py b/tests/unit/test_s3_text.py
--- a/tests/unit/test_s3_text.py
+++ b/tests/unit/test_s3_text.py
@@ -417,12 +417,6 @@ def test_to_csv_schema_evolution(path, glue_database, glue_table) -> None:
 
 
 def test_exceptions(path):
-    with pytest.raises(wr.exceptions.EmptyDataFrame):
-        wr.s3.to_json(df=pd.DataFrame(), path=path)
-
-    with pytest.raises(wr.exceptions.EmptyDataFrame):
-        wr.s3.to_csv(df=pd.DataFrame(), path=path)
-
     df = pd.DataFrame({"c0": [1, 2], "c1": ["a", "b"]})
     with pytest.raises(wr.exceptions.InvalidArgument):
         wr.s3.to_csv(df=df, path=path, pandas_kwargs={})