1212from abc import ABC
1313from collections import deque
1414from collections .abc import Callable
15- from typing import Generic , Self , Union
15+ from typing import Any , Generic , Self , TypeVar
1616
1717from frequenz .channels import Broadcast , Receiver
1818
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-
7258class 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
10881094class 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