Skip to content

Commit 2ddcd57

Browse files
committed
Remove the _CompositionType from formula_engine.py
And replace it with `Self` and a generic `FormulaEngineT` that would be tightly coupled with the type of the HigherOrderFormulaBuilder. With this, there are no type-ambiguities anymore in `formula_engine.py`. Signed-off-by: Sahas Subramanian <[email protected]>
1 parent 3d6cd6e commit 2ddcd57

File tree

1 file changed

+32
-26
lines changed

1 file changed

+32
-26
lines changed

src/frequenz/sdk/timeseries/formula_engine/_formula_engine.py

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from abc import ABC
1313
from collections import deque
1414
from collections.abc import Callable
15-
from typing import Generic, Self, Union
15+
from typing import Any, Generic, Self, TypeVar
1616

1717
from frequenz.channels import Broadcast, Receiver
1818

@@ -55,20 +55,6 @@
5555
"""The dictionary of operator precedence for the shunting yard algorithm."""
5656

5757

58-
# The `FormulaEngine*` and `HigherOrderFormulaBuilder*` classes are generic, but
59-
# `TypeVar`s can't be defined on generic types, so we need to use `# type: ignore` to
60-
# avoid mypy errors, and they get treated as `FormulaEngine[Any]`, etc.
61-
#
62-
# This is not ideal, but it's the best we can do until mypy supports generic types with
63-
# `TypeVar`s.
64-
_CompositionType = Union[
65-
"FormulaEngine", # type: ignore[type-arg]
66-
"HigherOrderFormulaBuilder", # type: ignore[type-arg]
67-
"FormulaEngine3Phase", # type: ignore[type-arg]
68-
"HigherOrderFormulaBuilder3Phase", # type: ignore[type-arg]
69-
]
70-
71-
7258
class FormulaEngine(Generic[QuantityT]):
7359
"""[`FormulaEngine`][frequenz.sdk.timeseries.formula_engine.FormulaEngine]s are a
7460
part of the SDK's data pipeline, and provide a way for the SDK to apply formulas on
@@ -868,12 +854,17 @@ def build(self) -> FormulaEngine[QuantityT]:
868854
return FormulaEngine(self, create_method=self._create_method)
869855

870856

871-
class _BaseHOFormulaBuilder(ABC, Generic[QuantityT]):
857+
FormulaEngineT = TypeVar(
858+
"FormulaEngineT", bound=(FormulaEngine[Any] | FormulaEngine3Phase[Any])
859+
)
860+
861+
862+
class _BaseHOFormulaBuilder(ABC, Generic[FormulaEngineT, QuantityT]):
872863
"""Provides a way to build formulas from the outputs of other formulas."""
873864

874865
def __init__(
875866
self,
876-
engine: FormulaEngine[QuantityT] | FormulaEngine3Phase[QuantityT],
867+
engine: FormulaEngineT,
877868
create_method: Callable[[float], QuantityT],
878869
) -> None:
879870
"""Create a `GenericHigherOrderFormulaBuilder` instance.
@@ -898,7 +889,11 @@ def __init__(
898889
self._steps.append((TokenType.COMPONENT_METRIC, engine))
899890
self._create_method: Callable[[float], QuantityT] = create_method
900891

901-
def _push(self, oper: str, other: _CompositionType | QuantityT | float) -> Self:
892+
def _push(
893+
self,
894+
oper: str,
895+
other: Self | FormulaEngineT | QuantityT | float,
896+
) -> Self:
902897
self._steps.appendleft((TokenType.OPER, "("))
903898
self._steps.append((TokenType.OPER, ")"))
904899
self._steps.append((TokenType.OPER, oper))
@@ -927,7 +922,10 @@ def _push(self, oper: str, other: _CompositionType | QuantityT | float) -> Self:
927922
raise RuntimeError(f"Can't build a formula from: {other}")
928923
return self
929924

930-
def __add__(self, other: _CompositionType | QuantityT) -> Self:
925+
def __add__(
926+
self,
927+
other: Self | FormulaEngineT | QuantityT,
928+
) -> Self:
931929
"""Return a formula builder that adds (data in) `other` to `self`.
932930
933931
Args:
@@ -942,7 +940,7 @@ def __add__(self, other: _CompositionType | QuantityT) -> Self:
942940

943941
def __sub__(
944942
self,
945-
other: _CompositionType | QuantityT,
943+
other: Self | FormulaEngineT | QuantityT,
946944
) -> Self:
947945
"""Return a formula builder that subtracts (data in) `other` from `self`.
948946
@@ -958,7 +956,7 @@ def __sub__(
958956

959957
def __mul__(
960958
self,
961-
other: _CompositionType | float,
959+
other: Self | FormulaEngineT | float,
962960
) -> Self:
963961
"""Return a formula builder that multiplies (data in) `self` with `other`.
964962
@@ -974,7 +972,7 @@ def __mul__(
974972

975973
def __truediv__(
976974
self,
977-
other: _CompositionType | float,
975+
other: Self | FormulaEngineT | float,
978976
) -> Self:
979977
"""Return a formula builder that divides (data in) `self` by `other`.
980978
@@ -988,7 +986,10 @@ def __truediv__(
988986
"""
989987
return self._push("/", other)
990988

991-
def max(self, other: _CompositionType | QuantityT) -> Self:
989+
def max(
990+
self,
991+
other: Self | FormulaEngineT | QuantityT,
992+
) -> Self:
992993
"""Return a formula builder that calculates the maximum of `self` and `other`.
993994
994995
Args:
@@ -1001,7 +1002,10 @@ def max(self, other: _CompositionType | QuantityT) -> Self:
10011002
"""
10021003
return self._push("max", other)
10031004

1004-
def min(self, other: _CompositionType | QuantityT) -> Self:
1005+
def min(
1006+
self,
1007+
other: Self | FormulaEngineT | QuantityT,
1008+
) -> Self:
10051009
"""Return a formula builder that calculates the minimum of `self` and `other`.
10061010
10071011
Args:
@@ -1049,7 +1053,9 @@ def production(
10491053
return self
10501054

10511055

1052-
class HigherOrderFormulaBuilder(Generic[QuantityT], _BaseHOFormulaBuilder[QuantityT]):
1056+
class HigherOrderFormulaBuilder(
1057+
Generic[QuantityT], _BaseHOFormulaBuilder[FormulaEngine[QuantityT], QuantityT]
1058+
):
10531059
"""A specialization of the _BaseHOFormulaBuilder for `FormulaReceiver`."""
10541060

10551061
def build(
@@ -1086,7 +1092,7 @@ def build(
10861092

10871093

10881094
class HigherOrderFormulaBuilder3Phase(
1089-
Generic[QuantityT], _BaseHOFormulaBuilder[QuantityT]
1095+
Generic[QuantityT], _BaseHOFormulaBuilder[FormulaEngine3Phase[QuantityT], QuantityT]
10901096
):
10911097
"""A specialization of the _BaseHOFormulaBuilder for `FormulaReceiver3Phase`."""
10921098

0 commit comments

Comments
 (0)