diff --git a/doc/source/user_guide/io.rst b/doc/source/user_guide/io.rst index 2b324a74fffaf..965833c013c03 100644 --- a/doc/source/user_guide/io.rst +++ b/doc/source/user_guide/io.rst @@ -3200,6 +3200,13 @@ pandas supports writing Excel files to buffer-like objects such as ``StringIO`` Excel writer engines '''''''''''''''''''' +.. deprecated:: 1.2.0 + + As the `xlwt `__ package is no longer + maintained, the ``xlwt`` engine will be removed from a future version + of pandas. This is the only engine in pandas that supports writing to + ``.xls`` files. + pandas chooses an Excel writer via two methods: 1. the ``engine`` keyword argument diff --git a/doc/source/user_guide/options.rst b/doc/source/user_guide/options.rst index c828bc28826b1..b8e75b0535823 100644 --- a/doc/source/user_guide/options.rst +++ b/doc/source/user_guide/options.rst @@ -432,6 +432,16 @@ display.html.use_mathjax True When True, Jupyter notebook dollar symbol. io.excel.xls.writer xlwt The default Excel writer engine for 'xls' files. + + .. deprecated:: 1.2.0 + + As `xlwt `__ + package is no longer maintained, the ``xlwt`` + engine will be removed in a future version of + pandas. Since this is the only engine in pandas + that supports writing to ``.xls`` files, + this option will also be removed. + io.excel.xlsm.writer openpyxl The default Excel writer engine for 'xlsm' files. Available options: 'openpyxl' (the default). diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index 5c4472749e11f..84227319bf6a0 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -10,12 +10,27 @@ including other versions of pandas. .. warning:: + The packages `xlrd `_ for reading excel + files and `xlwt `_ for + writing excel files are no longer maintained. These are the only engines in pandas + that support the xls format. + Previously, the default argument ``engine=None`` to ``pd.read_excel`` - would result in using the `xlrd `_ engine in - many cases. The engine ``xlrd`` is no longer maintained, and is not supported with - python >= 3.9. If `openpyxl `_ is installed, - many of these cases will now default to using the ``openpyxl`` engine. See the - :func:`read_excel` documentation for more details. + would result in using the ``xlrd`` engine in many cases. If + `openpyxl `_ is installed, + many of these cases will now default to using the ``openpyxl`` engine. + See the :func:`read_excel` documentation for more details. Attempting to read + ``.xls`` files or specifying ``engine="xlrd"`` to ``pd.read_excel`` will not + raise a warning. However users should be aware that ``xlrd`` is already + broken with certain package configurations, for example with Python 3.9 + when `defusedxml `_ is installed, and + is anticipated to be unusable in the future. + + Attempting to use the the ``xlwt`` engine will raise a ``FutureWarning`` + unless the option :attr:`io.excel.xls.writer` is set to ``"xlwt"``. + While this option is now deprecated and will also raise a ``FutureWarning``, + it can be globally set and the warning suppressed. Users are recommended to + write ``.xlsx`` files using the ``openpyxl`` engine instead. .. --------------------------------------------------------------------------- diff --git a/pandas/core/config_init.py b/pandas/core/config_init.py index 541ecd9df6fc7..7d9664bd9f965 100644 --- a/pandas/core/config_init.py +++ b/pandas/core/config_init.py @@ -581,6 +581,13 @@ def use_inf_as_na_cb(key): writer_engine_doc.format(ext="xls", others=", ".join(_xls_options)), validator=str, ) +cf.deprecate_option( + "io.excel.xls.writer", + msg="As the xlwt package is no longer maintained, the xlwt engine will be " + "removed in a future version of pandas. This is the only engine in pandas that " + "supports writing in the xls format. Install openpyxl and write to an " + "xlsx file instead.", +) with cf.config_prefix("io.excel.xlsm"): cf.register_option( diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 637897391f6bb..b851c4d7d4931 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -2093,6 +2093,13 @@ def to_excel( Write engine to use, 'openpyxl' or 'xlsxwriter'. You can also set this via the options ``io.excel.xlsx.writer``, ``io.excel.xls.writer``, and ``io.excel.xlsm.writer``. + + .. deprecated:: 1.2.0 + + As the `xlwt `__ package is no longer + maintained, the ``xlwt`` engine will be removed in a future version + of pandas. + merge_cells : bool, default True Write MultiIndex and Hierarchical Rows as merged cells. encoding : str, optional diff --git a/pandas/io/excel/_base.py b/pandas/io/excel/_base.py index 626c3df196380..bf1011176693f 100644 --- a/pandas/io/excel/_base.py +++ b/pandas/io/excel/_base.py @@ -587,6 +587,13 @@ class ExcelWriter(metaclass=abc.ABCMeta): Engine to use for writing. If None, defaults to ``io.excel..writer``. NOTE: can only be passed as a keyword argument. + + .. deprecated:: 1.2.0 + + As the `xlwt `__ package is no longer + maintained, the ``xlwt`` engine will be removed in a future + version of pandas. + date_format : str, default None Format string for dates written into Excel files (e.g. 'YYYY-MM-DD'). datetime_format : str, default None @@ -691,11 +698,31 @@ def __new__(cls, path, engine=None, **kwargs): ext = "xlsx" try: - engine = config.get_option(f"io.excel.{ext}.writer") + engine = config.get_option(f"io.excel.{ext}.writer", silent=True) if engine == "auto": engine = get_default_writer(ext) except KeyError as err: raise ValueError(f"No engine for filetype: '{ext}'") from err + + if engine == "xlwt": + xls_config_engine = config.get_option( + "io.excel.xls.writer", silent=True + ) + # Don't warn a 2nd time if user has changed the default engine for xls + if xls_config_engine != "xlwt": + warnings.warn( + "As the xlwt package is no longer maintained, the xlwt " + "engine will be removed in a future version of pandas. " + "This is the only engine in pandas that supports writing " + "in the xls format. Install openpyxl and write to an xlsx " + "file instead. You can set the option io.excel.xls.writer " + "to 'xlwt' to silence this warning. While this option is " + "deprecated and will also raise a warning, it can " + "be globally set and the warning suppressed.", + FutureWarning, + stacklevel=4, + ) + cls = get_writer(engine) return object.__new__(cls) diff --git a/pandas/io/formats/excel.py b/pandas/io/formats/excel.py index bded853f383e0..be8f2de1d53fb 100644 --- a/pandas/io/formats/excel.py +++ b/pandas/io/formats/excel.py @@ -805,6 +805,13 @@ def write( write engine to use if writer is a path - you can also set this via the options ``io.excel.xlsx.writer``, ``io.excel.xls.writer``, and ``io.excel.xlsm.writer``. + + .. deprecated:: 1.2.0 + + As the `xlwt `__ package is no longer + maintained, the ``xlwt`` engine will be removed in a future + version of pandas. + {storage_options} .. versionadded:: 1.2.0 diff --git a/pandas/tests/io/excel/__init__.py b/pandas/tests/io/excel/__init__.py index 419761cbe1d6d..384f1006c44df 100644 --- a/pandas/tests/io/excel/__init__.py +++ b/pandas/tests/io/excel/__init__.py @@ -9,4 +9,8 @@ pytest.mark.filterwarnings( "ignore:This method will be removed in future versions:DeprecationWarning" ), + # GH 26552 + pytest.mark.filterwarnings( + "ignore:As the xlwt package is no longer maintained:FutureWarning" + ), ] diff --git a/pandas/tests/io/excel/test_xlwt.py b/pandas/tests/io/excel/test_xlwt.py index 6bfd71beed0ca..ac53a7d5aee69 100644 --- a/pandas/tests/io/excel/test_xlwt.py +++ b/pandas/tests/io/excel/test_xlwt.py @@ -1,7 +1,7 @@ import numpy as np import pytest -from pandas import DataFrame, MultiIndex +from pandas import DataFrame, MultiIndex, options import pandas._testing as tm from pandas.io.excel import ExcelWriter, _XlwtWriter @@ -69,3 +69,24 @@ def test_write_append_mode_raises(ext): with tm.ensure_clean(ext) as f: with pytest.raises(ValueError, match=msg): ExcelWriter(f, engine="xlwt", mode="a") + + +def test_to_excel_xlwt_warning(ext): + # GH 26552 + df = DataFrame(np.random.randn(3, 10)) + with tm.ensure_clean(ext) as path: + with tm.assert_produces_warning( + FutureWarning, + match="As the xlwt package is no longer maintained", + ): + df.to_excel(path) + + +def test_option_xls_writer_deprecated(ext): + # GH 26552 + with tm.assert_produces_warning( + FutureWarning, + match="As the xlwt package is no longer maintained", + check_stacklevel=False, + ): + options.io.excel.xls.writer = "xlwt"