Skip to content

REGR: ExcelWriter fails when passed kwargs #42292

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)