diff --git a/asv_bench/benchmarks/io/excel.py b/asv_bench/benchmarks/io/excel.py index 5bd4d832f3dde..3cc443f6cfbed 100644 --- a/asv_bench/benchmarks/io/excel.py +++ b/asv_bench/benchmarks/io/excel.py @@ -42,9 +42,8 @@ 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") class WriteExcelStyled: @@ -57,13 +56,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: diff --git a/doc/source/whatsnew/v2.0.0.rst b/doc/source/whatsnew/v2.0.0.rst index d6e0bb2ae0830..94febf054f626 100644 --- a/doc/source/whatsnew/v2.0.0.rst +++ b/doc/source/whatsnew/v2.0.0.rst @@ -640,6 +640,8 @@ Removal of prior version deprecations/changes - Changed default of ``numeric_only`` to ``False`` in :class:`.Resampler` methods (:issue:`47177`) - Using the method :meth:`DataFrameGroupBy.transform` with a callable that returns DataFrames will align to the input's index (:issue:`47244`) - When providing a list of columns of length one to :meth:`DataFrame.groupby`, the keys that are returned by iterating over the resulting :class:`DataFrameGroupBy` object will now be tuples of length one (:issue:`47761`) +- 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..6f706a4554855 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, @@ -1136,40 +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. - """ - - 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 +1158,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 +1187,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 +1219,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 +1240,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(