Skip to content

Commit

Permalink
Backport PR #42292: REGR: ExcelWriter fails when passed kwargs (#42300)
Browse files Browse the repository at this point in the history
Co-authored-by: Richard Shadrach <45562402+rhshadrach@users.noreply.github.com>
  • Loading branch information
meeseeksmachine and rhshadrach authored Jun 29, 2021
1 parent 282b76e commit 538d69a
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 24 deletions.
1 change: 1 addition & 0 deletions pandas/io/excel/_odswriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def __init__(
storage_options: StorageOptions = None,
if_sheet_exists: str | None = None,
engine_kwargs: dict[str, Any] | None = None,
**kwargs,
):
from odf.opendocument import OpenDocumentSpreadsheet

Expand Down
8 changes: 7 additions & 1 deletion pandas/io/excel/_openpyxl.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
BaseExcelReader,
ExcelWriter,
)
from pandas.io.excel._util import validate_freeze_panes
from pandas.io.excel._util import (
combine_kwargs,
validate_freeze_panes,
)

if TYPE_CHECKING:
from openpyxl.descriptors.serialisable import Serialisable
Expand All @@ -39,10 +42,13 @@ def __init__(
storage_options: StorageOptions = None,
if_sheet_exists: str | None = None,
engine_kwargs: dict[str, Any] | None = None,
**kwargs,
):
# Use the openpyxl module as the Excel writer.
from openpyxl.workbook import Workbook

engine_kwargs = combine_kwargs(engine_kwargs, kwargs)

