diff --git a/stubs/openpyxl/@tests/test_cases/check_read_only.py b/stubs/openpyxl/@tests/test_cases/check_read_only.py new file mode 100644 index 000000000000..41f828451585 --- /dev/null +++ b/stubs/openpyxl/@tests/test_cases/check_read_only.py @@ -0,0 +1,20 @@ +from collections.abc import Generator +from typing_extensions import assert_type + +from openpyxl.cell import _CellGetValue +from openpyxl.cell.read_only import EmptyCell, ReadOnlyCell +from openpyxl.worksheet._read_only import ReadOnlyWorksheet + + +def check_read_only_methods(sheet: ReadOnlyWorksheet, values_only: bool) -> None: + # These methods alias Worksheet methods at runtime; their self and cell types differ. + assert_type(sheet.cell(1, 1), ReadOnlyCell | EmptyCell) + assert_type(next(sheet.iter_rows()), tuple[ReadOnlyCell | EmptyCell, ...]) + assert_type(next(sheet.iter_rows(values_only=True)), tuple[_CellGetValue, ...]) + assert_type(next(sheet.iter_rows(1, 2, 1, 3, True)), tuple[_CellGetValue, ...]) + assert_type( + sheet.iter_rows(values_only=values_only), + Generator[tuple[ReadOnlyCell | EmptyCell, ...]] | Generator[tuple[_CellGetValue, ...]], + ) + assert_type(next(iter(sheet)), tuple[ReadOnlyCell | EmptyCell, ...]) + assert_type(sheet[1], tuple[ReadOnlyCell | EmptyCell, ...]) diff --git a/stubs/openpyxl/openpyxl/worksheet/_read_only.pyi b/stubs/openpyxl/openpyxl/worksheet/_read_only.pyi index a1f95ee4b577..974a087a3adb 100644 --- a/stubs/openpyxl/openpyxl/worksheet/_read_only.pyi +++ b/stubs/openpyxl/openpyxl/worksheet/_read_only.pyi @@ -1,27 +1,77 @@ from _typeshed import SupportsGetItem -from collections.abc import Generator +from collections.abc import Generator, Iterator +from typing import Any, Literal, TypeAlias, overload from openpyxl import _VisibilityType -from openpyxl.cell import _CellGetValue, _CellOrMergedCell +from openpyxl.cell import _CellGetValue +from openpyxl.cell.read_only import EmptyCell, ReadOnlyCell from openpyxl.utils.cell import _RangeBoundariesTuple from openpyxl.workbook.workbook import Workbook -from openpyxl.worksheet.worksheet import Worksheet + +_ReadOnlyCell: TypeAlias = ReadOnlyCell | EmptyCell def read_dimension(source) -> _RangeBoundariesTuple | None: ... class ReadOnlyWorksheet: - cell = Worksheet.cell - iter_rows = Worksheet.iter_rows - # Same as Worksheet.values - # https://github.com/python/mypy/issues/6700 + # Worksheet methods are reused at runtime, but create read-only or empty cells here. + def cell(self, row: int, column: int, value: None = None) -> _ReadOnlyCell: ... + + @overload + def iter_rows( + self, min_row: int | None, max_row: int | None, min_col: int | None, max_col: int | None, values_only: Literal[True] + ) -> Generator[tuple[_CellGetValue, ...]]: ... + @overload + def iter_rows( + self, + min_row: int | None = None, + max_row: int | None = None, + min_col: int | None = None, + max_col: int | None = None, + *, + values_only: Literal[True], + ) -> Generator[tuple[_CellGetValue, ...]]: ... + @overload + def iter_rows( + self, + min_row: int | None = None, + max_row: int | None = None, + min_col: int | None = None, + max_col: int | None = None, + values_only: Literal[False] = False, + ) -> Generator[tuple[_ReadOnlyCell, ...]]: ... + @overload + def iter_rows( + self, min_row: int | None, max_row: int | None, min_col: int | None, max_col: int | None, values_only: bool + ) -> Generator[tuple[_ReadOnlyCell, ...]] | Generator[tuple[_CellGetValue, ...]]: ... + @overload + def iter_rows( + self, + min_row: int | None = None, + max_row: int | None = None, + min_col: int | None = None, + max_col: int | None = None, + *, + values_only: bool, + ) -> Generator[tuple[_ReadOnlyCell, ...]] | Generator[tuple[_CellGetValue, ...]]: ... + @property def values(self) -> Generator[tuple[_CellGetValue, ...]]: ... - # Same as Worksheet.rows - # https://github.com/python/mypy/issues/6700 @property - def rows(self) -> Generator[tuple[_CellOrMergedCell, ...]]: ... - __getitem__ = Worksheet.__getitem__ - __iter__ = Worksheet.__iter__ + def rows(self) -> Generator[tuple[_ReadOnlyCell, ...]]: ... + + # An int is necessarily a row selection + @overload + def __getitem__(self, key: int) -> tuple[_ReadOnlyCell, ...]: ... + # A slice is necessarily a row or rows, even if targetting a single cell + @overload + def __getitem__(self, key: slice) -> tuple[Any, ...]: ... # tuple[AnyOf[_ReadOnlyCell, tuple[_ReadOnlyCell, ...]]] + # A str could be an individual cell, row or full range + @overload + def __getitem__( + self, key: str + ) -> Any: ... # AnyOf[_ReadOnlyCell, tuple[_ReadOnlyCell, ...], tuple[tuple[_ReadOnlyCell, ...], ...]] + + def __iter__(self) -> Iterator[tuple[_ReadOnlyCell, ...]]: ... parent: Workbook title: str sheet_state: _VisibilityType