Skip to content

Commit 320a9c3

Browse files
authored
Make openpyxl stubtest-complete (#10570)
1 parent d82a832 commit 320a9c3

File tree

16 files changed

+76
-19
lines changed

16 files changed

+76
-19
lines changed

stubs/openpyxl/@tests/stubtest_allowlist.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
# Loop variables leaked in global scope
2+
openpyxl.utils.cell.col
3+
openpyxl.utils.cell.i
4+
5+
# Unintended re-export from star-import
6+
openpyxl.chart.marker.DRAWING_NS
7+
openpyxl.chart.marker.PRESET_COLORS
8+
openpyxl.chart.shapes.DRAWING_NS
9+
openpyxl.chart.shapes.PRESET_COLORS
10+
openpyxl.descriptors.DEBUG
11+
112
# The actual runtime definition depends on what else is installed
213
# (lxml, defusedxml, et_xmlfile)
314
openpyxl.xml._functions_overloads

stubs/openpyxl/METADATA.toml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,2 @@
11
version = "3.1.*"
22
upstream_repository = "https://foss.heptapod.net/openpyxl/openpyxl"
3-
partial_stub = true
4-
5-
[tool.stubtest]
6-
ignore_missing_stub = true

stubs/openpyxl/openpyxl/__init__.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ from ._constants import (
1212
__version__ as __version__,
1313
)
1414

15+
DEBUG: bool
1516
open = load_workbook

stubs/openpyxl/openpyxl/cell/rich_text.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ from typing_extensions import Literal, Self
44

55
from openpyxl.cell.text import InlineFont
66
from openpyxl.descriptors import Strict, String, Typed
7+
from openpyxl.descriptors.serialisable import _ChildSerialisableTreeElement
78

89
class TextBlock(Strict):
910
font: Typed[InlineFont, Literal[False]]
@@ -18,7 +19,7 @@ class CellRichText(list[str | TextBlock]):
1819
@overload
1920
def __init__(self, *args: str | TextBlock) -> None: ...
2021
@classmethod
21-
def from_tree(cls, node) -> Self: ...
22+
def from_tree(cls, node: _ChildSerialisableTreeElement) -> Self: ...
2223
def __add__(self, arg: Iterable[str | TextBlock]) -> CellRichText: ... # type: ignore[override]
2324
def append(self, arg: str | TextBlock) -> None: ...
2425
def extend(self, arg: Iterable[str | TextBlock]) -> None: ...

stubs/openpyxl/openpyxl/chart/chartspace.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ class ChartSpace(Serialisable):
9191
protection: Typed[Protection, Literal[True]]
9292
chart: Typed[ChartContainer, Literal[False]]
9393
spPr: Typed[GraphicalProperties, Literal[True]]
94-
graphicalProperties: Alias
94+
graphical_properties: Alias
9595
txPr: Typed[RichText, Literal[True]]
9696
textProperties: Alias
9797
externalData: Typed[ExternalData, Literal[True]]

stubs/openpyxl/openpyxl/descriptors/excel.pyi

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
from _typeshed import Incomplete
2-
from typing import ClassVar, TypeVar
2+
from typing import ClassVar
33
from typing_extensions import Literal
44

55
from . import Integer, MatchPattern, MinMax, Strict, String
6+
from .base import _M, _N
67
from .serialisable import Serialisable
78

8-
_N = TypeVar("_N", bound=bool)
9-
_M = TypeVar("_M", int, float)
10-
119
class HexBinary(MatchPattern[str, Incomplete]):
1210
pattern: str
1311

stubs/openpyxl/openpyxl/descriptors/sequence.pyi

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,17 @@ from .base import Alias, Descriptor
99

1010
class Sequence(Descriptor[Incomplete]):
1111
expected_type: type[Incomplete]
12-
seq_types: Incomplete
12+
seq_types: tuple[type, ...]
1313
idx_base: int
1414
unique: bool
15+
container: type
1516
def __set__(self, instance: Serialisable | Strict, seq) -> None: ...
1617
def to_tree(self, tagname, obj, namespace: str | None = None) -> Generator[Incomplete, None, None]: ...
1718

19+
class UniqueSequence(Sequence):
20+
seq_types: tuple[type, ...]
21+
container: type
22+
1823
class ValueSequence(Sequence):
1924
attribute: str
2025
def to_tree(self, tagname, obj, namespace: str | None = None) -> Generator[Incomplete, None, None]: ...

stubs/openpyxl/openpyxl/descriptors/serialisable.pyi

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
from _typeshed import Incomplete
2-
from typing import Any, ClassVar, NoReturn
1+
from _typeshed import Incomplete, SupportsIter
2+
from typing import Any, ClassVar, NoReturn, Protocol
33
from typing_extensions import Final
44

55
from openpyxl.descriptors import MetaSerialisable
66

7-
from ..xml._functions_overloads import _HasTagAndTextAndAttrib
7+
from ..xml._functions_overloads import _HasAttrib, _HasTagAndGet, _HasText
8+
9+
# For any override directly re-using Serialisable.from_tree
10+
class _ChildSerialisableTreeElement(_HasAttrib, _HasText, SupportsIter[Incomplete], Protocol): ...
11+
12+
class _SerialisableTreeElement(_HasTagAndGet[Incomplete], _ChildSerialisableTreeElement, Protocol):
13+
def find(self, __path: str) -> Incomplete | None: ...
814

915
KEYWORDS: Final[frozenset[str]]
1016
seq_types: Final[tuple[type[list[Any]], type[tuple[Any, ...]]]]
@@ -20,8 +26,12 @@ class Serialisable(metaclass=MetaSerialisable):
2026
@property
2127
def tagname(self) -> str | NoReturn: ...
2228
namespace: ClassVar[str | None]
29+
# Note: To respect the Liskov substitution principle, the protocol for node includes all child class requirements
30+
# See comment in xml/functions.pyi as to why use a protocol instead of Element
31+
# Child classes should be more precise than _SerialisableTreeElement !
32+
# Use _ChildSerialisableTreeElement instead for child classes that reuse Serialisable.from_tree directly.
2333
@classmethod
24-
def from_tree(cls, node: _HasTagAndTextAndAttrib): ...
34+
def from_tree(cls, node: _SerialisableTreeElement): ...
2535
def to_tree(self, tagname: str | None = None, idx: Incomplete | None = None, namespace: str | None = None): ...
2636
def __iter__(self): ...
2737
def __eq__(self, other): ...

stubs/openpyxl/openpyxl/pivot/table.pyi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -953,3 +953,6 @@ class TableDefinition(Serialisable):
953953
def to_tree(self): ...
954954
@property
955955
def path(self): ...
956+
def formatted_fields(self) -> dict[Incomplete, list[Incomplete]]: ...
957+
@property
958+
def summary(self) -> str: ...

stubs/openpyxl/openpyxl/reader/excel.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class ExcelReader:
3737
def read_strings(self) -> None: ...
3838
def read_workbook(self) -> None: ...
3939
def read_properties(self) -> None: ...
40+
def read_custom(self) -> None: ...
4041
def read_theme(self) -> None: ...
4142
def read_chartsheet(self, sheet: Chartsheet, rel: Relationship) -> None: ...
4243
def read_worksheets(self) -> None: ...
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1-
def read_string_table(xml_source): ...
1+
from xml.etree.ElementTree import _FileRead
2+
3+
from openpyxl.cell.rich_text import CellRichText
4+
5+
def read_string_table(xml_source: _FileRead) -> list[str]: ...
6+
def read_rich_text(xml_source: _FileRead) -> list[CellRichText | str]: ...
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
from typing_extensions import Final
22

33
FORMULAE: Final[frozenset[str]]
4+
5+
def validate(formula: str) -> None: ...

stubs/openpyxl/openpyxl/worksheet/_reader.pyi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ from _typeshed import Incomplete
33
from collections.abc import Container, Generator
44
from typing_extensions import Final
55

6+
from openpyxl.cell.rich_text import CellRichText
7+
from openpyxl.descriptors.serialisable import _ChildSerialisableTreeElement
8+
69
from .hyperlink import HyperlinkList
710
from .pagebreak import ColBreak, RowBreak
811
from .protection import SheetProtection
@@ -37,6 +40,8 @@ DATA_TAG: Final[str]
3740
DIMENSION_TAG: Final[str]
3841
CUSTOM_VIEWS_TAG: Final[str]
3942

43+
def parse_richtext_string(element: _ChildSerialisableTreeElement) -> CellRichText | str: ...
44+
4045
class WorkSheetParser:
4146
min_row: Incomplete | None
4247
min_col: Incomplete | None

stubs/openpyxl/openpyxl/worksheet/cell_range.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class MultiCellRange(Strict):
7979
ranges: Incomplete
8080
def __init__(self, ranges=...) -> None: ...
8181
def __contains__(self, coord): ...
82+
def sorted(self) -> list[CellRange]: ...
8283
def add(self, coord) -> None: ...
8384
def __iadd__(self, coord): ...
8485
def __eq__(self, other): ...

stubs/openpyxl/openpyxl/worksheet/filters.pyi

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
from _typeshed import Incomplete, Unused
22
from datetime import datetime
3+
from re import Pattern
34
from typing import ClassVar, overload
45
from typing_extensions import Literal, TypeAlias
56

7+
from openpyxl.descriptors import Strict
68
from openpyxl.descriptors.base import (
79
Alias,
810
Bool,
11+
Convertible,
912
DateTime,
1013
Float,
1114
Integer,
@@ -21,6 +24,8 @@ from openpyxl.descriptors.base import (
2124
from openpyxl.descriptors.excel import ExtensionList
2225
from openpyxl.descriptors.serialisable import Serialisable
2326

27+
from ..descriptors.base import _N
28+
2429
_SortConditionSortBy: TypeAlias = Literal["value", "cellColor", "fontColor", "icon"]
2530
_IconSet: TypeAlias = Literal[
2631
"3Arrows",
@@ -167,6 +172,18 @@ class DynamicFilter(Serialisable):
167172
maxValIso: datetime | str | None = None,
168173
) -> None: ...
169174

175+
class CustomFilterValueDescriptor(Convertible[float | str, _N]):
176+
pattern: Pattern[str]
177+
expected_type: type[float | str]
178+
@overload # type:ignore[override] # Different restrictions
179+
def __set__(
180+
self: CustomFilterValueDescriptor[Literal[True]], instance: Serialisable | Strict, value: str | _ConvertibleToFloat | None
181+
) -> None: ...
182+
@overload
183+
def __set__(
184+
self: CustomFilterValueDescriptor[Literal[False]], instance: Serialisable | Strict, value: str | _ConvertibleToFloat
185+
) -> None: ...
186+
170187
class CustomFilter(Serialisable):
171188
tagname: ClassVar[str]
172189
operator: NoneSet[_CustomFilterOperator]

stubs/openpyxl/openpyxl/xml/_functions_overloads.pyi

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@ class _HasTag(Protocol):
2020
class _HasText(Protocol):
2121
text: str
2222

23+
class _HasAttrib(Protocol):
24+
attrib: Iterable[Any] # AnyOf[dict[str, str], Iterable[tuple[str, str]]]
25+
2326
class _HasTagAndGet(_HasTag, Protocol[_T_co]):
2427
def get(self, __value: str) -> _T_co | None: ...
2528

2629
class _HasTagAndText(_HasTag, _HasText, Protocol): ... # noqa: Y046
27-
28-
class _HasTagAndTextAndAttrib(_HasTag, _HasText, Protocol): # noqa: Y046
29-
attrib: Iterable[Any] # AnyOf[dict[str, str], Iterable[tuple[str, str]]]
30+
class _HasTagAndTextAndAttrib(_HasTag, _HasText, _HasAttrib, Protocol): ... # noqa: Y046
3031

3132
class _ParentElement(Protocol[_T]):
3233
def makeelement(self, __tag: str, __attrib: dict[str, str]) -> _T: ...

0 commit comments

Comments
 (0)