diff --git a/stubs/openpyxl/@tests/stubtest_allowlist.txt b/stubs/openpyxl/@tests/stubtest_allowlist.txt index 5bb3c6b68383..8ef8dbb7e49b 100644 --- a/stubs/openpyxl/@tests/stubtest_allowlist.txt +++ b/stubs/openpyxl/@tests/stubtest_allowlist.txt @@ -41,8 +41,9 @@ openpyxl\.descriptors\.(base\.)?MinMax\.allow_none openpyxl\.descriptors\.(base\.)?String\.allow_none openpyxl\.descriptors\.(base\.)?Typed\.allow_none -# "has a default value but stub argument does not" -# Runtime has default arguments that would fail +# Inconsistent methods because +# - using the default value results in an error because of the runtime type-guards +# - or, keyword arguments are explicitly specified openpyxl.cell.text.PhoneticProperties.__init__ openpyxl.cell.text.PhoneticText.__init__ openpyxl.chart.axis._BaseAxis.__init__ @@ -110,6 +111,7 @@ openpyxl.drawing.text.PresetTextShape.__init__ openpyxl.drawing.text.TextField.__init__ openpyxl.drawing.text.TextNormalAutofit.__init__ openpyxl.formatting.rule.DataBar.__init__ +openpyxl.packaging.relationship.get_rel openpyxl.packaging.relationship.Relationship.__init__ openpyxl.packaging.workbook.ChildSheet.__init__ openpyxl.packaging.workbook.PivotCache.__init__ diff --git a/stubs/openpyxl/openpyxl/__init__.pyi b/stubs/openpyxl/openpyxl/__init__.pyi index fa930c9cf276..2b1c6fc90be5 100644 --- a/stubs/openpyxl/openpyxl/__init__.pyi +++ b/stubs/openpyxl/openpyxl/__init__.pyi @@ -1,4 +1,5 @@ -from typing import Protocol +from _typeshed import StrPath, SupportsRead +from typing import IO, Protocol from typing_extensions import Literal, TypeAlias from openpyxl.compat.numbers import NUMPY as NUMPY @@ -21,5 +22,9 @@ open = load_workbook # Utility types reused elsewhere _VisibilityType: TypeAlias = Literal["visible", "hidden", "veryHidden"] # noqa: Y047 +# TODO: Use a proper protocol from ZipFile. See: #10880 +# This alias is to minimize false-positives +_ZipFileFileProtocol: TypeAlias = StrPath | IO[bytes] | SupportsRead[bytes] # noqa: Y047 + class _Decodable(Protocol): # noqa: Y046 def decode(self, __encoding: str) -> str: ... diff --git a/stubs/openpyxl/openpyxl/drawing/image.pyi b/stubs/openpyxl/openpyxl/drawing/image.pyi index b275596fd009..3bd545df3254 100644 --- a/stubs/openpyxl/openpyxl/drawing/image.pyi +++ b/stubs/openpyxl/openpyxl/drawing/image.pyi @@ -1,9 +1,20 @@ -from _typeshed import Incomplete +from _typeshed import SupportsRead +from pathlib import Path +from types import ModuleType +from typing import Any +from typing_extensions import Literal, TypeAlias + +# Is actually PIL.Image.Image +_PILImageImage: TypeAlias = Any +# same as first parameter of PIL.Image.open +_PILImageFilePath: TypeAlias = str | bytes | Path | SupportsRead[bytes] + +PILImage: ModuleType | Literal[False] class Image: anchor: str - ref: Incomplete - format: Incomplete - def __init__(self, img) -> None: ... + ref: _PILImageImage | _PILImageFilePath + format: str + def __init__(self, img: _PILImageImage | _PILImageFilePath) -> None: ... @property def path(self) -> str: ... diff --git a/stubs/openpyxl/openpyxl/packaging/relationship.pyi b/stubs/openpyxl/openpyxl/packaging/relationship.pyi index 09165e7bb179..4c56bfb4d3eb 100644 --- a/stubs/openpyxl/openpyxl/packaging/relationship.pyi +++ b/stubs/openpyxl/openpyxl/packaging/relationship.pyi @@ -1,10 +1,17 @@ from _typeshed import Incomplete, Unused from collections.abc import Generator -from typing import ClassVar, overload +from typing import ClassVar, TypeVar, overload from typing_extensions import Literal +from zipfile import ZipFile from openpyxl.descriptors.base import Alias, String from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.pivot.cache import CacheDefinition +from openpyxl.pivot.record import RecordList +from openpyxl.pivot.table import TableDefinition + +_SerialisableT = TypeVar("_SerialisableT", bound=Serialisable) +_SerialisableRelTypeT = TypeVar("_SerialisableRelTypeT", bound=CacheDefinition | RecordList | TableDefinition) class Relationship(Serialisable): tagname: ClassVar[str] @@ -37,5 +44,18 @@ class RelationshipList(Serialisable): def to_tree(self): ... def get_rels_path(path): ... -def get_dependents(archive, filename): ... -def get_rel(archive, deps, id: Incomplete | None = None, cls: Incomplete | None = None): ... +def get_dependents(archive: ZipFile, filename: str) -> RelationshipList: ... + +# If `id` is None, `cls` needs to have ClassVar `rel_type`. +# The `deps` attribute used at runtime is for internal use immediatly after the return. +# `cls` cannot be None +@overload +def get_rel( + archive: ZipFile, deps: RelationshipList, id: None = None, *, cls: type[_SerialisableRelTypeT] +) -> _SerialisableRelTypeT | None: ... +@overload +def get_rel( + archive: ZipFile, deps: RelationshipList, id: None, cls: type[_SerialisableRelTypeT] +) -> _SerialisableRelTypeT | None: ... +@overload +def get_rel(archive: ZipFile, deps: RelationshipList, id: str, cls: type[_SerialisableT]) -> _SerialisableT: ... diff --git a/stubs/openpyxl/openpyxl/reader/drawings.pyi b/stubs/openpyxl/openpyxl/reader/drawings.pyi index 4334877fc2b0..f2bf8cd0863f 100644 --- a/stubs/openpyxl/openpyxl/reader/drawings.pyi +++ b/stubs/openpyxl/openpyxl/reader/drawings.pyi @@ -1 +1,6 @@ -def find_images(archive, path): ... +from zipfile import ZipFile + +from openpyxl.chart._chart import ChartBase +from openpyxl.drawing.image import Image + +def find_images(archive: ZipFile, path: str) -> tuple[list[ChartBase], list[Image]]: ... diff --git a/stubs/openpyxl/openpyxl/reader/excel.pyi b/stubs/openpyxl/openpyxl/reader/excel.pyi index 7464059aa03a..4d707269683b 100644 --- a/stubs/openpyxl/openpyxl/reader/excel.pyi +++ b/stubs/openpyxl/openpyxl/reader/excel.pyi @@ -1,7 +1,8 @@ -from _typeshed import Incomplete, StrPath, SupportsRead +from _typeshed import Incomplete from typing_extensions import Final, Literal, TypeAlias from zipfile import ZipFile +from openpyxl import _ZipFileFileProtocol from openpyxl.chartsheet.chartsheet import Chartsheet from openpyxl.packaging.manifest import Manifest from openpyxl.packaging.relationship import Relationship @@ -26,7 +27,7 @@ class ExcelReader: def __init__( self, - fn: SupportsRead[bytes] | str, + fn: _ZipFileFileProtocol, read_only: bool = False, keep_vba: bool = False, data_only: bool = False, @@ -44,7 +45,7 @@ class ExcelReader: def read(self) -> None: ... def load_workbook( - filename: SupportsRead[bytes] | StrPath, + filename: _ZipFileFileProtocol, read_only: bool = False, keep_vba: bool = False, data_only: bool = False, diff --git a/stubs/openpyxl/openpyxl/reader/workbook.pyi b/stubs/openpyxl/openpyxl/reader/workbook.pyi index b016ceebe35c..2b6e03d5b9eb 100644 --- a/stubs/openpyxl/openpyxl/reader/workbook.pyi +++ b/stubs/openpyxl/openpyxl/reader/workbook.pyi @@ -1,17 +1,18 @@ from _typeshed import Incomplete from collections.abc import Generator +from zipfile import ZipFile from openpyxl.packaging.relationship import RelationshipList from openpyxl.pivot.cache import CacheDefinition from openpyxl.workbook import Workbook class WorkbookParser: - archive: Incomplete + archive: ZipFile workbook_part_name: Incomplete wb: Workbook keep_links: Incomplete sheets: Incomplete - def __init__(self, archive, workbook_part_name, keep_links: bool = True) -> None: ... + def __init__(self, archive: ZipFile, workbook_part_name, keep_links: bool = True) -> None: ... @property def rels(self) -> RelationshipList: ... caches: Incomplete diff --git a/stubs/openpyxl/openpyxl/styles/stylesheet.pyi b/stubs/openpyxl/openpyxl/styles/stylesheet.pyi index 3d7b94d335aa..61c8c475c56c 100644 --- a/stubs/openpyxl/openpyxl/styles/stylesheet.pyi +++ b/stubs/openpyxl/openpyxl/styles/stylesheet.pyi @@ -1,6 +1,7 @@ from _typeshed import Incomplete, Unused -from typing import ClassVar +from typing import ClassVar, TypeVar from typing_extensions import Literal, Self +from zipfile import ZipFile from openpyxl.descriptors.base import Typed from openpyxl.descriptors.excel import ExtensionList @@ -10,6 +11,9 @@ from openpyxl.styles.colors import ColorList from openpyxl.styles.named_styles import _NamedCellStyleList from openpyxl.styles.numbers import NumberFormatList from openpyxl.styles.table import TableStyleList +from openpyxl.workbook.workbook import Workbook + +_WorkbookT = TypeVar("_WorkbookT", bound=Workbook) class Stylesheet(Serialisable): tagname: ClassVar[str] @@ -50,5 +54,5 @@ class Stylesheet(Serialisable): def custom_formats(self) -> dict[int, str]: ... def to_tree(self, tagname: str | None = None, idx: Incomplete | None = None, namespace: str | None = None): ... -def apply_stylesheet(archive, wb): ... +def apply_stylesheet(archive: ZipFile, wb: _WorkbookT) -> _WorkbookT | None: ... def write_stylesheet(wb): ... diff --git a/stubs/openpyxl/openpyxl/workbook/external_link/external.pyi b/stubs/openpyxl/openpyxl/workbook/external_link/external.pyi index ef1f1d578bd8..ba43b041bdfe 100644 --- a/stubs/openpyxl/openpyxl/workbook/external_link/external.pyi +++ b/stubs/openpyxl/openpyxl/workbook/external_link/external.pyi @@ -1,6 +1,7 @@ from _typeshed import Incomplete, Unused from typing import ClassVar from typing_extensions import Literal, TypeAlias +from zipfile import ZipFile from openpyxl.descriptors.base import Bool, Integer, NoneSet, String, Typed, _ConvertibleToBool, _ConvertibleToInt from openpyxl.descriptors.nested import NestedText @@ -76,4 +77,4 @@ class ExternalLink(Serialisable): @property def path(self) -> str: ... -def read_external_link(archive, book_path): ... +def read_external_link(archive: ZipFile, book_path: str) -> ExternalLink: ... diff --git a/stubs/openpyxl/openpyxl/workbook/workbook.pyi b/stubs/openpyxl/openpyxl/workbook/workbook.pyi index 831e57d106a4..d418f46a5f66 100644 --- a/stubs/openpyxl/openpyxl/workbook/workbook.pyi +++ b/stubs/openpyxl/openpyxl/workbook/workbook.pyi @@ -1,10 +1,10 @@ -from _typeshed import Incomplete, StrPath +from _typeshed import Incomplete from collections.abc import Iterator from datetime import datetime -from typing import IO from typing_extensions import Final +from zipfile import ZipFile -from openpyxl import _Decodable +from openpyxl import _Decodable, _ZipFileFileProtocol from openpyxl.chartsheet.chartsheet import Chartsheet from openpyxl.styles.named_styles import NamedStyle from openpyxl.workbook.child import _WorkbookChild @@ -21,7 +21,7 @@ class Workbook: security: Incomplete shared_strings: Incomplete loaded_theme: Incomplete - vba_archive: Incomplete + vba_archive: ZipFile | None is_template: bool code_name: Incomplete encoding: str @@ -77,7 +77,7 @@ class Workbook: def named_styles(self) -> list[str]: ... @property def mime_type(self) -> str: ... - def save(self, filename: StrPath | IO[bytes]) -> None: ... + def save(self, filename: _ZipFileFileProtocol) -> None: ... @property def style_names(self) -> list[str]: ... def copy_worksheet(self, from_worksheet: Worksheet) -> Worksheet | WriteOnlyWorksheet: ... diff --git a/stubs/openpyxl/openpyxl/writer/excel.pyi b/stubs/openpyxl/openpyxl/writer/excel.pyi index 624a3c4c05bd..059baeb022e7 100644 --- a/stubs/openpyxl/openpyxl/writer/excel.pyi +++ b/stubs/openpyxl/openpyxl/writer/excel.pyi @@ -1,12 +1,17 @@ -from _typeshed import Incomplete +from typing_extensions import Literal +from zipfile import ZipFile + +from openpyxl import _ZipFileFileProtocol +from openpyxl.packaging.manifest import Manifest +from openpyxl.workbook.workbook import Workbook class ExcelWriter: - workbook: Incomplete - manifest: Incomplete - vba_modified: Incomplete - def __init__(self, workbook, archive) -> None: ... + workbook: Workbook + manifest: Manifest + vba_modified: set[str | None] + def __init__(self, workbook: Workbook, archive: ZipFile) -> None: ... def write_data(self) -> None: ... def write_worksheet(self, ws) -> None: ... def save(self) -> None: ... -def save_workbook(workbook, filename): ... +def save_workbook(workbook: Workbook, filename: _ZipFileFileProtocol) -> Literal[True]: ...