diff --git a/pandas-stubs/_typing.pyi b/pandas-stubs/_typing.pyi index 0f1e5bf28..fd71f408f 100644 --- a/pandas-stubs/_typing.pyi +++ b/pandas-stubs/_typing.pyi @@ -780,4 +780,7 @@ TimeGrouperOrigin: TypeAlias = ( Timestamp | Literal["epoch", "start", "start_day", "end", "end_day"] ) +ExcelReadEngine: TypeAlias = Literal["xlrd", "openpyxl", "odf", "pyxlsb", "calamine"] +ExcelWriteEngine: TypeAlias = Literal["openpyxl", "odf", "xlsxwriter"] + __all__ = ["npt", "type_t"] diff --git a/pandas-stubs/io/excel/_base.pyi b/pandas-stubs/io/excel/_base.pyi index 8cfade314..a3368d2e3 100644 --- a/pandas-stubs/io/excel/_base.pyi +++ b/pandas-stubs/io/excel/_base.pyi @@ -23,6 +23,8 @@ from pandas._libs.lib import NoDefault from pandas._typing import ( Dtype, DtypeBackend, + ExcelReadEngine, + ExcelWriteEngine, FilePath, IntStrT, ListLikeHashable, @@ -50,7 +52,7 @@ def read_excel( index_col: int | Sequence[int] | None = ..., usecols: str | UsecolsArgType = ..., dtype: str | Dtype | Mapping[str, str | Dtype] | None = ..., - engine: Literal["xlrd", "openpyxl", "odf", "pyxlsb"] | None = ..., + engine: ExcelReadEngine | None = ..., converters: Mapping[int | str, Callable[[object], object]] | None = ..., true_values: Iterable[Hashable] | None = ..., false_values: Iterable[Hashable] | None = ..., @@ -92,7 +94,7 @@ def read_excel( index_col: int | Sequence[int] | None = ..., usecols: str | UsecolsArgType = ..., dtype: str | Dtype | Mapping[str, str | Dtype] | None = ..., - engine: Literal["xlrd", "openpyxl", "odf", "pyxlsb"] | None = ..., + engine: ExcelReadEngine | None = ..., converters: Mapping[int | str, Callable[[object], object]] | None = ..., true_values: Iterable[Hashable] | None = ..., false_values: Iterable[Hashable] | None = ..., @@ -135,7 +137,7 @@ def read_excel( # type: ignore[misc] index_col: int | Sequence[int] | None = ..., usecols: str | UsecolsArgType = ..., dtype: str | Dtype | Mapping[str, str | Dtype] | None = ..., - engine: Literal["xlrd", "openpyxl", "odf", "pyxlsb"] | None = ..., + engine: ExcelReadEngine | None = ..., converters: Mapping[int | str, Callable[[object], object]] | None = ..., true_values: Iterable[Hashable] | None = ..., false_values: Iterable[Hashable] | None = ..., @@ -177,7 +179,7 @@ def read_excel( index_col: int | Sequence[int] | None = ..., usecols: str | UsecolsArgType = ..., dtype: str | Dtype | Mapping[str, str | Dtype] | None = ..., - engine: Literal["xlrd", "openpyxl", "odf", "pyxlsb"] | None = ..., + engine: ExcelReadEngine | None = ..., converters: Mapping[int | str, Callable[[object], object]] | None = ..., true_values: Iterable[Hashable] | None = ..., false_values: Iterable[Hashable] | None = ..., @@ -206,7 +208,7 @@ class ExcelWriter: def __init__( self, path: FilePath | WriteExcelBuffer | ExcelWriter, - engine: Literal["auto", "openpyxl", "odf", "xlsxwriter"] | None = ..., + engine: ExcelWriteEngine | Literal["auto"] | None = ..., date_format: str | None = ..., datetime_format: str | None = ..., mode: Literal["w", "a"] = ..., @@ -217,7 +219,7 @@ class ExcelWriter: @property def supported_extensions(self) -> tuple[str, ...]: ... @property - def engine(self) -> Literal["openpyxl", "odf", "xlsxwriter"]: ... + def engine(self) -> ExcelWriteEngine: ... @property def sheets(self) -> dict[str, Any]: ... @property diff --git a/pyproject.toml b/pyproject.toml index 06368bc77..f21594481 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,6 +64,7 @@ SQLAlchemy = ">=2.0.12" types-python-dateutil = ">=2.8.19" beautifulsoup4 = ">=4.12.2" html5lib = ">=1.1" +python-calamine = "0.2.0" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/tests/test_io.py b/tests/test_io.py index 75d0331be..6bc53c255 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -961,6 +961,16 @@ def test_read_excel() -> None: ), pd.DataFrame, ) + check( + assert_type( + pd.read_excel( + path, + engine="calamine", + ), + pd.DataFrame, + ), + pd.DataFrame, + ) if TYPE_CHECKING_INVALID_USAGE: pd.read_excel(path, names="abcd") # type: ignore[call-overload] # pyright: ignore[reportArgumentType]