super().__init__(
path,
mode=mode,
Expand Down
32 changes: 31 additions & 1 deletion pandas/io/excel/_util.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from __future__ import annotations

from typing import MutableMapping
from typing import (
Any,
MutableMapping,
)

from pandas.compat._optional import import_optional_dependency

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

return header_name, row[:i] + [""] + row[i + 1 :]


def combine_kwargs(engine_kwargs: dict[str, Any] | None, kwargs: dict) -> dict:
"""
Used to combine two sources of kwargs for the backend engine.
Use of kwargs is deprecated, this function is solely for use in 1.3 and should
be removed in 1.4/2.0. Also _base.ExcelWriter.__new__ ensures either engine_kwargs
or kwargs must be None or empty respectively.
Parameters
----------
engine_kwargs: dict
kwargs to be passed through to the engine.
kwargs: dict
kwargs to be psased through to the engine (deprecated)
Returns
-------
engine_kwargs combined with kwargs
"""
if engine_kwargs is None:
result = {}
else:
result = engine_kwargs.copy()
result.update(kwargs)
return result
8 changes: 6 additions & 2 deletions pandas/io/excel/_xlsxwriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
from pandas._typing import StorageOptions

from pandas.io.excel._base import ExcelWriter
from pandas.io.excel._util import validate_freeze_panes
from pandas.io.excel._util import (
combine_kwargs,
validate_freeze_panes,
)


class _XlsxStyler:
Expand Down Expand Up @@ -175,11 +178,12 @@ def __init__(
storage_options: StorageOptions = None,
if_sheet_exists: str | None = None,
engine_kwargs: dict[str, Any] | None = None,
**kwargs,
):
# Use the xlsxwriter module as the Excel writer.
from xlsxwriter import Workbook

engine_kwargs = engine_kwargs or {}
engine_kwargs = combine_kwargs(engine_kwargs, kwargs)

if mode == "a":
raise ValueError("Append mode is not supported with xlsxwriter!")
Expand Down
8 changes: 7 additions & 1 deletion pandas/io/excel/_xlwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
from pandas._typing import StorageOptions

from pandas.io.excel._base import ExcelWriter
from pandas.io.excel._util import validate_freeze_panes
from pandas.io.excel._util import (
combine_kwargs,
validate_freeze_panes,
)

if TYPE_CHECKING:
from xlwt import XFStyle
Expand All @@ -30,10 +33,13 @@ def __init__(
storage_options: StorageOptions = None,
if_sheet_exists: str | None = None,
engine_kwargs: dict[str, Any] | None = None,
**kwargs,
):
# Use the xlwt module as the Excel writer.
import xlwt

engine_kwargs = combine_kwargs(engine_kwargs, kwargs)

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

Expand Down
24 changes: 24 additions & 0 deletions pandas/tests/io/excel/test_odswriter.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import re

import pytest

import pandas._testing as tm
Expand All @@ -15,3 +17,25 @@ def test_write_append_mode_raises(ext):
with tm.ensure_clean(ext) as f:
with pytest.raises(ValueError, match=msg):
ExcelWriter(f, engine="odf", mode="a")


@pytest.mark.parametrize("nan_inf_to_errors", [True, False])
def test_kwargs(ext, nan_inf_to_errors):
# GH 42286
# odswriter doesn't utilize kwargs, nothing to check except that it works
kwargs = {"options": {"nan_inf_to_errors": nan_inf_to_errors}}
with tm.ensure_clean(ext) as f:
msg = re.escape("Use of **kwargs is deprecated")
with tm.assert_produces_warning(FutureWarning, match=msg):
with ExcelWriter(f, engine="odf", **kwargs) as _:
pass


@pytest.mark.parametrize("nan_inf_to_errors", [True, False])
def test_engine_kwargs(ext, nan_inf_to_errors):
# GH 42286
# odswriter doesn't utilize engine_kwargs, nothing to check except that it works
engine_kwargs = {"options": {"nan_inf_to_errors": nan_inf_to_errors}}
with tm.ensure_clean(ext) as f:
with ExcelWriter(f, engine="odf", engine_kwargs=engine_kwargs) as _:
pass
24 changes: 24 additions & 0 deletions pandas/tests/io/excel/test_openpyxl.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,30 @@ def test_write_cells_merge_styled(ext):
assert xcell_a2.font == openpyxl_sty_merged


@pytest.mark.parametrize("write_only", [True, False])
def test_kwargs(ext, write_only):
# GH 42286
# openpyxl doesn't utilize kwargs, only test that supplying a kwarg works
kwargs = {"write_only": write_only}
with tm.ensure_clean(ext) as f:
msg = re.escape("Use of **kwargs is deprecated")
with tm.assert_produces_warning(FutureWarning, match=msg):
with ExcelWriter(f, engine="openpyxl", **kwargs) as writer:
# ExcelWriter won't allow us to close without writing something
DataFrame().to_excel(writer)


@pytest.mark.parametrize("write_only", [True, False])
def test_engine_kwargs(ext, write_only):
# GH 42286
# openpyxl doesn't utilize kwargs, only test that supplying a engine_kwarg works
engine_kwargs = {"write_only": write_only}
with tm.ensure_clean(ext) as f:
with ExcelWriter(f, engine="openpyxl", engine_kwargs=engine_kwargs) as writer:
# ExcelWriter won't allow us to close without writing something
DataFrame().to_excel(writer)


@pytest.mark.parametrize(
"mode,expected", [("w", ["baz"]), ("a", ["foo", "bar", "baz"])]
)
Expand Down
19 changes: 0 additions & 19 deletions pandas/tests/io/excel/test_writers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1399,25 +1399,6 @@ def check_called(func):
with tm.ensure_clean("something.xls") as filepath:
check_called(lambda: df.to_excel(filepath, engine="dummy"))

@pytest.mark.parametrize(
"ext",
[
pytest.param(".xlsx", marks=td.skip_if_no("xlsxwriter")),
pytest.param(".xlsx", marks=td.skip_if_no("openpyxl")),
pytest.param(".ods", marks=td.skip_if_no("odf")),
],
)
def test_kwargs_deprecated(self, ext):
# GH 40430
msg = re.escape("Use of **kwargs is deprecated")
with tm.assert_produces_warning(FutureWarning, match=msg):
with tm.ensure_clean(ext) as path:
try:
with ExcelWriter(path, kwarg=1):
pass
except TypeError:
pass

@pytest.mark.parametrize(
"ext",
[
Expand Down
21 changes: 21 additions & 0 deletions pandas/tests/io/excel/test_xlsxwriter.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import re
import warnings

import pytest
Expand Down Expand Up @@ -61,3 +62,23 @@ def test_write_append_mode_raises(ext):
with tm.ensure_clean(ext) as f:
with pytest.raises(ValueError, match=msg):
ExcelWriter(f, engine="xlsxwriter", mode="a")


@pytest.mark.parametrize("nan_inf_to_errors", [True, False])
def test_kwargs(ext, nan_inf_to_errors):
# GH 42286
kwargs = {"options": {"nan_inf_to_errors": nan_inf_to_errors}}
with tm.ensure_clean(ext) as f:
msg = re.escape("Use of **kwargs is deprecated")
with tm.assert_produces_warning(FutureWarning, match=msg):
with ExcelWriter(f, engine="xlsxwriter", **kwargs) as writer:
assert writer.book.nan_inf_to_errors == nan_inf_to_errors


@pytest.mark.parametrize("nan_inf_to_errors", [True, False])
def test_engine_kwargs(ext, nan_inf_to_errors):
# GH 42286
engine_kwargs = {"options": {"nan_inf_to_errors": nan_inf_to_errors}}
with tm.ensure_clean(ext) as f:
with ExcelWriter(f, engine="xlsxwriter", engine_kwargs=engine_kwargs) as writer:
assert writer.book.nan_inf_to_errors == nan_inf_to_errors
26 changes: 26 additions & 0 deletions pandas/tests/io/excel/test_xlwt.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import re

import numpy as np
import pytest

Expand Down Expand Up @@ -97,3 +99,27 @@ def test_option_xls_writer_deprecated(ext):
check_stacklevel=False,
):
options.io.excel.xls.writer = "xlwt"


@pytest.mark.parametrize("write_only", [True, False])
def test_kwargs(ext, write_only):
# GH 42286
# xlwt doesn't utilize kwargs, only test that supplying a kwarg works
kwargs = {"write_only": write_only}
with tm.ensure_clean(ext) as f:
msg = re.escape("Use of **kwargs is deprecated")
with tm.assert_produces_warning(FutureWarning, match=msg):
with ExcelWriter(f, engine="openpyxl", **kwargs) as writer:
# xlwt won't allow us to close without writing something
DataFrame().to_excel(writer)


@pytest.mark.parametrize("write_only", [True, False])
def test_engine_kwargs(ext, write_only):
# GH 42286
# xlwt doesn't utilize kwargs, only test that supplying a engine_kwarg works
engine_kwargs = {"write_only": write_only}
with tm.ensure_clean(ext) as f:
with ExcelWriter(f, engine="openpyxl", engine_kwargs=engine_kwargs) as writer:
# xlwt won't allow us to close without writing something
DataFrame().to_excel(writer)

0 comments on commit 538d69a

Please sign in to comment.