diff --git a/asv_bench/asv.conf.json b/asv_bench/asv.conf.json index b1ea2682b7ea7..4a0c882640eb6 100644 --- a/asv_bench/asv.conf.json +++ b/asv_bench/asv.conf.json @@ -54,7 +54,6 @@ "openpyxl": [], "xlsxwriter": [], "xlrd": [], - "xlwt": [], "odfpy": [], "jinja2": [], }, diff --git a/asv_bench/benchmarks/io/excel.py b/asv_bench/benchmarks/io/excel.py index a88c4374b7030..5bd4d832f3dde 100644 --- a/asv_bench/benchmarks/io/excel.py +++ b/asv_bench/benchmarks/io/excel.py @@ -33,7 +33,7 @@ def _generate_dataframe(): class WriteExcel: - params = ["openpyxl", "xlsxwriter", "xlwt"] + params = ["openpyxl", "xlsxwriter"] param_names = ["engine"] def setup(self, engine): @@ -68,10 +68,9 @@ def time_write_excel_style(self, engine): class ReadExcel: - params = ["xlrd", "openpyxl", "odf"] + params = ["openpyxl", "odf"] param_names = ["engine"] fname_excel = "spreadsheet.xlsx" - fname_excel_xls = "spreadsheet.xls" fname_odf = "spreadsheet.ods" def _create_odf(self): @@ -92,13 +91,10 @@ def setup_cache(self): self.df = _generate_dataframe() self.df.to_excel(self.fname_excel, sheet_name="Sheet1") - self.df.to_excel(self.fname_excel_xls, sheet_name="Sheet1") self._create_odf() def time_read_excel(self, engine): - if engine == "xlrd": - fname = self.fname_excel_xls - elif engine == "odf": + if engine == "odf": fname = self.fname_odf else: fname = self.fname_excel @@ -107,9 +103,7 @@ def time_read_excel(self, engine): class ReadExcelNRows(ReadExcel): def time_read_excel(self, engine): - if engine == "xlrd": - fname = self.fname_excel_xls - elif engine == "odf": + if engine == "odf": fname = self.fname_odf else: fname = self.fname_excel diff --git a/ci/code_checks.sh b/ci/code_checks.sh index 113186c746157..c6067faf92d37 100755 --- a/ci/code_checks.sh +++ b/ci/code_checks.sh @@ -47,7 +47,7 @@ import pandas blocklist = {'bs4', 'gcsfs', 'html5lib', 'http', 'ipython', 'jinja2', 'hypothesis', 'lxml', 'matplotlib', 'openpyxl', 'py', 'pytest', 's3fs', 'scipy', - 'tables', 'urllib.request', 'xlrd', 'xlsxwriter', 'xlwt'} + 'tables', 'urllib.request', 'xlrd', 'xlsxwriter'} # GH#28227 for some of these check for top-level modules, while others are # more specific (e.g. urllib.request) diff --git a/ci/deps/actions-310.yaml b/ci/deps/actions-310.yaml index da3578e7191eb..9ebc305a0cb0c 100644 --- a/ci/deps/actions-310.yaml +++ b/ci/deps/actions-310.yaml @@ -51,5 +51,4 @@ dependencies: - xarray - xlrd - xlsxwriter - - xlwt - zstandard diff --git a/ci/deps/actions-38-downstream_compat.yaml b/ci/deps/actions-38-downstream_compat.yaml index 29ad2669afbd2..7e127b9dfebc1 100644 --- a/ci/deps/actions-38-downstream_compat.yaml +++ b/ci/deps/actions-38-downstream_compat.yaml @@ -51,7 +51,6 @@ dependencies: - xarray - xlrd - xlsxwriter - - xlwt - zstandard # downstream packages diff --git a/ci/deps/actions-38-minimum_versions.yaml b/ci/deps/actions-38-minimum_versions.yaml index f92d9958a6248..5540ba01a8f36 100644 --- a/ci/deps/actions-38-minimum_versions.yaml +++ b/ci/deps/actions-38-minimum_versions.yaml @@ -53,5 +53,4 @@ dependencies: - xarray=0.19.0 - xlrd=2.0.1 - xlsxwriter=1.4.3 - - xlwt=1.3.0 - zstandard=0.15.2 diff --git a/ci/deps/actions-38.yaml b/ci/deps/actions-38.yaml index b478b7c900425..825b8aeebfc2f 100644 --- a/ci/deps/actions-38.yaml +++ b/ci/deps/actions-38.yaml @@ -50,5 +50,4 @@ dependencies: - xarray - xlrd - xlsxwriter - - xlwt - zstandard diff --git a/ci/deps/actions-39.yaml b/ci/deps/actions-39.yaml index a12f36ba84cca..1ee96878dbe34 100644 --- a/ci/deps/actions-39.yaml +++ b/ci/deps/actions-39.yaml @@ -51,5 +51,4 @@ dependencies: - xarray - xlrd - xlsxwriter - - xlwt - zstandard diff --git a/ci/deps/circle-38-arm64.yaml b/ci/deps/circle-38-arm64.yaml index 2b65ece881df7..ae4a82d016131 100644 --- a/ci/deps/circle-38-arm64.yaml +++ b/ci/deps/circle-38-arm64.yaml @@ -51,5 +51,4 @@ dependencies: - xarray - xlrd - xlsxwriter - - xlwt - zstandard diff --git a/doc/source/getting_started/install.rst b/doc/source/getting_started/install.rst index 5f258973b3db9..ca45540b637ba 100644 --- a/doc/source/getting_started/install.rst +++ b/doc/source/getting_started/install.rst @@ -336,7 +336,6 @@ Can be managed as optional_extra with ``pandas[excel]``. Dependency Minimum Version optional_extra Notes ========================= ================== =============== ============================================================= xlrd 2.0.1 excel Reading Excel -xlwt 1.3.0 excel Writing Excel xlsxwriter 1.4.3 excel Writing Excel openpyxl 3.0.7 excel Reading / writing for xlsx files pyxlsb 1.0.8 excel Reading for xlsb files diff --git a/doc/source/user_guide/io.rst b/doc/source/user_guide/io.rst index cf4221d055a27..92bd239d51ae4 100644 --- a/doc/source/user_guide/io.rst +++ b/doc/source/user_guide/io.rst @@ -3466,8 +3466,6 @@ See the :ref:`cookbook` for some advanced strategies. .. warning:: - The `xlwt `__ package for writing old-style ``.xls`` - excel files is no longer maintained. The `xlrd `__ package is now only for reading old-style ``.xls`` files. @@ -3481,12 +3479,6 @@ See the :ref:`cookbook` for some advanced strategies. **Please do not report issues when using ``xlrd`` to read ``.xlsx`` files.** This is no longer supported, switch to using ``openpyxl`` instead. - Attempting to use 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. - .. _io.excel_reader: Reading Excel files @@ -3788,7 +3780,7 @@ written. For example: df.to_excel("path_to_file.xlsx", sheet_name="Sheet1") -Files with a ``.xls`` extension will be written using ``xlwt`` and those with a +Files with a ``.xlsx`` extension will be written using ``xlsxwriter`` (if available) or ``openpyxl``. @@ -3849,20 +3841,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 2. the filename extension (via the default specified in config options) By default, pandas uses the `XlsxWriter`_ for ``.xlsx``, `openpyxl`_ -for ``.xlsm``, and `xlwt`_ for ``.xls`` files. If you have multiple +for ``.xlsm``. If you have multiple engines installed, you can set the default engine through :ref:`setting the config options ` ``io.excel.xlsx.writer`` and ``io.excel.xls.writer``. pandas will fall back on `openpyxl`_ for ``.xlsx`` @@ -3870,14 +3855,12 @@ files if `Xlsxwriter`_ is not available. .. _XlsxWriter: https://xlsxwriter.readthedocs.io .. _openpyxl: https://openpyxl.readthedocs.io/ -.. _xlwt: http://www.python-excel.org To specify which writer you want to use, you can pass an engine keyword argument to ``to_excel`` and to ``ExcelWriter``. The built-in engines are: * ``openpyxl``: version 2.4 or higher is required * ``xlsxwriter`` -* ``xlwt`` .. code-block:: python diff --git a/environment.yml b/environment.yml index 391d7bc779af9..f6ef6367800bd 100644 --- a/environment.yml +++ b/environment.yml @@ -53,7 +53,6 @@ dependencies: - xarray - xlrd - xlsxwriter - - xlwt - zstandard # downstream packages diff --git a/pandas/compat/_optional.py b/pandas/compat/_optional.py index 34e3234390ba5..856fb5e4cb66b 100644 --- a/pandas/compat/_optional.py +++ b/pandas/compat/_optional.py @@ -43,7 +43,6 @@ "tabulate": "0.8.9", "xarray": "0.19.0", "xlrd": "2.0.1", - "xlwt": "1.3.0", "xlsxwriter": "1.4.3", "zstandard": "0.15.2", "tzdata": "2022.1", diff --git a/pandas/core/config_init.py b/pandas/core/config_init.py index e6bdbbcb5aa12..b101b25a10a80 100644 --- a/pandas/core/config_init.py +++ b/pandas/core/config_init.py @@ -624,27 +624,11 @@ def use_inf_as_na_cb(key) -> None: auto, {others}. """ -_xls_options = ["xlwt"] _xlsm_options = ["openpyxl"] _xlsx_options = ["openpyxl", "xlsxwriter"] _ods_options = ["odf"] -with cf.config_prefix("io.excel.xls"): - cf.register_option( - "writer", - "auto", - 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( "writer", diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 13a2a00dbc6eb..05494e37256df 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -2260,15 +2260,9 @@ def to_excel( Upper left cell column to dump data frame. engine : str, optional Write engine to use, 'openpyxl' or 'xlsxwriter'. You can also set this - via the options ``io.excel.xlsx.writer``, ``io.excel.xls.writer``, and + via the options ``io.excel.xlsx.writer`` or ``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/__init__.py b/pandas/io/excel/__init__.py index 854e2a1ec3a73..275cbf0148f94 100644 --- a/pandas/io/excel/__init__.py +++ b/pandas/io/excel/__init__.py @@ -7,17 +7,12 @@ from pandas.io.excel._openpyxl import OpenpyxlWriter as _OpenpyxlWriter from pandas.io.excel._util import register_writer from pandas.io.excel._xlsxwriter import XlsxWriter as _XlsxWriter -from pandas.io.excel._xlwt import XlwtWriter as _XlwtWriter __all__ = ["read_excel", "ExcelWriter", "ExcelFile"] register_writer(_OpenpyxlWriter) - -register_writer(_XlwtWriter) - - register_writer(_XlsxWriter) diff --git a/pandas/io/excel/_base.py b/pandas/io/excel/_base.py index 19edb9acf0aa4..bc3abfb94f31c 100644 --- a/pandas/io/excel/_base.py +++ b/pandas/io/excel/_base.py @@ -893,7 +893,6 @@ class ExcelWriter(metaclass=abc.ABCMeta): Default is to use: - * `xlwt `__ for xls files * `xlsxwriter `__ for xlsx files if xlsxwriter is installed otherwise `openpyxl `__ * `odswriter `__ for ods files @@ -911,13 +910,6 @@ 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 @@ -1127,25 +1119,6 @@ def __new__( 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=find_stack_level(), - ) - # for mypy assert engine is not None cls = get_writer(engine) diff --git a/pandas/io/excel/_util.py b/pandas/io/excel/_util.py index c315657170a97..72c64c5ec8939 100644 --- a/pandas/io/excel/_util.py +++ b/pandas/io/excel/_util.py @@ -73,7 +73,6 @@ def get_default_engine(ext: str, mode: Literal["reader", "writer"] = "reader") - "xlsx": "openpyxl", "xlsm": "openpyxl", "xlsb": "pyxlsb", - "xls": "xlwt", "ods": "odf", } assert mode in ["reader", "writer"] diff --git a/pandas/io/excel/_xlwt.py b/pandas/io/excel/_xlwt.py deleted file mode 100644 index f1455e472bb43..0000000000000 --- a/pandas/io/excel/_xlwt.py +++ /dev/null @@ -1,228 +0,0 @@ -from __future__ import annotations - -from typing import ( - TYPE_CHECKING, - Any, - Tuple, - cast, -) - -import pandas._libs.json as json -from pandas._typing import ( - FilePath, - StorageOptions, - WriteExcelBuffer, -) - -from pandas.io.excel._base import ExcelWriter -from pandas.io.excel._util import ( - combine_kwargs, - validate_freeze_panes, -) - -if TYPE_CHECKING: - from xlwt import ( - Workbook, - XFStyle, - ) - - -class XlwtWriter(ExcelWriter): - _engine = "xlwt" - _supported_extensions = (".xls",) - - def __init__( - self, - path: FilePath | WriteExcelBuffer | ExcelWriter, - engine: str | None = None, - date_format: str | None = None, - datetime_format: str | None = None, - encoding: str | None = None, - mode: str = "w", - storage_options: StorageOptions = None, - if_sheet_exists: str | None = None, - engine_kwargs: dict[str, Any] | None = None, - **kwargs, - ) -> None: - # 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!") - - super().__init__( - path, - mode=mode, - storage_options=storage_options, - if_sheet_exists=if_sheet_exists, - engine_kwargs=engine_kwargs, - ) - - if encoding is None: - encoding = "ascii" - self._book = xlwt.Workbook(encoding=encoding, **engine_kwargs) - self._fm_datetime = xlwt.easyxf(num_format_str=self._datetime_format) - self._fm_date = xlwt.easyxf(num_format_str=self._date_format) - - @property - def book(self) -> Workbook: - """ - Book instance of class xlwt.Workbook. - - This attribute can be used to access engine-specific features. - """ - 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.""" - result = {sheet.name: sheet for sheet in self.book._Workbook__worksheets} - return result - - @property - def fm_date(self): - """ - XFStyle formatter for dates. - """ - self._deprecate("fm_date") - return self._fm_date - - @property - def fm_datetime(self): - """ - XFStyle formatter for dates. - """ - self._deprecate("fm_datetime") - return self._fm_datetime - - def _save(self) -> None: - """ - Save workbook to disk. - """ - if self.sheets: - # fails when the ExcelWriter is just opened and then closed - self.book.save(self._handles.handle) - - def _write_cells( - self, - cells, - sheet_name: str | None = None, - startrow: int = 0, - startcol: int = 0, - freeze_panes: tuple[int, int] | None = None, - ) -> None: - - sheet_name = self._get_sheet_name(sheet_name) - - if sheet_name in self.sheets: - wks = self.sheets[sheet_name] - else: - wks = self.book.add_sheet(sheet_name) - self.sheets[sheet_name] = wks - - if validate_freeze_panes(freeze_panes): - freeze_panes = cast(Tuple[int, int], freeze_panes) - wks.set_panes_frozen(True) - wks.set_horz_split_pos(freeze_panes[0]) - wks.set_vert_split_pos(freeze_panes[1]) - - style_dict: dict[str, XFStyle] = {} - - for cell in cells: - val, fmt = self._value_with_fmt(cell.val) - - stylekey = json.dumps(cell.style) - if fmt: - stylekey += fmt - - if stylekey in style_dict: - style = style_dict[stylekey] - else: - style = self._convert_to_style(cell.style, fmt) - style_dict[stylekey] = style - - if cell.mergestart is not None and cell.mergeend is not None: - wks.write_merge( - startrow + cell.row, - startrow + cell.mergestart, - startcol + cell.col, - startcol + cell.mergeend, - val, - style, - ) - else: - wks.write(startrow + cell.row, startcol + cell.col, val, style) - - @classmethod - def _style_to_xlwt( - cls, item, firstlevel: bool = True, field_sep: str = ",", line_sep: str = ";" - ) -> str: - """ - helper which recursively generate an xlwt easy style string - for example: - - hstyle = {"font": {"bold": True}, - "border": {"top": "thin", - "right": "thin", - "bottom": "thin", - "left": "thin"}, - "align": {"horiz": "center"}} - will be converted to - font: bold on; \ - border: top thin, right thin, bottom thin, left thin; \ - align: horiz center; - """ - if hasattr(item, "items"): - if firstlevel: - it = [ - f"{key}: {cls._style_to_xlwt(value, False)}" - for key, value in item.items() - ] - out = f"{line_sep.join(it)} " - return out - else: - it = [ - f"{key} {cls._style_to_xlwt(value, False)}" - for key, value in item.items() - ] - out = f"{field_sep.join(it)} " - return out - else: - item = f"{item}" - item = item.replace("True", "on") - item = item.replace("False", "off") - return item - - @classmethod - def _convert_to_style( - cls, style_dict, num_format_str: str | None = None - ) -> XFStyle: - """ - converts a style_dict to an xlwt style object - - Parameters - ---------- - style_dict : style dictionary to convert - num_format_str : optional number format string - """ - import xlwt - - if style_dict: - xlwt_stylestr = cls._style_to_xlwt(style_dict) - style = xlwt.easyxf(xlwt_stylestr, field_sep=",", line_sep=";") - else: - style = xlwt.XFStyle() - if num_format_str is not None: - style.num_format_str = num_format_str - - return style diff --git a/pandas/io/formats/excel.py b/pandas/io/formats/excel.py index 466ffe5ac1b49..5c9b3a76123c4 100644 --- a/pandas/io/formats/excel.py +++ b/pandas/io/formats/excel.py @@ -879,14 +879,8 @@ def write( is to be frozen engine : string, default None 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. + via the options ``io.excel.xlsx.writer``, + or ``io.excel.xlsm.writer``. {storage_options} diff --git a/pandas/tests/io/__init__.py b/pandas/tests/io/__init__.py index 3231e38b985af..c99d03afc8320 100644 --- a/pandas/tests/io/__init__.py +++ b/pandas/tests/io/__init__.py @@ -20,8 +20,4 @@ r"Use 'tree.iter\(\)' or 'list\(tree.iter\(\)\)' instead." ":PendingDeprecationWarning" ), - # GH 26552 - pytest.mark.filterwarnings( - "ignore:As the xlwt package is no longer maintained:FutureWarning" - ), ] diff --git a/pandas/tests/io/excel/__init__.py b/pandas/tests/io/excel/__init__.py index e7a182ea63178..9136153101e23 100644 --- a/pandas/tests/io/excel/__init__.py +++ b/pandas/tests/io/excel/__init__.py @@ -9,10 +9,6 @@ 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" - ), # GH 38571 pytest.mark.filterwarnings( "ignore:.*In xlrd >= 2.0, only the xls format is supported:FutureWarning" diff --git a/pandas/tests/io/excel/test_readers.py b/pandas/tests/io/excel/test_readers.py index 62c93de4d44aa..8ad15ac05e26a 100644 --- a/pandas/tests/io/excel/test_readers.py +++ b/pandas/tests/io/excel/test_readers.py @@ -538,8 +538,8 @@ def test_reader_dtype_str(self, read_ext, dtype, expected): def test_use_nullable_dtypes(self, read_ext): # GH#36712 - if read_ext == ".xlsb": - pytest.skip("No engine for filetype: 'xlsb'") + if read_ext in (".xlsb", ".xls"): + pytest.skip(f"No engine for filetype: '{read_ext}'") df = DataFrame( { @@ -564,8 +564,8 @@ def test_use_nullable_dtypes(self, read_ext): def test_use_nullabla_dtypes_and_dtype(self, read_ext): # GH#36712 - if read_ext == ".xlsb": - pytest.skip("No engine for filetype: 'xlsb'") + if read_ext in (".xlsb", ".xls"): + pytest.skip(f"No engine for filetype: '{read_ext}'") df = DataFrame({"a": [np.nan, 1.0], "b": [2.5, np.nan]}) with tm.ensure_clean(read_ext) as file_path: @@ -579,8 +579,8 @@ def test_use_nullabla_dtypes_and_dtype(self, read_ext): @pytest.mark.parametrize("storage", ["pyarrow", "python"]) def test_use_nullabla_dtypes_string(self, read_ext, storage): # GH#36712 - if read_ext == ".xlsb": - pytest.skip("No engine for filetype: 'xlsb'") + if read_ext in (".xlsb", ".xls"): + pytest.skip(f"No engine for filetype: '{read_ext}'") import pyarrow as pa diff --git a/pandas/tests/io/excel/test_writers.py b/pandas/tests/io/excel/test_writers.py index 897d6969ea6ae..307f8b7a7798f 100644 --- a/pandas/tests/io/excel/test_writers.py +++ b/pandas/tests/io/excel/test_writers.py @@ -27,7 +27,6 @@ ExcelWriter, _OpenpyxlWriter, _XlsxWriter, - _XlwtWriter, register_writer, ) @@ -61,7 +60,6 @@ def set_engine(engine, ext): [ pytest.param(".xlsx", marks=[td.skip_if_no("openpyxl"), td.skip_if_no("xlrd")]), pytest.param(".xlsm", marks=[td.skip_if_no("openpyxl"), td.skip_if_no("xlrd")]), - pytest.param(".xls", marks=[td.skip_if_no("xlwt"), td.skip_if_no("xlrd")]), pytest.param( ".xlsx", marks=[td.skip_if_no("xlsxwriter"), td.skip_if_no("xlrd")] ), @@ -319,9 +317,6 @@ def test_multiindex_interval_datetimes(self, ext): ".xlsm", marks=[td.skip_if_no("openpyxl"), td.skip_if_no("xlrd")], ), - pytest.param( - "xlwt", ".xls", marks=[td.skip_if_no("xlwt"), td.skip_if_no("xlrd")] - ), pytest.param( "xlsxwriter", ".xlsx", @@ -1283,7 +1278,6 @@ class TestExcelWriterEngineTests: [ pytest.param(_XlsxWriter, ".xlsx", marks=td.skip_if_no("xlsxwriter")), pytest.param(_OpenpyxlWriter, ".xlsx", marks=td.skip_if_no("openpyxl")), - pytest.param(_XlwtWriter, ".xls", marks=td.skip_if_no("xlwt")), ], ) def test_ExcelWriter_dispatch(self, klass, ext): diff --git a/pandas/tests/io/excel/test_xlrd.py b/pandas/tests/io/excel/test_xlrd.py index 86141f08f5f2d..30dddbd7de50b 100644 --- a/pandas/tests/io/excel/test_xlrd.py +++ b/pandas/tests/io/excel/test_xlrd.py @@ -11,12 +11,6 @@ from pandas.io.excel._base import inspect_excel_format xlrd = pytest.importorskip("xlrd") -xlwt = pytest.importorskip("xlwt") - -pytestmark = pytest.mark.filterwarnings( - "ignore:As the xlwt package is no longer maintained:FutureWarning" -) - exts = [".xls"] @@ -31,23 +25,18 @@ def read_ext_xlrd(request): return request.param -def test_read_xlrd_book(read_ext_xlrd, frame): - df = frame - +def test_read_xlrd_book(read_ext_xlrd, datapath): engine = "xlrd" - sheet_name = "SheetA" - - with tm.ensure_clean(read_ext_xlrd) as pth: - df.to_excel(pth, sheet_name) - with xlrd.open_workbook(pth) as book: - with ExcelFile(book, engine=engine) as xl: - result = pd.read_excel(xl, sheet_name=sheet_name, index_col=0) - tm.assert_frame_equal(df, result) - - result = pd.read_excel( - book, sheet_name=sheet_name, engine=engine, index_col=0 - ) - tm.assert_frame_equal(df, result) + sheet_name = "Sheet1" + pth = datapath("io", "data", "excel", "test1.xls") + with xlrd.open_workbook(pth) as book: + with ExcelFile(book, engine=engine) as xl: + result = pd.read_excel(xl, sheet_name=sheet_name, index_col=0) + + expected = pd.read_excel( + book, sheet_name=sheet_name, engine=engine, index_col=0 + ) + tm.assert_frame_equal(result, expected) def test_excel_file_warning_with_xlsx_file(datapath): diff --git a/pandas/tests/io/excel/test_xlwt.py b/pandas/tests/io/excel/test_xlwt.py deleted file mode 100644 index 3aa405eb1e275..0000000000000 --- a/pandas/tests/io/excel/test_xlwt.py +++ /dev/null @@ -1,146 +0,0 @@ -import re - -import numpy as np -import pytest - -from pandas import ( - DataFrame, - MultiIndex, - options, -) -import pandas._testing as tm - -from pandas.io.excel import ( - ExcelWriter, - _XlwtWriter, -) - -xlwt = pytest.importorskip("xlwt") - -pytestmark = pytest.mark.parametrize("ext,", [".xls"]) - - -def test_excel_raise_error_on_multiindex_columns_and_no_index(ext): - # MultiIndex as columns is not yet implemented 9794 - cols = MultiIndex.from_tuples( - [("site", ""), ("2014", "height"), ("2014", "weight")] - ) - df = DataFrame(np.random.randn(10, 3), columns=cols) - - msg = ( - "Writing to Excel with MultiIndex columns and no index " - "\\('index'=False\\) is not yet implemented." - ) - with pytest.raises(NotImplementedError, match=msg): - with tm.ensure_clean(ext) as path: - df.to_excel(path, index=False) - - -def test_excel_multiindex_columns_and_index_true(ext): - cols = MultiIndex.from_tuples( - [("site", ""), ("2014", "height"), ("2014", "weight")] - ) - df = DataFrame(np.random.randn(10, 3), columns=cols) - with tm.ensure_clean(ext) as path: - df.to_excel(path, index=True) - - -def test_excel_multiindex_index(ext): - # MultiIndex as index works so assert no error #9794 - cols = MultiIndex.from_tuples( - [("site", ""), ("2014", "height"), ("2014", "weight")] - ) - df = DataFrame(np.random.randn(3, 10), index=cols) - with tm.ensure_clean(ext) as path: - df.to_excel(path, index=False) - - -def test_to_excel_styleconverter(ext): - hstyle = { - "font": {"bold": True}, - "borders": {"top": "thin", "right": "thin", "bottom": "thin", "left": "thin"}, - "alignment": {"horizontal": "center", "vertical": "top"}, - } - - xls_style = _XlwtWriter._convert_to_style(hstyle) - assert xls_style.font.bold - assert xlwt.Borders.THIN == xls_style.borders.top - assert xlwt.Borders.THIN == xls_style.borders.right - assert xlwt.Borders.THIN == xls_style.borders.bottom - assert xlwt.Borders.THIN == xls_style.borders.left - assert xlwt.Alignment.HORZ_CENTER == xls_style.alignment.horz - assert xlwt.Alignment.VERT_TOP == xls_style.alignment.vert - - -def test_write_append_mode_raises(ext): - msg = "Append mode is not supported with xlwt!" - - 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" - - -@pytest.mark.parametrize("style_compression", [0, 2]) -def test_kwargs(ext, style_compression): - # GH 42286 - kwargs = {"style_compression": style_compression} - 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="xlwt", **kwargs) as writer: - assert ( - writer.book._Workbook__styles.style_compression == style_compression - ) - # xlwt won't allow us to close without writing something - DataFrame().to_excel(writer) - - -@pytest.mark.parametrize("style_compression", [0, 2]) -def test_engine_kwargs(ext, style_compression): - # GH 42286 - engine_kwargs = {"style_compression": style_compression} - with tm.ensure_clean(ext) as f: - with ExcelWriter(f, engine="xlwt", engine_kwargs=engine_kwargs) as writer: - assert writer.book._Workbook__styles.style_compression == style_compression - # xlwt won't allow us to close without writing something - DataFrame().to_excel(writer) - - -def test_book_and_sheets_consistent(ext): - # GH#45687 - Ensure sheets is updated if user modifies book - with tm.ensure_clean(ext) as f: - with ExcelWriter(f) as writer: - assert writer.sheets == {} - sheet = writer.book.add_sheet("test_name") - assert writer.sheets == {"test_name": sheet} - - -@pytest.mark.parametrize("attr", ["fm_date", "fm_datetime"]) -def test_deprecated_attr(ext, attr): - # GH#45572 - with tm.ensure_clean(ext) as path: - with ExcelWriter(path, engine="xlwt") as writer: - msg = f"{attr} is not part of the public API" - with tm.assert_produces_warning(FutureWarning, match=msg): - getattr(writer, attr) diff --git a/pandas/tests/io/test_common.py b/pandas/tests/io/test_common.py index 4a6ec7cfd2ae3..ec48357e0395d 100644 --- a/pandas/tests/io/test_common.py +++ b/pandas/tests/io/test_common.py @@ -342,7 +342,7 @@ def test_read_fspath_all(self, reader, module, path, datapath): "writer_name, writer_kwargs, module", [ ("to_csv", {}, "os"), - ("to_excel", {"engine": "xlwt"}, "xlwt"), + ("to_excel", {"engine": "openpyxl"}, "openpyxl"), ("to_feather", {}, "pyarrow"), ("to_html", {}, "os"), ("to_json", {}, "os"), diff --git a/pandas/tests/io/test_fsspec.py b/pandas/tests/io/test_fsspec.py index 82f5bdda2a4c5..bdd4cd95ec1af 100644 --- a/pandas/tests/io/test_fsspec.py +++ b/pandas/tests/io/test_fsspec.py @@ -74,13 +74,9 @@ def test_to_csv(cleared_fs, df1): tm.assert_frame_equal(df1, df2) -@pytest.mark.parametrize("ext", ["xls", "xlsx"]) -def test_to_excel(cleared_fs, ext, df1): - if ext == "xls": - pytest.importorskip("xlwt") - else: - pytest.importorskip("openpyxl") - +def test_to_excel(cleared_fs, df1): + pytest.importorskip("openpyxl") + ext = "xlsx" path = f"memory://test/test.{ext}" df1.to_excel(path, index=True) @@ -132,12 +128,9 @@ def test_read_table_options(fsspectest): assert fsspectest.test[0] == "csv_read" -@pytest.mark.parametrize("extension", ["xlsx", "xls"]) -def test_excel_options(fsspectest, extension): - if extension == "xls": - pytest.importorskip("xlwt") - else: - pytest.importorskip("openpyxl") +def test_excel_options(fsspectest): + pytest.importorskip("openpyxl") + extension = "xlsx" df = DataFrame({"a": [0]}) diff --git a/pandas/tests/io/test_gcs.py b/pandas/tests/io/test_gcs.py index 3e94a31b3b25c..e3333025da547 100644 --- a/pandas/tests/io/test_gcs.py +++ b/pandas/tests/io/test_gcs.py @@ -74,7 +74,7 @@ def test_to_read_gcs(gcs_buffer, format): df1.to_csv(path, index=True) df2 = read_csv(path, parse_dates=["dt"], index_col=0) elif format == "excel": - path = "gs://test/test.xls" + path = "gs://test/test.xlsx" df1.to_excel(path) df2 = read_excel(path, parse_dates=["dt"], index_col=0) elif format == "json": diff --git a/requirements-dev.txt b/requirements-dev.txt index 90e4c6dca5ff1..5e98113625374 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -44,7 +44,6 @@ tzdata>=2022.1 xarray xlrd xlsxwriter -xlwt zstandard aiobotocore<2.0.0 botocore diff --git a/setup.cfg b/setup.cfg index eede4a66d598d..d80d09725d18b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -78,7 +78,6 @@ excel = openpyxl>=3.0.7 pyxlsb>=1.0.8 xlrd>=2.0.1 - xlwt>=1.3.0 xlsxwriter>=1.4.3 parquet = pyarrow>=6.0.0 @@ -158,7 +157,6 @@ all = xarray>=0.19.0 xlrd>=2.0.1 xlsxwriter>=1.4.3 - xlwt>=1.3.0 zstandard>=0.15.2 [build_ext]