Skip to content

Commit 105bb0a

Browse files
authored
openpyxl: Type usages of PIL and zipfile (#10706)
1 parent 18cd196 commit 105bb0a

File tree

11 files changed

+85
-30
lines changed

11 files changed

+85
-30
lines changed

stubs/openpyxl/@tests/stubtest_allowlist.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ openpyxl\.descriptors\.(base\.)?MinMax\.allow_none
4141
openpyxl\.descriptors\.(base\.)?String\.allow_none
4242
openpyxl\.descriptors\.(base\.)?Typed\.allow_none
4343

44-
# "has a default value but stub argument does not"
45-
# Runtime has default arguments that would fail
44+
# Inconsistent methods because
45+
# - using the default value results in an error because of the runtime type-guards
46+
# - or, keyword arguments are explicitly specified
4647
openpyxl.cell.text.PhoneticProperties.__init__
4748
openpyxl.cell.text.PhoneticText.__init__
4849
openpyxl.chart.axis._BaseAxis.__init__
@@ -110,6 +111,7 @@ openpyxl.drawing.text.PresetTextShape.__init__
110111
openpyxl.drawing.text.TextField.__init__
111112
openpyxl.drawing.text.TextNormalAutofit.__init__
112113
openpyxl.formatting.rule.DataBar.__init__
114+
openpyxl.packaging.relationship.get_rel
113115
openpyxl.packaging.relationship.Relationship.__init__
114116
openpyxl.packaging.workbook.ChildSheet.__init__
115117
openpyxl.packaging.workbook.PivotCache.__init__

stubs/openpyxl/openpyxl/__init__.pyi

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from typing import Protocol
1+
from _typeshed import StrPath, SupportsRead
2+
from typing import IO, Protocol
23
from typing_extensions import Literal, TypeAlias
34

45
from openpyxl.compat.numbers import NUMPY as NUMPY
@@ -21,5 +22,9 @@ open = load_workbook
2122
# Utility types reused elsewhere
2223
_VisibilityType: TypeAlias = Literal["visible", "hidden", "veryHidden"] # noqa: Y047
2324

25+
# TODO: Use a proper protocol from ZipFile. See: #10880
26+
# This alias is to minimize false-positives
27+
_ZipFileFileProtocol: TypeAlias = StrPath | IO[bytes] | SupportsRead[bytes] # noqa: Y047
28+
2429
class _Decodable(Protocol): # noqa: Y046
2530
def decode(self, __encoding: str) -> str: ...
Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
1-
from _typeshed import Incomplete
1+
from _typeshed import SupportsRead
2+
from pathlib import Path
3+
from types import ModuleType
4+
from typing import Any
5+
from typing_extensions import Literal, TypeAlias
6+
7+
# Is actually PIL.Image.Image
8+
_PILImageImage: TypeAlias = Any
9+
# same as first parameter of PIL.Image.open
10+
_PILImageFilePath: TypeAlias = str | bytes | Path | SupportsRead[bytes]
11+
12+
PILImage: ModuleType | Literal[False]
213

314
class Image:
415
anchor: str
5-
ref: Incomplete
6-
format: Incomplete
7-
def __init__(self, img) -> None: ...
16+
ref: _PILImageImage | _PILImageFilePath
17+
format: str
18+
def __init__(self, img: _PILImageImage | _PILImageFilePath) -> None: ...
819
@property
920
def path(self) -> str: ...

stubs/openpyxl/openpyxl/packaging/relationship.pyi

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
from _typeshed import Incomplete, Unused
22
from collections.abc import Generator
3-
from typing import ClassVar, overload
3+
from typing import ClassVar, TypeVar, overload
44
from typing_extensions import Literal
5+
from zipfile import ZipFile
56

67
from openpyxl.descriptors.base import Alias, String
78
from openpyxl.descriptors.serialisable import Serialisable
9+
from openpyxl.pivot.cache import CacheDefinition
10+
from openpyxl.pivot.record import RecordList
11+
from openpyxl.pivot.table import TableDefinition
12+
13+
_SerialisableT = TypeVar("_SerialisableT", bound=Serialisable)
14+
_SerialisableRelTypeT = TypeVar("_SerialisableRelTypeT", bound=CacheDefinition | RecordList | TableDefinition)
815

916
class Relationship(Serialisable):
1017
tagname: ClassVar[str]
@@ -37,5 +44,18 @@ class RelationshipList(Serialisable):
3744
def to_tree(self): ...
3845

3946
def get_rels_path(path): ...
40-
def get_dependents(archive, filename): ...
41-
def get_rel(archive, deps, id: Incomplete | None = None, cls: Incomplete | None = None): ...
47+
def get_dependents(archive: ZipFile, filename: str) -> RelationshipList: ...
48+
49+
# If `id` is None, `cls` needs to have ClassVar `rel_type`.
50+
# The `deps` attribute used at runtime is for internal use immediatly after the return.
51+
# `cls` cannot be None
52+
@overload
53+
def get_rel(
54+
archive: ZipFile, deps: RelationshipList, id: None = None, *, cls: type[_SerialisableRelTypeT]
55+
) -> _SerialisableRelTypeT | None: ...
56+
@overload
57+
def get_rel(
58+
archive: ZipFile, deps: RelationshipList, id: None, cls: type[_SerialisableRelTypeT]
59+
) -> _SerialisableRelTypeT | None: ...
60+
@overload
61+
def get_rel(archive: ZipFile, deps: RelationshipList, id: str, cls: type[_SerialisableT]) -> _SerialisableT: ...
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1-
def find_images(archive, path): ...
1+
from zipfile import ZipFile
2+
3+
from openpyxl.chart._chart import ChartBase
4+
from openpyxl.drawing.image import Image
5+
6+
def find_images(archive: ZipFile, path: str) -> tuple[list[ChartBase], list[Image]]: ...

stubs/openpyxl/openpyxl/reader/excel.pyi

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
from _typeshed import Incomplete, StrPath, SupportsRead
1+
from _typeshed import Incomplete
22
from typing_extensions import Final, Literal, TypeAlias
33
from zipfile import ZipFile
44

5+
from openpyxl import _ZipFileFileProtocol
56
from openpyxl.chartsheet.chartsheet import Chartsheet
67
from openpyxl.packaging.manifest import Manifest
78
from openpyxl.packaging.relationship import Relationship
@@ -26,7 +27,7 @@ class ExcelReader:
2627

2728
def __init__(
2829
self,
29-
fn: SupportsRead[bytes] | str,
30+
fn: _ZipFileFileProtocol,
3031
read_only: bool = False,
3132
keep_vba: bool = False,
3233
data_only: bool = False,
@@ -44,7 +45,7 @@ class ExcelReader:
4445
def read(self) -> None: ...
4546

4647
def load_workbook(
47-
filename: SupportsRead[bytes] | StrPath,
48+
filename: _ZipFileFileProtocol,
4849
read_only: bool = False,
4950
keep_vba: bool = False,
5051
data_only: bool = False,

stubs/openpyxl/openpyxl/reader/workbook.pyi

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
from _typeshed import Incomplete
22
from collections.abc import Generator
3+
from zipfile import ZipFile
34

45
from openpyxl.packaging.relationship import RelationshipList
56
from openpyxl.pivot.cache import CacheDefinition
67
from openpyxl.workbook import Workbook
78

89
class WorkbookParser:
9-
archive: Incomplete
10+
archive: ZipFile
1011
workbook_part_name: Incomplete
1112
wb: Workbook
1213
keep_links: Incomplete
1314
sheets: Incomplete
14-
def __init__(self, archive, workbook_part_name, keep_links: bool = True) -> None: ...
15+
def __init__(self, archive: ZipFile, workbook_part_name, keep_links: bool = True) -> None: ...
1516
@property
1617
def rels(self) -> RelationshipList: ...
1718
caches: Incomplete

stubs/openpyxl/openpyxl/styles/stylesheet.pyi

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from _typeshed import Incomplete, Unused
2-
from typing import ClassVar
2+
from typing import ClassVar, TypeVar
33
from typing_extensions import Literal, Self
4+
from zipfile import ZipFile
45

56
from openpyxl.descriptors.base import Typed
67
from openpyxl.descriptors.excel import ExtensionList
@@ -10,6 +11,9 @@ from openpyxl.styles.colors import ColorList
1011
from openpyxl.styles.named_styles import _NamedCellStyleList
1112
from openpyxl.styles.numbers import NumberFormatList
1213
from openpyxl.styles.table import TableStyleList
14+
from openpyxl.workbook.workbook import Workbook
15+
16+
_WorkbookT = TypeVar("_WorkbookT", bound=Workbook)
1317

1418
class Stylesheet(Serialisable):
1519
tagname: ClassVar[str]
@@ -50,5 +54,5 @@ class Stylesheet(Serialisable):
5054
def custom_formats(self) -> dict[int, str]: ...
5155
def to_tree(self, tagname: str | None = None, idx: Incomplete | None = None, namespace: str | None = None): ...
5256

53-
def apply_stylesheet(archive, wb): ...
57+
def apply_stylesheet(archive: ZipFile, wb: _WorkbookT) -> _WorkbookT | None: ...
5458
def write_stylesheet(wb): ...

stubs/openpyxl/openpyxl/workbook/external_link/external.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from _typeshed import Incomplete, Unused
22
from typing import ClassVar
33
from typing_extensions import Literal, TypeAlias
4+
from zipfile import ZipFile
45

56
from openpyxl.descriptors.base import Bool, Integer, NoneSet, String, Typed, _ConvertibleToBool, _ConvertibleToInt
67
from openpyxl.descriptors.nested import NestedText
@@ -76,4 +77,4 @@ class ExternalLink(Serialisable):
7677
@property
7778
def path(self) -> str: ...
7879

79-
def read_external_link(archive, book_path): ...
80+
def read_external_link(archive: ZipFile, book_path: str) -> ExternalLink: ...

stubs/openpyxl/openpyxl/workbook/workbook.pyi

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
from _typeshed import Incomplete, StrPath
1+
from _typeshed import Incomplete
22
from collections.abc import Iterator
33
from datetime import datetime
4-
from typing import IO
54
from typing_extensions import Final
5+
from zipfile import ZipFile
66

7-
from openpyxl import _Decodable
7+
from openpyxl import _Decodable, _ZipFileFileProtocol
88
from openpyxl.chartsheet.chartsheet import Chartsheet
99
from openpyxl.styles.named_styles import NamedStyle
1010
from openpyxl.workbook.child import _WorkbookChild
@@ -21,7 +21,7 @@ class Workbook:
2121
security: Incomplete
2222
shared_strings: Incomplete
2323
loaded_theme: Incomplete
24-
vba_archive: Incomplete
24+
vba_archive: ZipFile | None
2525
is_template: bool
2626
code_name: Incomplete
2727
encoding: str
@@ -77,7 +77,7 @@ class Workbook:
7777
def named_styles(self) -> list[str]: ...
7878
@property
7979
def mime_type(self) -> str: ...
80-
def save(self, filename: StrPath | IO[bytes]) -> None: ...
80+
def save(self, filename: _ZipFileFileProtocol) -> None: ...
8181
@property
8282
def style_names(self) -> list[str]: ...
8383
def copy_worksheet(self, from_worksheet: Worksheet) -> Worksheet | WriteOnlyWorksheet: ...
Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
from _typeshed import Incomplete
1+
from typing_extensions import Literal
2+
from zipfile import ZipFile
3+
4+
from openpyxl import _ZipFileFileProtocol
5+
from openpyxl.packaging.manifest import Manifest
6+
from openpyxl.workbook.workbook import Workbook
27

38
class ExcelWriter:
4-
workbook: Incomplete
5-
manifest: Incomplete
6-
vba_modified: Incomplete
7-
def __init__(self, workbook, archive) -> None: ...
9+
workbook: Workbook
10+
manifest: Manifest
11+
vba_modified: set[str | None]
12+
def __init__(self, workbook: Workbook, archive: ZipFile) -> None: ...
813
def write_data(self) -> None: ...
914
def write_worksheet(self, ws) -> None: ...
1015
def save(self) -> None: ...
1116

12-
def save_workbook(workbook, filename): ...
17+
def save_workbook(workbook: Workbook, filename: _ZipFileFileProtocol) -> Literal[True]: ...

0 commit comments

Comments
 (0)