From af3511f0c32e7830a0f51d7d6f744fd87e96d4af Mon Sep 17 00:00:00 2001 From: Avasam Date: Mon, 21 Aug 2023 19:52:19 -0400 Subject: [PATCH] Type openpyxl `from_tree` functions --- stubs/openpyxl/openpyxl/chart/axis.pyi | 6 ++--- stubs/openpyxl/openpyxl/chart/plotarea.pyi | 6 ++--- .../openpyxl/openpyxl/descriptors/nested.pyi | 8 +++---- .../openpyxl/descriptors/sequence.pyi | 21 +++++++++++++----- .../openpyxl/descriptors/serialisable.pyi | 13 +++++------ stubs/openpyxl/openpyxl/packaging/custom.pyi | 3 ++- stubs/openpyxl/openpyxl/styles/fills.pyi | 5 ++++- stubs/openpyxl/openpyxl/styles/fonts.pyi | 6 ++--- stubs/openpyxl/openpyxl/styles/stylesheet.pyi | 6 ++--- .../openpyxl/openpyxl/workbook/protection.pyi | 6 +++-- .../openpyxl/worksheet/header_footer.pyi | 6 +++-- stubs/openpyxl/openpyxl/worksheet/page.pyi | 6 ++--- stubs/openpyxl/openpyxl/worksheet/table.pyi | 6 ++--- .../openpyxl/xml/_functions_overloads.pyi | 22 +++++++++++++++---- 14 files changed, 76 insertions(+), 44 deletions(-) diff --git a/stubs/openpyxl/openpyxl/chart/axis.pyi b/stubs/openpyxl/openpyxl/chart/axis.pyi index 36a5e5d536de..229080a5eccc 100644 --- a/stubs/openpyxl/openpyxl/chart/axis.pyi +++ b/stubs/openpyxl/openpyxl/chart/axis.pyi @@ -1,6 +1,6 @@ from _typeshed import Incomplete, Unused from typing import ClassVar, overload -from typing_extensions import Literal, TypeAlias +from typing_extensions import Literal, Self, TypeAlias from openpyxl.chart.layout import Layout from openpyxl.chart.shapes import GraphicalProperties @@ -19,7 +19,7 @@ from openpyxl.descriptors.nested import ( ) from openpyxl.descriptors.serialisable import Serialisable -from ..xml._functions_overloads import _HasTagAndGet +from ..xml._functions_overloads import _HasTagAndGet, _SupportsFindAndIterAndAttribAndText _ScalingOrientation: TypeAlias = Literal["maxMin", "minMax"] _BaseAxisAxPos: TypeAlias = Literal["b", "l", "r", "t"] @@ -198,7 +198,7 @@ class NumericAxis(_BaseAxis): **kw, ) -> None: ... @classmethod - def from_tree(cls, node): ... + def from_tree(cls, node: _SupportsFindAndIterAndAttribAndText) -> Self: ... class TextAxis(_BaseAxis): tagname: ClassVar[str] diff --git a/stubs/openpyxl/openpyxl/chart/plotarea.pyi b/stubs/openpyxl/openpyxl/chart/plotarea.pyi index caaa62a0d61b..3e80697ac28d 100644 --- a/stubs/openpyxl/openpyxl/chart/plotarea.pyi +++ b/stubs/openpyxl/openpyxl/chart/plotarea.pyi @@ -1,6 +1,6 @@ from _typeshed import Incomplete, Unused from typing import ClassVar -from typing_extensions import Literal +from typing_extensions import Literal, Self from openpyxl.chart.layout import Layout from openpyxl.chart.shapes import GraphicalProperties @@ -8,7 +8,7 @@ from openpyxl.chart.text import RichText from openpyxl.descriptors.base import Alias, Typed, _ConvertibleToBool from openpyxl.descriptors.excel import ExtensionList from openpyxl.descriptors.nested import NestedBool -from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors.serialisable import Serialisable, _ChildSerialisableTreeElement from ..xml._functions_overloads import _HasTagAndGet @@ -73,4 +73,4 @@ class PlotArea(Serialisable): ) -> None: ... def to_tree(self, tagname: str | None = None, idx: Incomplete | None = None, namespace: str | None = None): ... @classmethod - def from_tree(cls, node): ... + def from_tree(cls, node: _ChildSerialisableTreeElement) -> Self: ... diff --git a/stubs/openpyxl/openpyxl/descriptors/nested.pyi b/stubs/openpyxl/openpyxl/descriptors/nested.pyi index caabf89d758a..8a7af3be8d5f 100644 --- a/stubs/openpyxl/openpyxl/descriptors/nested.pyi +++ b/stubs/openpyxl/openpyxl/descriptors/nested.pyi @@ -9,7 +9,7 @@ from openpyxl.descriptors.serialisable import Serialisable from openpyxl.drawing.fill import Blip from openpyxl.xml.functions import Element -from ..xml._functions_overloads import _HasTagAndGet, _HasTagAndText +from ..xml._functions_overloads import _HasGet, _HasTagAndGet, _HasText from .base import _M, _N, _T, _ConvertibleToBool, _ConvertibleToFloat, _ConvertibleToInt, _ExpectedTypeParam _NestedNoneSetParam: TypeAlias = _HasTagAndGet[_T | Literal["none"] | None] | _T | Literal["none"] | None @@ -35,7 +35,7 @@ class Nested(Descriptor[_T]): ) -> None: ... def __get__(self, instance: Serialisable | Strict, cls: type | None) -> _T: ... def __set__(self, instance: Serialisable | Strict, value: _HasTagAndGet[_T] | _T) -> None: ... - def from_tree(self, node: _HasTagAndGet[_T]) -> _T: ... + def from_tree(self, node: _HasGet[_T]) -> _T: ... def to_tree(self, tagname: str | None = None, value: Incomplete | None = None, namespace: str | None = None) -> Element: ... class NestedValue(Nested[_T], Convertible[_T, _N]): # type: ignore[misc] @@ -150,7 +150,7 @@ class NestedText(NestedValue[_T, _N]): # Anything else @overload def __set__(self: NestedText[_T, Literal[True]], instance: Serialisable | Strict, value: _T | int | Any | None) -> None: ... - def from_tree(self, node: _HasTagAndText) -> str: ... # type: ignore[override] + def from_tree(self, node: _HasText) -> str: ... # type: ignore[override] def to_tree(self, tagname: str | None = None, value: Incomplete | None = None, namespace: str | None = None) -> Element: ... class NestedFloat(NestedValue[float, _N], Float[_N]): # type: ignore[misc] @@ -179,7 +179,7 @@ class NestedBool(NestedValue[bool, _N], Bool[_N]): # type: ignore[misc] def __set__( # type:ignore[override] # Different restrictions self, instance: Serialisable | Strict, value: _HasTagAndGet[_ConvertibleToBool] | _ConvertibleToBool ) -> None: ... - def from_tree(self, node) -> bool: ... # type: ignore[override] # Actual overriden return type + def from_tree(self, node: _HasGet[bool]) -> bool: ... # type: ignore[override] # Actual overriden return type class NestedNoneSet(Nested[_T | None], NoneSet[_T]): # type: ignore[misc] def __init__(self, name: str | None = None, *, values: Iterable[_T | None]) -> None: ... diff --git a/stubs/openpyxl/openpyxl/descriptors/sequence.pyi b/stubs/openpyxl/openpyxl/descriptors/sequence.pyi index ff2176f257f9..ef3d3096fd15 100644 --- a/stubs/openpyxl/openpyxl/descriptors/sequence.pyi +++ b/stubs/openpyxl/openpyxl/descriptors/sequence.pyi @@ -1,12 +1,20 @@ from _typeshed import Incomplete, Unused -from collections.abc import Generator -from typing_extensions import Self +from collections.abc import Generator, Iterable +from typing import Any, Protocol +from typing_extensions import Self, TypeVar from openpyxl.descriptors import Strict -from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors.serialisable import Serialisable, _SerialisableTreeElement +from openpyxl.xml._functions_overloads import _HasGet from .base import Alias, Descriptor +_T = TypeVar("_T") + +class _SupportsFromTree(Protocol): + @classmethod + def from_tree(cls, node: _SerialisableTreeElement) -> Any: ... + class Sequence(Descriptor[Incomplete]): expected_type: type[Incomplete] seq_types: tuple[type, ...] @@ -23,12 +31,15 @@ class UniqueSequence(Sequence): class ValueSequence(Sequence): attribute: str def to_tree(self, tagname, obj, namespace: str | None = None) -> Generator[Incomplete, None, None]: ... - def from_tree(self, node): ... + def from_tree(self, node: _HasGet[_T]) -> _T: ... class NestedSequence(Sequence): count: bool + expected_type: type[_SupportsFromTree] def to_tree(self, tagname, obj, namespace: str | None = None): ... - def from_tree(self, node): ... + # returned list generic type should be same as the return type of expected_type.from_tree(node) + # Which can really be anything given the wildly different, and sometimes generic, from_tree return types + def from_tree(self, node: Iterable[_SerialisableTreeElement]) -> list[Any]: ... class MultiSequence(Sequence): def __set__(self, instance: Serialisable | Strict, seq) -> None: ... diff --git a/stubs/openpyxl/openpyxl/descriptors/serialisable.pyi b/stubs/openpyxl/openpyxl/descriptors/serialisable.pyi index 3afff355f1cf..7da292d8867e 100644 --- a/stubs/openpyxl/openpyxl/descriptors/serialisable.pyi +++ b/stubs/openpyxl/openpyxl/descriptors/serialisable.pyi @@ -1,16 +1,14 @@ from _typeshed import Incomplete, SupportsIter from typing import Any, ClassVar, NoReturn, Protocol -from typing_extensions import Final +from typing_extensions import Final, Self from openpyxl.descriptors import MetaSerialisable -from ..xml._functions_overloads import _HasAttrib, _HasTagAndGet, _HasText +from ..xml._functions_overloads import _HasAttrib, _HasGet, _HasText, _SupportsFindChartLines # For any override directly re-using Serialisable.from_tree class _ChildSerialisableTreeElement(_HasAttrib, _HasText, SupportsIter[Incomplete], Protocol): ... - -class _SerialisableTreeElement(_HasTagAndGet[Incomplete], _ChildSerialisableTreeElement, Protocol): - def find(self, __path: str) -> Incomplete | None: ... +class _SerialisableTreeElement(_HasGet[object], _SupportsFindChartLines, _ChildSerialisableTreeElement, Protocol): ... KEYWORDS: Final[frozenset[str]] seq_types: Final[tuple[type[list[Any]], type[tuple[Any, ...]]]] @@ -26,12 +24,13 @@ class Serialisable(metaclass=MetaSerialisable): @property def tagname(self) -> str | NoReturn: ... namespace: ClassVar[str | None] - # Note: To respect the Liskov substitution principle, the protocol for node includes all child class requirements + # Note: To respect the Liskov substitution principle, the protocol for node includes all child class requirements. + # Same with the return type to avoid override issues. # See comment in xml/functions.pyi as to why use a protocol instead of Element # Child classes should be more precise than _SerialisableTreeElement ! # Use _ChildSerialisableTreeElement instead for child classes that reuse Serialisable.from_tree directly. @classmethod - def from_tree(cls, node: _SerialisableTreeElement): ... + def from_tree(cls, node: _SerialisableTreeElement) -> Self | None: ... def to_tree(self, tagname: str | None = None, idx: Incomplete | None = None, namespace: str | None = None): ... def __iter__(self): ... def __eq__(self, other): ... diff --git a/stubs/openpyxl/openpyxl/packaging/custom.pyi b/stubs/openpyxl/openpyxl/packaging/custom.pyi index 9aadb179cd35..048329b9414f 100644 --- a/stubs/openpyxl/openpyxl/packaging/custom.pyi +++ b/stubs/openpyxl/openpyxl/packaging/custom.pyi @@ -16,6 +16,7 @@ from openpyxl.descriptors.base import ( _ConvertibleToInt, ) from openpyxl.descriptors.nested import NestedText +from openpyxl.descriptors.serialisable import _ChildSerialisableTreeElement _T = TypeVar("_T") @@ -57,7 +58,7 @@ class CustomPropertyList(Strict): props: Sequence def __init__(self) -> None: ... @classmethod - def from_tree(cls, tree) -> Self: ... + def from_tree(cls, tree: _ChildSerialisableTreeElement) -> Self: ... def append(self, prop) -> None: ... def to_tree(self): ... def __len__(self) -> int: ... diff --git a/stubs/openpyxl/openpyxl/styles/fills.pyi b/stubs/openpyxl/openpyxl/styles/fills.pyi index b65e4d567c78..f8bf1b486a9f 100644 --- a/stubs/openpyxl/openpyxl/styles/fills.pyi +++ b/stubs/openpyxl/openpyxl/styles/fills.pyi @@ -1,4 +1,5 @@ from _typeshed import Incomplete +from collections.abc import Iterable, Sequence as ABCSequence from typing import ClassVar from typing_extensions import Final, Literal, TypeAlias @@ -6,6 +7,8 @@ from openpyxl.descriptors import Sequence from openpyxl.descriptors.base import Alias, Float, MinMax, NoneSet, Set, _ConvertibleToFloat from openpyxl.descriptors.serialisable import Serialisable +from ..xml._functions_overloads import _SupportsIterAndAttribAndTextAndTag + FILL_NONE: Final = "none" FILL_SOLID: Final = "solid" FILL_PATTERN_DARKDOWN: Final = "darkDown" @@ -53,7 +56,7 @@ fills: tuple[_FillsType, ...] class Fill(Serialisable): tagname: ClassVar[str] @classmethod - def from_tree(cls, el): ... + def from_tree(cls, el: Iterable[ABCSequence[_SupportsIterAndAttribAndTextAndTag]]) -> PatternFill | GradientFill | None: ... class PatternFill(Fill): tagname: ClassVar[str] diff --git a/stubs/openpyxl/openpyxl/styles/fonts.pyi b/stubs/openpyxl/openpyxl/styles/fonts.pyi index 923d8a850320..d8ab9e06dc9e 100644 --- a/stubs/openpyxl/openpyxl/styles/fonts.pyi +++ b/stubs/openpyxl/openpyxl/styles/fonts.pyi @@ -1,6 +1,6 @@ from _typeshed import Incomplete from typing import ClassVar -from typing_extensions import Final, Literal, TypeAlias +from typing_extensions import Final, Literal, Self, TypeAlias from openpyxl.descriptors.base import Alias, _ConvertibleToBool, _ConvertibleToFloat, _ConvertibleToInt from openpyxl.descriptors.nested import ( @@ -14,7 +14,7 @@ from openpyxl.descriptors.nested import ( ) from openpyxl.descriptors.serialisable import Serialisable -from ..xml._functions_overloads import _HasTagAndGet +from ..xml._functions_overloads import _HasTagAndGet, _SupportsFindAndIterAndAttribAndText _FontU: TypeAlias = Literal["single", "double", "singleAccounting", "doubleAccounting"] _FontVertAlign: TypeAlias = Literal["superscript", "subscript", "baseline"] @@ -71,6 +71,6 @@ class Font(Serialisable): extend: _HasTagAndGet[_ConvertibleToBool | None] | _ConvertibleToBool | None = None, ) -> None: ... @classmethod - def from_tree(cls, node): ... + def from_tree(cls, node: _SupportsFindAndIterAndAttribAndText) -> Self: ... DEFAULT_FONT: Final[Font] diff --git a/stubs/openpyxl/openpyxl/styles/stylesheet.pyi b/stubs/openpyxl/openpyxl/styles/stylesheet.pyi index 1d7b2fe266b3..b3c1465ad6ad 100644 --- a/stubs/openpyxl/openpyxl/styles/stylesheet.pyi +++ b/stubs/openpyxl/openpyxl/styles/stylesheet.pyi @@ -1,10 +1,10 @@ from _typeshed import Incomplete, Unused from typing import ClassVar -from typing_extensions import Literal +from typing_extensions import Literal, Self from openpyxl.descriptors.base import Typed from openpyxl.descriptors.excel import ExtensionList -from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors.serialisable import Serialisable, _ChildSerialisableTreeElement from openpyxl.styles.cell_style import CellStyleList from openpyxl.styles.colors import ColorList from openpyxl.styles.named_styles import _NamedCellStyleList @@ -45,7 +45,7 @@ class Stylesheet(Serialisable): extLst: Unused = None, ) -> None: ... @classmethod - def from_tree(cls, node): ... + def from_tree(cls, node: _ChildSerialisableTreeElement) -> Self: ... @property def custom_formats(self): ... def to_tree(self, tagname: str | None = None, idx: Incomplete | None = None, namespace: str | None = None): ... diff --git a/stubs/openpyxl/openpyxl/workbook/protection.pyi b/stubs/openpyxl/openpyxl/workbook/protection.pyi index 1b4c732b18d3..236dfceb6bee 100644 --- a/stubs/openpyxl/openpyxl/workbook/protection.pyi +++ b/stubs/openpyxl/openpyxl/workbook/protection.pyi @@ -1,10 +1,12 @@ from _typeshed import Incomplete from typing import ClassVar -from typing_extensions import Literal +from typing_extensions import Literal, Self from openpyxl.descriptors.base import Alias, Bool, Integer, String, _ConvertibleToBool, _ConvertibleToInt from openpyxl.descriptors.serialisable import Serialisable +from ..xml._functions_overloads import _SupportsIterAndAttribAndTextAndGet + class WorkbookProtection(Serialisable): tagname: ClassVar[str] workbook_password: Alias @@ -55,7 +57,7 @@ class WorkbookProtection(Serialisable): @revisionsPassword.setter def revisionsPassword(self, value) -> None: ... @classmethod - def from_tree(cls, node): ... + def from_tree(cls, node: _SupportsIterAndAttribAndTextAndGet) -> Self: ... DocumentSecurity = WorkbookProtection diff --git a/stubs/openpyxl/openpyxl/worksheet/header_footer.pyi b/stubs/openpyxl/openpyxl/worksheet/header_footer.pyi index e8066e0231c4..714e6ef6c956 100644 --- a/stubs/openpyxl/openpyxl/worksheet/header_footer.pyi +++ b/stubs/openpyxl/openpyxl/worksheet/header_footer.pyi @@ -1,11 +1,13 @@ from re import Pattern from typing import ClassVar -from typing_extensions import Final, Literal +from typing_extensions import Final, Literal, Self from openpyxl.descriptors import Strict from openpyxl.descriptors.base import Alias, Bool, Integer, MatchPattern, String, Typed, _ConvertibleToBool, _ConvertibleToInt from openpyxl.descriptors.serialisable import Serialisable +from ..xml._functions_overloads import _HasText + FONT_PATTERN: Final = '&"(?P.+)"' COLOR_PATTERN: Final = "&K(?P[A-F0-9]{6})" SIZE_REGEX: Final = r"&(?P\d+\s?)" @@ -38,7 +40,7 @@ class HeaderFooterItem(Strict): def __bool__(self) -> bool: ... def to_tree(self, tagname): ... @classmethod - def from_tree(cls, node): ... + def from_tree(cls, node: _HasText) -> Self: ... class HeaderFooter(Serialisable): tagname: ClassVar[str] diff --git a/stubs/openpyxl/openpyxl/worksheet/page.pyi b/stubs/openpyxl/openpyxl/worksheet/page.pyi index 06a96e133408..2538cabf076b 100644 --- a/stubs/openpyxl/openpyxl/worksheet/page.pyi +++ b/stubs/openpyxl/openpyxl/worksheet/page.pyi @@ -1,9 +1,9 @@ from _typeshed import Incomplete from typing import ClassVar -from typing_extensions import Literal, TypeAlias +from typing_extensions import Literal, Self, TypeAlias from openpyxl.descriptors.base import Bool, Float, Integer, NoneSet, _ConvertibleToBool, _ConvertibleToFloat, _ConvertibleToInt -from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors.serialisable import Serialisable, _ChildSerialisableTreeElement _PrintPageSetupOrientation: TypeAlias = Literal["default", "portrait", "landscape"] _PrintPageSetupPageOrder: TypeAlias = Literal["downThenOver", "overThenDown"] @@ -66,7 +66,7 @@ class PrintPageSetup(Serialisable): @autoPageBreaks.setter def autoPageBreaks(self, value) -> None: ... @classmethod - def from_tree(cls, node): ... + def from_tree(cls, node: _ChildSerialisableTreeElement) -> Self: ... class PrintOptions(Serialisable): tagname: ClassVar[str] diff --git a/stubs/openpyxl/openpyxl/worksheet/table.pyi b/stubs/openpyxl/openpyxl/worksheet/table.pyi index 8b48b4a02215..03f0e0ffb361 100644 --- a/stubs/openpyxl/openpyxl/worksheet/table.pyi +++ b/stubs/openpyxl/openpyxl/worksheet/table.pyi @@ -1,11 +1,11 @@ from _typeshed import Incomplete, Unused from typing import ClassVar, overload -from typing_extensions import Final, Literal, TypeAlias +from typing_extensions import Final, Literal, Self, TypeAlias from openpyxl.descriptors import Strict, String from openpyxl.descriptors.base import Alias, Bool, Integer, NoneSet, Typed, _ConvertibleToBool, _ConvertibleToInt from openpyxl.descriptors.excel import ExtensionList -from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors.serialisable import Serialisable, _ChildSerialisableTreeElement from openpyxl.worksheet.filters import AutoFilter, SortState _TableColumnTotalsRowFunction: TypeAlias = Literal[ @@ -129,7 +129,7 @@ class TableColumn(Serialisable): ) -> None: ... def __iter__(self): ... @classmethod - def from_tree(cls, node): ... + def from_tree(cls, node: _ChildSerialisableTreeElement) -> Self: ... class TableNameDescriptor(String[Incomplete]): def __set__(self, instance: Serialisable | Strict, value) -> None: ... diff --git a/stubs/openpyxl/openpyxl/xml/_functions_overloads.pyi b/stubs/openpyxl/openpyxl/xml/_functions_overloads.pyi index 06437d35f977..af057881a726 100644 --- a/stubs/openpyxl/openpyxl/xml/_functions_overloads.pyi +++ b/stubs/openpyxl/openpyxl/xml/_functions_overloads.pyi @@ -1,12 +1,14 @@ # This file does not exist at runtime. It is a helper file to overload imported functions in openpyxl.xml.functions import sys -from _typeshed import Incomplete, ReadableBuffer +from _typeshed import Incomplete, ReadableBuffer, SupportsIter from collections.abc import Iterable, Iterator, Mapping, Sequence from typing import Any, Protocol, TypeVar, overload from typing_extensions import TypeAlias from xml.etree.ElementTree import Element, ElementTree, QName, XMLParser, _FileRead +from openpyxl.chart.axis import ChartLines + _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) @@ -17,18 +19,30 @@ _T_co = TypeVar("_T_co", covariant=True) class _HasTag(Protocol): tag: Any # AnyOf[str, None, Callable[..., AnyOf[str, None]]] +class _HasGet(Protocol[_T_co]): + def get(self, __value: str) -> _T_co | None: ... + class _HasText(Protocol): text: str class _HasAttrib(Protocol): attrib: Iterable[Any] # AnyOf[dict[str, str], Iterable[tuple[str, str]]] -class _HasTagAndGet(_HasTag, Protocol[_T_co]): - def get(self, __value: str) -> _T_co | None: ... - +class _HasTagAndGet(_HasTag, _HasGet[_T_co], Protocol[_T_co]): ... class _HasTagAndText(_HasTag, _HasText, Protocol): ... # noqa: Y046 class _HasTagAndTextAndAttrib(_HasTag, _HasText, _HasAttrib, Protocol): ... # noqa: Y046 +class _SupportsFindChartLines(Protocol): + def find(self, __path: str) -> ChartLines | None: ... + +class _SupportsFindAndIterAndAttribAndText( # noqa: Y046 + _SupportsFindChartLines, SupportsIter[Incomplete], _HasAttrib, _HasText, Protocol +): ... +class _SupportsIterAndAttribAndTextAndTag(SupportsIter[Incomplete], _HasAttrib, _HasText, _HasTag, Protocol): ... # noqa: Y046 +class _SupportsIterAndAttribAndTextAndGet( # noqa: Y046 + SupportsIter[Incomplete], _HasAttrib, _HasText, _HasGet[Incomplete], Protocol +): ... + class _ParentElement(Protocol[_T]): def makeelement(self, __tag: str, __attrib: dict[str, str]) -> _T: ... def append(self, __element: _T) -> object: ...