Skip to content

Commit

Permalink
openpyxl: Typed from_tree methods (#10602)
Browse files Browse the repository at this point in the history
  • Loading branch information
Avasam committed Sep 11, 2023
1 parent ae9d225 commit 32535ee
Show file tree
Hide file tree
Showing 14 changed files with 76 additions and 44 deletions.
6 changes: 3 additions & 3 deletions stubs/openpyxl/openpyxl/chart/axis.pyi
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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"]
Expand Down Expand Up @@ -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]
Expand Down
6 changes: 3 additions & 3 deletions stubs/openpyxl/openpyxl/chart/plotarea.pyi
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
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
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

Expand Down Expand Up @@ -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: ...
8 changes: 4 additions & 4 deletions stubs/openpyxl/openpyxl/descriptors/nested.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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]
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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: ...
Expand Down
21 changes: 16 additions & 5 deletions stubs/openpyxl/openpyxl/descriptors/sequence.pyi
Original file line number Diff line number Diff line change
@@ -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, ...]
Expand All @@ -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: ...
Expand Down
13 changes: 6 additions & 7 deletions stubs/openpyxl/openpyxl/descriptors/serialisable.pyi
Original file line number Diff line number Diff line change
@@ -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, ...]]]]
Expand All @@ -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): ...
Expand Down
3 changes: 2 additions & 1 deletion stubs/openpyxl/openpyxl/packaging/custom.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down Expand Up @@ -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: ...
Expand Down
5 changes: 4 additions & 1 deletion stubs/openpyxl/openpyxl/styles/fills.pyi
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from _typeshed import Incomplete
from collections.abc import Iterable, Sequence as ABCSequence
from typing import ClassVar
from typing_extensions import Final, Literal, TypeAlias

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"
Expand Down Expand Up @@ -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]
Expand Down
6 changes: 3 additions & 3 deletions stubs/openpyxl/openpyxl/styles/fonts.pyi
Original file line number Diff line number Diff line change
@@ -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 (
Expand All @@ -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"]
Expand Down Expand Up @@ -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]
6 changes: 3 additions & 3 deletions stubs/openpyxl/openpyxl/styles/stylesheet.pyi
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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): ...
Expand Down
6 changes: 4 additions & 2 deletions stubs/openpyxl/openpyxl/workbook/protection.pyi
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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

Expand Down
6 changes: 4 additions & 2 deletions stubs/openpyxl/openpyxl/worksheet/header_footer.pyi
Original file line number Diff line number Diff line change
@@ -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<font>.+)"'
COLOR_PATTERN: Final = "&K(?P<color>[A-F0-9]{6})"
SIZE_REGEX: Final = r"&(?P<size>\d+\s?)"
Expand Down Expand Up @@ -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]
Expand Down
6 changes: 3 additions & 3 deletions stubs/openpyxl/openpyxl/worksheet/page.pyi
Original file line number Diff line number Diff line change
@@ -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"]
Expand Down Expand Up @@ -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]
Expand Down
6 changes: 3 additions & 3 deletions stubs/openpyxl/openpyxl/worksheet/table.pyi
Original file line number Diff line number Diff line change
@@ -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[
Expand Down Expand Up @@ -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: ...
Expand Down
22 changes: 18 additions & 4 deletions stubs/openpyxl/openpyxl/xml/_functions_overloads.pyi
Original file line number Diff line number Diff line change
@@ -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)

Expand All @@ -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: ...
Expand Down

0 comments on commit 32535ee

Please sign in to comment.