From 300b22dde2de43a935748b1ae36ce68d24d3fba0 Mon Sep 17 00:00:00 2001 From: Richard Shadrach Date: Tue, 6 Dec 2022 16:36:36 -0500 Subject: [PATCH 1/5] DEPR: Remove deprecated ExcelWriter methods --- doc/source/whatsnew/v2.0.0.rst | 2 + pandas/io/excel/_base.py | 93 +-------------------------- pandas/io/excel/_odswriter.py | 10 --- pandas/io/excel/_openpyxl.py | 8 --- pandas/io/excel/_xlsxwriter.py | 16 +---- pandas/tests/io/excel/test_writers.py | 36 ----------- 6 files changed, 4 insertions(+), 161 deletions(-) diff --git a/doc/source/whatsnew/v2.0.0.rst b/doc/source/whatsnew/v2.0.0.rst index 62b0ea5307e41..2b052bf8bc140 100644 --- a/doc/source/whatsnew/v2.0.0.rst +++ b/doc/source/whatsnew/v2.0.0.rst @@ -595,6 +595,8 @@ Removal of prior version deprecations/changes - Enforced deprecation of silently dropping nuisance columns in groupby and resample operations when ``numeric_only=False`` (:issue:`41475`) - Changed default of ``numeric_only`` in various :class:`.DataFrameGroupBy` methods; all methods now default to ``numeric_only=False`` (:issue:`46072`) - Changed default of ``numeric_only`` to ``False`` in :class:`.Resampler` methods (:issue:`47177`) +- Removed deprecated methods :meth:`ExcelWriter.write_cells`, :meth:`ExcelWriter.save`, :meth:`ExcelWriter.cur_sheet`, :meth:`ExcelWriter.handles`, :meth:`ExcelWriter.path` (:issue:`45795`) +- The :class:`ExcelWriter` attribute ``book`` can no longer be set; it is still available to be accessed and mutated (:issue:`48943`) - .. --------------------------------------------------------------------------- diff --git a/pandas/io/excel/_base.py b/pandas/io/excel/_base.py index 6362e892f0012..eba8d1ef30a6b 100644 --- a/pandas/io/excel/_base.py +++ b/pandas/io/excel/_base.py @@ -21,7 +21,6 @@ cast, overload, ) -import warnings import zipfile from pandas._config import config @@ -44,7 +43,6 @@ Appender, doc, ) -from pandas.util._exceptions import find_stack_level from pandas.core.dtypes.common import ( is_bool, @@ -1143,33 +1141,6 @@ def book(self, other) -> None: Set book instance. Class type will depend on the engine used. """ - def write_cells( - self, - cells, - sheet_name: str | None = None, - startrow: int = 0, - startcol: int = 0, - freeze_panes: tuple[int, int] | None = None, - ) -> None: - """ - Write given formatted cells into Excel an excel sheet - - .. deprecated:: 1.5.0 - - Parameters - ---------- - cells : generator - cell of formatted data to save to Excel sheet - sheet_name : str, default None - Name of Excel sheet, if None, then use self.cur_sheet - startrow : upper left cell row to dump data frame - startcol : upper left cell column to dump data frame - freeze_panes: int tuple of length 2 - contains the bottom-most row and right-most column to freeze - """ - self._deprecate("write_cells") - return self._write_cells(cells, sheet_name, startrow, startcol, freeze_panes) - @abc.abstractmethod def _write_cells( self, @@ -1194,15 +1165,6 @@ def _write_cells( contains the bottom-most row and right-most column to freeze """ - def save(self) -> None: - """ - Save workbook to disk. - - .. deprecated:: 1.5.0 - """ - self._deprecate("save") - return self._save() - @abc.abstractmethod def _save(self) -> None: """ @@ -1232,7 +1194,7 @@ def __init__( # the excel backend first read the existing file and then write any data to it mode = mode.replace("a", "r+") - # cast ExcelWriter to avoid adding 'if self.handles is not None' + # cast ExcelWriter to avoid adding 'if self._handles is not None' self._handles = IOHandles( cast(IO[bytes], path), compression={"compression": None} ) @@ -1264,29 +1226,6 @@ def __init__( if_sheet_exists = "error" self._if_sheet_exists = if_sheet_exists - def _deprecate(self, attr: str) -> None: - """ - Deprecate attribute or method for ExcelWriter. - """ - warnings.warn( - f"{attr} is not part of the public API, usage can give unexpected " - "results and will be removed in a future version", - FutureWarning, - stacklevel=find_stack_level(), - ) - - def _deprecate_set_book(self) -> None: - """ - Deprecate setting the book attribute - GH#48780. - """ - warnings.warn( - "Setting the `book` attribute is not part of the public API, " - "usage can give unexpected or corrupted results and will be " - "removed in a future version", - FutureWarning, - stacklevel=find_stack_level(), - ) - @property def date_format(self) -> str: """ @@ -1308,36 +1247,6 @@ def if_sheet_exists(self) -> str: """ return self._if_sheet_exists - @property - def cur_sheet(self): - """ - Current sheet for writing. - - .. deprecated:: 1.5.0 - """ - self._deprecate("cur_sheet") - return self._cur_sheet - - @property - def handles(self) -> IOHandles[bytes]: - """ - Handles to Excel sheets. - - .. deprecated:: 1.5.0 - """ - self._deprecate("handles") - return self._handles - - @property - def path(self): - """ - Path to Excel file. - - .. deprecated:: 1.5.0 - """ - self._deprecate("path") - return self._path - def __fspath__(self) -> str: return getattr(self._handles.handle, "name", "") diff --git a/pandas/io/excel/_odswriter.py b/pandas/io/excel/_odswriter.py index 7c90178226408..5ea3d8d3e43f4 100644 --- a/pandas/io/excel/_odswriter.py +++ b/pandas/io/excel/_odswriter.py @@ -24,8 +24,6 @@ ) if TYPE_CHECKING: - from odf.opendocument import OpenDocumentSpreadsheet - from pandas.io.formats.excel import ExcelCell @@ -72,14 +70,6 @@ def book(self): """ return self._book - @book.setter - def book(self, other: OpenDocumentSpreadsheet) -> None: - """ - Set book instance. Class type will depend on the engine used. - """ - self._deprecate_set_book() - self._book = other - @property def sheets(self) -> dict[str, Any]: """Mapping of sheet names to sheet objects.""" diff --git a/pandas/io/excel/_openpyxl.py b/pandas/io/excel/_openpyxl.py index 85f1e7fda8daa..10bb1dc548313 100644 --- a/pandas/io/excel/_openpyxl.py +++ b/pandas/io/excel/_openpyxl.py @@ -88,14 +88,6 @@ def book(self) -> Workbook: """ return self._book - @book.setter - def book(self, other: Workbook) -> None: - """ - Set book instance. Class type will depend on the engine used. - """ - self._deprecate_set_book() - self._book = other - @property def sheets(self) -> dict[str, Any]: """Mapping of sheet names to sheet objects.""" diff --git a/pandas/io/excel/_xlsxwriter.py b/pandas/io/excel/_xlsxwriter.py index dd583c41a90d0..1800d3d87f7c0 100644 --- a/pandas/io/excel/_xlsxwriter.py +++ b/pandas/io/excel/_xlsxwriter.py @@ -1,9 +1,6 @@ from __future__ import annotations -from typing import ( - TYPE_CHECKING, - Any, -) +from typing import Any from pandas._libs import json from pandas._typing import ( @@ -18,9 +15,6 @@ validate_freeze_panes, ) -if TYPE_CHECKING: - from xlsxwriter import Workbook - class _XlsxStyler: # Map from openpyxl-oriented styles to flatter xlsxwriter representation @@ -224,14 +218,6 @@ def book(self): """ return self._book - @book.setter - def book(self, other: Workbook) -> None: - """ - Set book instance. Class type will depend on the engine used. - """ - self._deprecate_set_book() - self._book = other - @property def sheets(self) -> dict[str, Any]: result = self.book.sheetnames diff --git a/pandas/tests/io/excel/test_writers.py b/pandas/tests/io/excel/test_writers.py index 161587a11fe9e..fa0d54f5ea2dd 100644 --- a/pandas/tests/io/excel/test_writers.py +++ b/pandas/tests/io/excel/test_writers.py @@ -1233,42 +1233,6 @@ def test_to_excel_empty_frame(self, engine, ext): expected = DataFrame() tm.assert_frame_equal(result, expected) - @pytest.mark.parametrize("attr", ["cur_sheet", "handles", "path"]) - def test_deprecated_attr(self, engine, ext, attr): - # GH#45572 - with tm.ensure_clean(ext) as path: - with ExcelWriter(path) as writer: - msg = f"{attr} is not part of the public API" - with tm.assert_produces_warning(FutureWarning, match=msg): - getattr(writer, attr) - # Some engines raise if nothing is written - DataFrame().to_excel(writer) - - @pytest.mark.filterwarnings("ignore:Calling close():UserWarning:xlsxwriter") - @pytest.mark.parametrize( - "attr, args", [("save", ()), ("write_cells", ([], "test"))] - ) - def test_deprecated_method(self, engine, ext, attr, args): - # GH#45572 - with tm.ensure_clean(ext) as path: - with ExcelWriter(path) as writer: - msg = f"{attr} is not part of the public API" - # Some engines raise if nothing is written - DataFrame().to_excel(writer) - with tm.assert_produces_warning(FutureWarning, match=msg): - getattr(writer, attr)(*args) - - def test_deprecated_book_setter(self, engine, ext): - # GH#48780 - with tm.ensure_clean(ext) as path: - with ExcelWriter(path) as writer: - msg = "Setting the `book` attribute is not part of the public API" - # Some engines raise if nothing is written - DataFrame().to_excel(writer) - book = writer.book - with tm.assert_produces_warning(FutureWarning, match=msg): - writer.book = book - class TestExcelWriterEngineTests: @pytest.mark.parametrize( From 119c450a66085b3d6b2abeec2e15c791e738dc5c Mon Sep 17 00:00:00 2001 From: Richard Shadrach Date: Tue, 6 Dec 2022 17:27:07 -0500 Subject: [PATCH 2/5] Fixup --- pandas/io/excel/_base.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pandas/io/excel/_base.py b/pandas/io/excel/_base.py index eba8d1ef30a6b..6f706a4554855 100644 --- a/pandas/io/excel/_base.py +++ b/pandas/io/excel/_base.py @@ -1134,13 +1134,6 @@ def book(self): This attribute can be used to access engine-specific features. """ - @book.setter - @abc.abstractmethod - def book(self, other) -> None: - """ - Set book instance. Class type will depend on the engine used. - """ - @abc.abstractmethod def _write_cells( self, From 5d6b77771911f31503a4ef3595c5b1d6597fc63c Mon Sep 17 00:00:00 2001 From: richard Date: Wed, 7 Dec 2022 22:59:50 -0500 Subject: [PATCH 3/5] asv fix --- asv_bench/benchmarks/io/excel.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/asv_bench/benchmarks/io/excel.py b/asv_bench/benchmarks/io/excel.py index 5bd4d832f3dde..33ca332ad68b6 100644 --- a/asv_bench/benchmarks/io/excel.py +++ b/asv_bench/benchmarks/io/excel.py @@ -57,13 +57,12 @@ def setup(self, engine): def time_write_excel_style(self, engine): bio = BytesIO() bio.seek(0) - writer = ExcelWriter(bio, engine=engine) - df_style = self.df.style - df_style.applymap(lambda x: "border: red 1px solid;") - df_style.applymap(lambda x: "color: blue") - df_style.applymap(lambda x: "border-color: green black", subset=["float1"]) - df_style.to_excel(writer, sheet_name="Sheet1") - writer.save() + with ExcelWriter(bio, engine=engine) as writer: + df_style = self.df.style + df_style.applymap(lambda x: "border: red 1px solid;") + df_style.applymap(lambda x: "color: blue") + df_style.applymap(lambda x: "border-color: green black", subset=["float1"]) + df_style.to_excel(writer, sheet_name="Sheet1") class ReadExcel: From aa957de6c9e15251e72db782b12421a63bbe9a15 Mon Sep 17 00:00:00 2001 From: Richard Shadrach Date: Thu, 8 Dec 2022 17:21:35 -0500 Subject: [PATCH 4/5] Second asv fix --- asv_bench/benchmarks/io/excel.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/asv_bench/benchmarks/io/excel.py b/asv_bench/benchmarks/io/excel.py index 33ca332ad68b6..b65df10700927 100644 --- a/asv_bench/benchmarks/io/excel.py +++ b/asv_bench/benchmarks/io/excel.py @@ -42,9 +42,9 @@ def setup(self, engine): def time_write_excel(self, engine): bio = BytesIO() bio.seek(0) - writer = ExcelWriter(bio, engine=engine) - self.df.to_excel(writer, sheet_name="Sheet1") - writer.save() + with ExcelWriter(bio, engine=engine) as writer: + self.df.to_excel(writer, sheet_name="Sheet1") + writer.save() class WriteExcelStyled: From 38db57ff7c14113c6567a1fc21aed03a89368b72 Mon Sep 17 00:00:00 2001 From: Richard Shadrach Date: Fri, 9 Dec 2022 16:26:18 -0500 Subject: [PATCH 5/5] fixup --- asv_bench/benchmarks/io/excel.py | 1 - 1 file changed, 1 deletion(-) diff --git a/asv_bench/benchmarks/io/excel.py b/asv_bench/benchmarks/io/excel.py index b65df10700927..3cc443f6cfbed 100644 --- a/asv_bench/benchmarks/io/excel.py +++ b/asv_bench/benchmarks/io/excel.py @@ -44,7 +44,6 @@ def time_write_excel(self, engine): bio.seek(0) with ExcelWriter(bio, engine=engine) as writer: self.df.to_excel(writer, sheet_name="Sheet1") - writer.save() class WriteExcelStyled: