Skip to content

Commit 538d69a

Browse files
Backport PR #42292: REGR: ExcelWriter fails when passed kwargs (#42300)
Co-authored-by: Richard Shadrach <[email protected]>
1 parent 282b76e commit 538d69a

10 files changed

+147
-24
lines changed

pandas/io/excel/_odswriter.py

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def __init__(
2929
storage_options: StorageOptions = None,
3030
if_sheet_exists: str | None = None,
3131
engine_kwargs: dict[str, Any] | None = None,
32+
**kwargs,
3233
):
3334
from odf.opendocument import OpenDocumentSpreadsheet
3435

pandas/io/excel/_openpyxl.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
BaseExcelReader,
2020
ExcelWriter,
2121
)
22-
from pandas.io.excel._util import validate_freeze_panes
22+
from pandas.io.excel._util import (
23+
combine_kwargs,
24+
validate_freeze_panes,
25+
)
2326

2427
if TYPE_CHECKING:
2528
from openpyxl.descriptors.serialisable import Serialisable
@@ -39,10 +42,13 @@ def __init__(
3942
storage_options: StorageOptions = None,
4043
if_sheet_exists: str | None = None,
4144
engine_kwargs: dict[str, Any] | None = None,
45+
**kwargs,
4246
):
4347
# Use the openpyxl module as the Excel writer.
4448
from openpyxl.workbook import Workbook
4549

