diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 8089fc58db07d..86e30024c51b1 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -2132,7 +2132,11 @@ def _repr_data_resource_(self): # I/O Methods @final - @doc(klass="object", storage_options=_shared_docs["storage_options"]) + @doc( + klass="object", + storage_options=_shared_docs["storage_options"], + storage_options_versionadded="1.2.0", + ) def to_excel( self, excel_writer, @@ -2218,7 +2222,7 @@ def to_excel( is to be frozen. {storage_options} - .. versionadded:: 1.2.0 + .. versionadded:: {storage_options_versionadded} See Also -------- diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 10b607da45ca8..4c143733ac77e 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -28,6 +28,7 @@ Level, QuantileInterpolation, Scalar, + StorageOptions, WriteBuffer, ) from pandas.compat._optional import import_optional_dependency @@ -549,6 +550,7 @@ def set_tooltips( NDFrame.to_excel, klass="Styler", storage_options=_shared_docs["storage_options"], + storage_options_versionadded="1.5.0", ) def to_excel( self, @@ -568,6 +570,7 @@ def to_excel( inf_rep: str = "inf", verbose: bool = True, freeze_panes: tuple[int, int] | None = None, + storage_options: StorageOptions = None, ) -> None: from pandas.io.formats.excel import ExcelFormatter @@ -590,6 +593,7 @@ def to_excel( startcol=startcol, freeze_panes=freeze_panes, engine=engine, + storage_options=storage_options, ) @overload diff --git a/pandas/tests/io/excel/test_style.py b/pandas/tests/io/excel/test_style.py index 9bbe61c90d973..00f6ccb96a905 100644 --- a/pandas/tests/io/excel/test_style.py +++ b/pandas/tests/io/excel/test_style.py @@ -1,9 +1,15 @@ import contextlib +import time import numpy as np import pytest -from pandas import DataFrame +import pandas.util._test_decorators as td + +from pandas import ( + DataFrame, + read_excel, +) import pandas._testing as tm from pandas.io.excel import ExcelWriter @@ -206,3 +212,27 @@ def custom_converter(css): with contextlib.closing(openpyxl.load_workbook(path)) as wb: assert wb["custom"].cell(2, 2).font.color.value == "00111222" + + +@pytest.mark.single_cpu +@td.skip_if_not_us_locale +def test_styler_to_s3(s3_resource, s3so): + # GH#46381 + + mock_bucket_name, target_file = "pandas-test", "test.xlsx" + df = DataFrame({"x": [1, 2, 3], "y": [2, 4, 6]}) + styler = df.style.set_sticky(axis="index") + styler.to_excel(f"s3://{mock_bucket_name}/{target_file}", storage_options=s3so) + timeout = 5 + while True: + if target_file in ( + obj.key for obj in s3_resource.Bucket("pandas-test").objects.all() + ): + break + time.sleep(0.1) + timeout -= 0.1 + assert timeout > 0, "Timed out waiting for file to appear on moto" + result = read_excel( + f"s3://{mock_bucket_name}/{target_file}", index_col=0, storage_options=s3so + ) + tm.assert_frame_equal(result, df)