Skip to content

Commit f2c6de4

Browse files
9en9icmp0xff
andauthored
TYP: enhance ExcelWriter with generic type support and overloads (#1561)
* TYP: enhance `ExcelWriter` with generic type support and overloads * Update pandas-stubs/io/excel/_base.pyi Co-authored-by: Yi-Fan Wang <[email protected]> * fix(types): narrow ExcelWriter path union to FilePath | BinaryIO Co-authored-by: Yi-Fan Wang <[email protected]> * fix(types): remove default value from ExcelWriter engine literal Co-authored-by: Yi-Fan Wang <[email protected]> * fix(types): remove default value from ExcelWriter engine literal Co-authored-by: Yi-Fan Wang <[email protected]> * fix(types): remove default value from ExcelWriter engine literal Co-authored-by: Yi-Fan Wang <[email protected]> * fix(types): update ExcelWriter path type to use BinaryIO instead of WriteExcelBuffer * fix(types): refactor ExcelWriter constructor to use __new__ and improve parameter defaults --------- Co-authored-by: Yi-Fan Wang <[email protected]>
1 parent b4c3b56 commit f2c6de4

File tree

3 files changed

+88
-19
lines changed

3 files changed

+88
-19
lines changed

pandas-stubs/io/excel/_base.pyi

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,25 @@ from collections.abc import (
88
from types import TracebackType
99
from typing import (
1010
Any,
11+
BinaryIO,
12+
Generic,
1113
Literal,
14+
TypeAlias,
1215
overload,
1316
)
1417

1518
from odf.opendocument import OpenDocument # pyright: ignore[reportMissingTypeStubs]
1619
from openpyxl.workbook.workbook import Workbook
1720
from pandas.core.frame import DataFrame
1821
import pyxlsb.workbook # pyright: ignore[reportMissingTypeStubs]
19-
from typing_extensions import Self
22+
from typing_extensions import (
23+
Self,
24+
TypeVar,
25+
)
2026
from xlrd.book import Book
27+
from xlsxwriter.workbook import ( # pyright: ignore[reportMissingTypeStubs]
28+
Workbook as XlsxWorkbook,
29+
)
2130

2231
from pandas._libs.lib import _NoDefaultDoNotUse
2332
from pandas._typing import (
@@ -32,7 +41,6 @@ from pandas._typing import (
3241
ReadBuffer,
3342
StorageOptions,
3443
UsecolsArgType,
35-
WriteExcelBuffer,
3644
)
3745

3846
@overload
@@ -209,26 +217,67 @@ def read_excel(
209217
engine_kwargs: dict[str, Any] | None = ...,
210218
) -> DataFrame: ...
211219

212-
class ExcelWriter:
213-
def __init__(
214-
self,
215-
path: FilePath | WriteExcelBuffer | ExcelWriter,
216-
engine: ExcelWriteEngine | Literal["auto"] | None = ...,
217-
date_format: str | None = ...,
218-
datetime_format: str | None = ...,
219-
mode: Literal["w", "a"] = ...,
220-
storage_options: StorageOptions = ...,
221-
if_sheet_exists: ExcelWriterIfSheetExists | None = ...,
222-
engine_kwargs: dict[str, Any] | None = ...,
223-
) -> None: ...
220+
ExcelWriteWorkbook: TypeAlias = Workbook | OpenDocument | XlsxWorkbook
221+
222+
_WorkbookT = TypeVar("_WorkbookT", default=ExcelWriteWorkbook, bound=ExcelWriteWorkbook)
223+
224+
class ExcelWriter(Generic[_WorkbookT]):
225+
@overload
226+
def __new__(
227+
cls,
228+
path: FilePath | BinaryIO,
229+
engine: Literal["openpyxl"],
230+
date_format: str | None = None,
231+
datetime_format: str | None = None,
232+
mode: Literal["w", "a"] = "w",
233+
storage_options: StorageOptions = None,
234+
if_sheet_exists: ExcelWriterIfSheetExists | None = None,
235+
engine_kwargs: Mapping[str, Any] | None = None,
236+
) -> ExcelWriter[Workbook]: ...
237+
@overload
238+
def __new__(
239+
cls,
240+
path: FilePath | BinaryIO,
241+
engine: Literal["odf"],
242+
date_format: str | None = None,
243+
datetime_format: str | None = None,
244+
mode: Literal["w", "a"] = "w",
245+
storage_options: StorageOptions = None,
246+
if_sheet_exists: ExcelWriterIfSheetExists | None = None,
247+
engine_kwargs: Mapping[str, Any] | None = None,
248+
) -> ExcelWriter[OpenDocument]: ...
249+
@overload
250+
def __new__(
251+
cls,
252+
path: FilePath | BinaryIO,
253+
engine: Literal["xlsxwriter"],
254+
date_format: str | None = None,
255+
datetime_format: str | None = None,
256+
mode: Literal["w", "a"] = "w",
257+
storage_options: StorageOptions = None,
258+
if_sheet_exists: ExcelWriterIfSheetExists | None = None,
259+
engine_kwargs: Mapping[str, Any] | None = None,
260+
) -> ExcelWriter[XlsxWorkbook]: ...
261+
@overload
262+
def __new__(
263+
cls,
264+
path: FilePath | BinaryIO,
265+
engine: Literal["auto"] | None = None,
266+
date_format: str | None = None,
267+
datetime_format: str | None = None,
268+
mode: Literal["w", "a"] = "w",
269+
storage_options: StorageOptions = None,
270+
if_sheet_exists: ExcelWriterIfSheetExists | None = None,
271+
engine_kwargs: Mapping[str, Any] | None = None,
272+
) -> ExcelWriter[ExcelWriteWorkbook]: ...
224273
@property
225274
def supported_extensions(self) -> tuple[str, ...]: ...
226275
@property
227276
def engine(self) -> ExcelWriteEngine: ...
228277
@property
229278
def sheets(self) -> dict[str, Any]: ...
230279
@property
231-
def book(self) -> Workbook | OpenDocument: ...
280+
def book(self) -> _WorkbookT: ...
232281
@property
233282
def date_format(self) -> str: ...
234283
@property

pandas-stubs/io/formats/style.pyi

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@ from typing import (
1313
)
1414

1515
from matplotlib.colors import Colormap
16+
from openpyxl.workbook.workbook import Workbook as OpenXlWorkbook
1617
from pandas.core.frame import DataFrame
1718
from pandas.core.series import Series
1819
from typing_extensions import Self
20+
from xlsxwriter.workbook import ( # pyright: ignore[reportMissingTypeStubs]
21+
Workbook as XlsxWorkbook,
22+
)
1923

2024
from pandas._typing import (
2125
Axis,
@@ -103,7 +107,9 @@ class Styler(StylerRenderer):
103107
) -> Styler: ...
104108
def to_excel(
105109
self,
106-
excel_writer: FilePath | WriteExcelBuffer | ExcelWriter,
110+
excel_writer: (
111+
FilePath | WriteExcelBuffer | ExcelWriter[OpenXlWorkbook | XlsxWorkbook]
112+
),
107113
sheet_name: str = "Sheet1",
108114
na_rep: str = "",
109115
float_format: str | None = None,

tests/test_io.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
)
1414

1515
import numpy as np
16+
from odf.opendocument import OpenDocument # pyright: ignore[reportMissingTypeStubs]
17+
from openpyxl.workbook.workbook import Workbook as OpenXlWorkbook
1618
import pandas as pd
1719
from pandas import (
1820
DataFrame,
@@ -45,6 +47,9 @@
4547
import sqlalchemy.orm
4648
import sqlalchemy.orm.decl_api
4749
from typing_extensions import assert_type
50+
from xlsxwriter.workbook import ( # pyright: ignore[reportMissingTypeStubs]
51+
Workbook as XlsxWorkbook,
52+
)
4853

4954
from tests import (
5055
TYPE_CHECKING_INVALID_USAGE,
@@ -1188,7 +1193,10 @@ def test_excel_writer_engine() -> None:
11881193

11891194
with ensure_clean(".xlsx") as path:
11901195
with pd.ExcelWriter(path, engine="openpyxl") as ew:
1191-
check(assert_type(ew, pd.ExcelWriter), pd.ExcelWriter)
1196+
check(
1197+
assert_type(ew, pd.ExcelWriter[OpenXlWorkbook]),
1198+
pd.ExcelWriter[OpenXlWorkbook],
1199+
)
11921200
DF.to_excel(ew, sheet_name="A")
11931201
check(
11941202
assert_type(ew.engine, Literal["openpyxl", "odf", "xlsxwriter"]),
@@ -1197,7 +1205,10 @@ def test_excel_writer_engine() -> None:
11971205

11981206
with ensure_clean(".ods") as path:
11991207
with pd.ExcelWriter(path, engine="odf") as ew:
1200-
check(assert_type(ew, pd.ExcelWriter), pd.ExcelWriter)
1208+
check(
1209+
assert_type(ew, pd.ExcelWriter[OpenDocument]),
1210+
pd.ExcelWriter[OpenDocument],
1211+
)
12011212
DF.to_excel(ew, sheet_name="A")
12021213
check(
12031214
assert_type(ew.engine, Literal["openpyxl", "odf", "xlsxwriter"]),
@@ -1206,7 +1217,10 @@ def test_excel_writer_engine() -> None:
12061217

12071218
with ensure_clean(".xlsx") as path:
12081219
with pd.ExcelWriter(path, engine="xlsxwriter") as ew:
1209-
check(assert_type(ew, pd.ExcelWriter), pd.ExcelWriter)
1220+
check(
1221+
assert_type(ew, pd.ExcelWriter[XlsxWorkbook]),
1222+
pd.ExcelWriter[XlsxWorkbook],
1223+
)
12101224
DF.to_excel(ew, sheet_name="A")
12111225
check(
12121226
assert_type(ew.engine, Literal["openpyxl", "odf", "xlsxwriter"]),

0 commit comments

Comments
 (0)