50+
engine_kwargs = combine_kwargs(engine_kwargs, kwargs)
51+
4652
super().__init__(
4753
path,
4854
mode=mode,

pandas/io/excel/_util.py

+31-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
from __future__ import annotations
22

3-
from typing import MutableMapping
3+
from typing import (
4+
Any,
5+
MutableMapping,
6+
)
47

58
from pandas.compat._optional import import_optional_dependency
69

@@ -246,3 +249,30 @@ def pop_header_name(row, index_col):
246249
header_name = None if header_name == "" else header_name
247250

248251
return header_name, row[:i] + [""] + row[i + 1 :]
252+
253+
254+
def combine_kwargs(engine_kwargs: dict[str, Any] | None, kwargs: dict) -> dict:
255+
"""
256+
Used to combine two sources of kwargs for the backend engine.
257+
258+
Use of kwargs is deprecated, this function is solely for use in 1.3 and should
259+
be removed in 1.4/2.0. Also _base.ExcelWriter.__new__ ensures either engine_kwargs
260+
or kwargs must be None or empty respectively.
261+
262+
Parameters
263+
----------
264+
engine_kwargs: dict
265+
kwargs to be passed through to the engine.
266+
kwargs: dict
267+
kwargs to be psased through to the engine (deprecated)
268+
269+
Returns
270+
-------
271+
engine_kwargs combined with kwargs
272+
"""
273+
if engine_kwargs is None:
274+
result = {}
275+
else:
276+
result = engine_kwargs.copy()
277+
result.update(kwargs)
278+
return result

pandas/io/excel/_xlsxwriter.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
from pandas._typing import StorageOptions
77

88
from pandas.io.excel._base import ExcelWriter
9-
from pandas.io.excel._util import validate_freeze_panes
9+
from pandas.io.excel._util import (
10+
combine_kwargs,
11+
validate_freeze_panes,
12+
)
1013

1114

1215
class _XlsxStyler:
@@ -175,11 +178,12 @@ def __init__(
175178
storage_options: StorageOptions = None,
176179
if_sheet_exists: str | None = None,
177180
engine_kwargs: dict[str, Any] | None = None,
181+
**kwargs,
178182
):
179183
# Use the xlsxwriter module as the Excel writer.
180184
from xlsxwriter import Workbook
181185

182-
engine_kwargs = engine_kwargs or {}
186+
engine_kwargs = combine_kwargs(engine_kwargs, kwargs)
183187

184188
if mode == "a":
185189
raise ValueError("Append mode is not supported with xlsxwriter!")

pandas/io/excel/_xlwt.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
from pandas._typing import StorageOptions
1010

1111
from pandas.io.excel._base import ExcelWriter
12-
from pandas.io.excel._util import validate_freeze_panes
12+
from pandas.io.excel._util import (
13+
combine_kwargs,
14+
validate_freeze_panes,
15+
)
1316

1417
if TYPE_CHECKING:
1518
from xlwt import XFStyle
@@ -30,10 +33,13 @@ def __init__(
3033
storage_options: StorageOptions = None,
3134
if_sheet_exists: str | None = None,
3235
engine_kwargs: dict[str, Any] | None = None,
36+
**kwargs,
3337
):
3438
# Use the xlwt module as the Excel writer.
3539
import xlwt
3640

41+
engine_kwargs = combine_kwargs(engine_kwargs, kwargs)
42+
3743
if mode == "a":
3844
raise ValueError("Append mode is not supported with xlwt!")
3945

pandas/tests/io/excel/test_odswriter.py

+24
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import re
2+
13
import pytest
24

35
import pandas._testing as tm
@@ -15,3 +17,25 @@ def test_write_append_mode_raises(ext):
1517
with tm.ensure_clean(ext) as f:
1618
with pytest.raises(ValueError, match=msg):
1719
ExcelWriter(f, engine="odf", mode="a")
20+
21+
22+
@pytest.mark.parametrize("nan_inf_to_errors", [True, False])
23+
def test_kwargs(ext, nan_inf_to_errors):
24+
# GH 42286
25+
# odswriter doesn't utilize kwargs, nothing to check except that it works
26+
kwargs = {"options": {"nan_inf_to_errors": nan_inf_to_errors}}
27+
with tm.ensure_clean(ext) as f:
28+
msg = re.escape("Use of **kwargs is deprecated")
29+
with tm.assert_produces_warning(FutureWarning, match=msg):
30+
with ExcelWriter(f, engine="odf", **kwargs) as _:
31+
pass
32+
33+
34+
@pytest.mark.parametrize("nan_inf_to_errors", [True, False])
35+
def test_engine_kwargs(ext, nan_inf_to_errors):
36+
# GH 42286
37+
# odswriter doesn't utilize engine_kwargs, nothing to check except that it works
38+
engine_kwargs = {"options": {"nan_inf_to_errors": nan_inf_to_errors}}
39+
with tm.ensure_clean(ext) as f:
40+
with ExcelWriter(f, engine="odf", engine_kwargs=engine_kwargs) as _:
41+
pass

pandas/tests/io/excel/test_openpyxl.py

+24
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,30 @@ def test_write_cells_merge_styled(ext):
8585
assert xcell_a2.font == openpyxl_sty_merged
8686

8787

88+
@pytest.mark.parametrize("write_only", [True, False])
89+
def test_kwargs(ext, write_only):
90+
# GH 42286
91+
# openpyxl doesn't utilize kwargs, only test that supplying a kwarg works
92+
kwargs = {"write_only": write_only}
93+
with tm.ensure_clean(ext) as f:
94+
msg = re.escape("Use of **kwargs is deprecated")
95+
with tm.assert_produces_warning(FutureWarning, match=msg):
96+
with ExcelWriter(f, engine="openpyxl", **kwargs) as writer:
97+
# ExcelWriter won't allow us to close without writing something
98+
DataFrame().to_excel(writer)
99+
100+
101+
@pytest.mark.parametrize("write_only", [True, False])
102+
def test_engine_kwargs(ext, write_only):
103+
# GH 42286
104+
# openpyxl doesn't utilize kwargs, only test that supplying a engine_kwarg works
105+
engine_kwargs = {"write_only": write_only}
106+
with tm.ensure_clean(ext) as f:
107+
with ExcelWriter(f, engine="openpyxl", engine_kwargs=engine_kwargs) as writer:
108+
# ExcelWriter won't allow us to close without writing something
109+
DataFrame().to_excel(writer)
110+
111+
88112
@pytest.mark.parametrize(
89113
"mode,expected", [("w", ["baz"]), ("a", ["foo", "bar", "baz"])]
90114
)

pandas/tests/io/excel/test_writers.py

-19
Original file line numberDiff line numberDiff line change
@@ -1399,25 +1399,6 @@ def check_called(func):
13991399
with tm.ensure_clean("something.xls") as filepath:
14001400
check_called(lambda: df.to_excel(filepath, engine="dummy"))
14011401

1402-
@pytest.mark.parametrize(
1403-
"ext",
1404-
[
1405-
pytest.param(".xlsx", marks=td.skip_if_no("xlsxwriter")),
1406-
pytest.param(".xlsx", marks=td.skip_if_no("openpyxl")),
1407-
pytest.param(".ods", marks=td.skip_if_no("odf")),
1408-
],
1409-
)
1410-
def test_kwargs_deprecated(self, ext):
1411-
# GH 40430
1412-
msg = re.escape("Use of **kwargs is deprecated")
1413-
with tm.assert_produces_warning(FutureWarning, match=msg):
1414-
with tm.ensure_clean(ext) as path:
1415-
try:
1416-
with ExcelWriter(path, kwarg=1):
1417-
pass
1418-
except TypeError:
1419-
pass
1420-
14211402
@pytest.mark.parametrize(
14221403
"ext",
14231404
[

pandas/tests/io/excel/test_xlsxwriter.py

+21
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import re
12
import warnings
23

34
import pytest
@@ -61,3 +62,23 @@ def test_write_append_mode_raises(ext):
6162
with tm.ensure_clean(ext) as f:
6263
with pytest.raises(ValueError, match=msg):
6364
ExcelWriter(f, engine="xlsxwriter", mode="a")
65+
66+
67+
@pytest.mark.parametrize("nan_inf_to_errors", [True, False])
68+
def test_kwargs(ext, nan_inf_to_errors):
69+
# GH 42286
70+
kwargs = {"options": {"nan_inf_to_errors": nan_inf_to_errors}}
71+
with tm.ensure_clean(ext) as f:
72+
msg = re.escape("Use of **kwargs is deprecated")
73+
with tm.assert_produces_warning(FutureWarning, match=msg):
74+
with ExcelWriter(f, engine="xlsxwriter", **kwargs) as writer:
75+
assert writer.book.nan_inf_to_errors == nan_inf_to_errors
76+
77+
78+
@pytest.mark.parametrize("nan_inf_to_errors", [True, False])
79+
def test_engine_kwargs(ext, nan_inf_to_errors):
80+
# GH 42286
81+
engine_kwargs = {"options": {"nan_inf_to_errors": nan_inf_to_errors}}
82+
with tm.ensure_clean(ext) as f:
83+
with ExcelWriter(f, engine="xlsxwriter", engine_kwargs=engine_kwargs) as writer:
84+
assert writer.book.nan_inf_to_errors == nan_inf_to_errors

pandas/tests/io/excel/test_xlwt.py

+26
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import re
2+
13
import numpy as np
24
import pytest
35

@@ -97,3 +99,27 @@ def test_option_xls_writer_deprecated(ext):
9799
check_stacklevel=False,
98100
):
99101
options.io.excel.xls.writer = "xlwt"
102+
103+
104+
@pytest.mark.parametrize("write_only", [True, False])
105+
def test_kwargs(ext, write_only):
106+
# GH 42286
107+
# xlwt doesn't utilize kwargs, only test that supplying a kwarg works
108+
kwargs = {"write_only": write_only}
109+
with tm.ensure_clean(ext) as f:
110+
msg = re.escape("Use of **kwargs is deprecated")
111+
with tm.assert_produces_warning(FutureWarning, match=msg):
112+
with ExcelWriter(f, engine="openpyxl", **kwargs) as writer:
113+
# xlwt won't allow us to close without writing something
114+
DataFrame().to_excel(writer)
115+
116+
117+
@pytest.mark.parametrize("write_only", [True, False])
118+
def test_engine_kwargs(ext, write_only):
119+
# GH 42286
120+
# xlwt doesn't utilize kwargs, only test that supplying a engine_kwarg works
121+
engine_kwargs = {"write_only": write_only}
122+
with tm.ensure_clean(ext) as f:
123+
with ExcelWriter(f, engine="openpyxl", engine_kwargs=engine_kwargs) as writer:
124+
# xlwt won't allow us to close without writing something
125+
DataFrame().to_excel(writer)

0 commit comments

Comments
 (0)